sync update of android-service to libsignal

pull/420/head
Ryan ZHAO 3 years ago
parent ef2380da76
commit 1f1ffdafdd

@ -6,6 +6,7 @@ import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.service.internal.util.Base64 import org.session.libsignal.service.internal.util.Base64
import org.session.libsignal.service.loki.api.crypto.ProofOfWork import org.session.libsignal.service.loki.api.crypto.ProofOfWork
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities import org.session.libsignal.service.loki.protocol.meta.TTLUtilities
import org.session.libsignal.service.loki.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.prettifiedDescription import org.session.libsignal.service.loki.utilities.prettifiedDescription
internal data class LokiMessage( internal data class LokiMessage(
@ -60,7 +61,7 @@ internal data class LokiMessage(
internal fun calculatePoW(): Promise<LokiMessage, Exception> { internal fun calculatePoW(): Promise<LokiMessage, Exception> {
val deferred = deferred<LokiMessage, Exception>() val deferred = deferred<LokiMessage, Exception>()
// Run PoW in a background thread // Run PoW in a background thread
Thread { ThreadUtils.queue {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val nonce = ProofOfWork.calculate(data, recipientPublicKey, now, ttl) val nonce = ProofOfWork.calculate(data, recipientPublicKey, now, ttl)
if (nonce != null ) { if (nonce != null ) {
@ -68,7 +69,7 @@ internal data class LokiMessage(
} else { } else {
deferred.reject(SnodeAPI.Error.ProofOfWorkCalculationFailed) deferred.reject(SnodeAPI.Error.ProofOfWorkCalculationFailed)
} }
}.start() }
return deferred.promise return deferred.promise
} }

@ -12,22 +12,19 @@ import org.session.libsignal.service.internal.util.Base64
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
import org.session.libsignal.service.loki.api.utilities.HTTP import org.session.libsignal.service.loki.api.utilities.HTTP
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.utilities.Broadcaster import org.session.libsignal.service.loki.utilities.*
import org.session.libsignal.service.loki.utilities.createContext
import org.session.libsignal.service.loki.utilities.prettifiedDescription
import org.session.libsignal.service.loki.utilities.retryIfNeeded
import java.net.ConnectException import java.net.ConnectException
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
class SnodeAPI private constructor(public var userPublicKey: String, public val database: LokiAPIDatabaseProtocol, public val broadcaster: Broadcaster) { class SnodeAPI private constructor(public var userPublicKey: String, public val database: LokiAPIDatabaseProtocol, public val broadcaster: Broadcaster) {
companion object { companion object {
val messageSendingContext = Kovenant.createContext("LokiAPIMessageSendingContext") val messageSendingContext = Kovenant.createContext()
val messagePollingContext = Kovenant.createContext("LokiAPIMessagePollingContext") val messagePollingContext = Kovenant.createContext()
/** /**
* For operations that are shared between message sending and message polling. * For operations that are shared between message sending and message polling.
*/ */
val sharedContext = Kovenant.createContext("LokiAPISharedContext") val sharedContext = Kovenant.createContext()
// region Initialization // region Initialization
lateinit var shared: SnodeAPI lateinit var shared: SnodeAPI
@ -75,7 +72,7 @@ class SnodeAPI private constructor(public var userPublicKey: String, public val
return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey) return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey)
} else { } else {
val deferred = deferred<Map<*, *>, Exception>() val deferred = deferred<Map<*, *>, Exception>()
Thread { ThreadUtils.queue {
val payload = mapOf( "method" to method.rawValue, "params" to parameters ) val payload = mapOf( "method" to method.rawValue, "params" to parameters )
try { try {
val json = HTTP.execute(HTTP.Verb.POST, url, payload) val json = HTTP.execute(HTTP.Verb.POST, url, payload)
@ -87,13 +84,13 @@ class SnodeAPI private constructor(public var userPublicKey: String, public val
val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException
if (httpRequestFailedException != null) { if (httpRequestFailedException != null) {
@Suppress("NAME_SHADOWING") val exception = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey) @Suppress("NAME_SHADOWING") val exception = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey)
return@Thread deferred.reject(exception) return@queue deferred.reject(exception)
} }
Log.d("Loki", "Unhandled exception: $exception.") Log.d("Loki", "Unhandled exception: $exception.")
} }
deferred.reject(exception) deferred.reject(exception)
} }
}.start() }
return deferred.promise return deferred.promise
} }
} }

@ -8,6 +8,7 @@ import nl.komponents.kovenant.task
import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.service.loki.api.utilities.HTTP import org.session.libsignal.service.loki.api.utilities.HTTP
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.getRandomElement import org.session.libsignal.service.loki.utilities.getRandomElement
import org.session.libsignal.service.loki.utilities.prettifiedDescription import org.session.libsignal.service.loki.utilities.prettifiedDescription
import org.session.libsignal.service.loki.utilities.retryIfNeeded import org.session.libsignal.service.loki.utilities.retryIfNeeded
@ -67,7 +68,7 @@ class SwarmAPI private constructor(private val database: LokiAPIDatabaseProtocol
) )
val deferred = deferred<Snode, Exception>() val deferred = deferred<Snode, Exception>()
deferred<Snode, Exception>(SnodeAPI.sharedContext) deferred<Snode, Exception>(SnodeAPI.sharedContext)
Thread { ThreadUtils.queue {
try { try {
val json = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true) val json = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true)
val intermediate = json["result"] as? Map<*, *> val intermediate = json["result"] as? Map<*, *>
@ -101,7 +102,7 @@ class SwarmAPI private constructor(private val database: LokiAPIDatabaseProtocol
} catch (exception: Exception) { } catch (exception: Exception) {
deferred.reject(exception) deferred.reject(exception)
} }
}.start() }
return deferred.promise return deferred.promise
} else { } else {
return Promise.of(snodePool.getRandomElement()) return Promise.of(snodePool.getRandomElement())

@ -82,12 +82,12 @@ public object OnionRequestAPI {
*/ */
private fun testSnode(snode: Snode): Promise<Unit, Exception> { private fun testSnode(snode: Snode): Promise<Unit, Exception> {
val deferred = deferred<Unit, Exception>() val deferred = deferred<Unit, Exception>()
Thread { // No need to block the shared context for this ThreadUtils.queue { // No need to block the shared context for this
val url = "${snode.address}:${snode.port}/get_stats/v1" val url = "${snode.address}:${snode.port}/get_stats/v1"
try { try {
val json = HTTP.execute(HTTP.Verb.GET, url) val json = HTTP.execute(HTTP.Verb.GET, url)
val version = json["version"] as? String val version = json["version"] as? String
if (version == null) { deferred.reject(Exception("Missing snode version.")); return@Thread } if (version == null) { deferred.reject(Exception("Missing snode version.")); return@queue }
if (version >= "2.0.7") { if (version >= "2.0.7") {
deferred.resolve(Unit) deferred.resolve(Unit)
} else { } else {
@ -98,7 +98,7 @@ public object OnionRequestAPI {
} catch (exception: Exception) { } catch (exception: Exception) {
deferred.reject(exception) deferred.reject(exception)
} }
}.start() }
return deferred.promise return deferred.promise
} }
@ -312,10 +312,10 @@ public object OnionRequestAPI {
return@success deferred.reject(exception) return@success deferred.reject(exception)
} }
val destinationSymmetricKey = result.destinationSymmetricKey val destinationSymmetricKey = result.destinationSymmetricKey
Thread { ThreadUtils.queue {
try { try {
val json = HTTP.execute(HTTP.Verb.POST, url, body) val json = HTTP.execute(HTTP.Verb.POST, url, body)
val base64EncodedIVAndCiphertext = json["result"] as? String ?: return@Thread deferred.reject(Exception("Invalid JSON")) val base64EncodedIVAndCiphertext = json["result"] as? String ?: return@queue deferred.reject(Exception("Invalid JSON"))
val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext) val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext)
try { try {
val plaintext = DecryptionUtilities.decryptUsingAESGCM(ivAndCiphertext, destinationSymmetricKey) val plaintext = DecryptionUtilities.decryptUsingAESGCM(ivAndCiphertext, destinationSymmetricKey)
@ -325,7 +325,7 @@ public object OnionRequestAPI {
if (statusCode == 406) { if (statusCode == 406) {
@Suppress("NAME_SHADOWING") val body = mapOf( "result" to "Your clock is out of sync with the service node network." ) @Suppress("NAME_SHADOWING") val body = mapOf( "result" to "Your clock is out of sync with the service node network." )
val exception = HTTPRequestFailedAtDestinationException(statusCode, body) val exception = HTTPRequestFailedAtDestinationException(statusCode, body)
return@Thread deferred.reject(exception) return@queue deferred.reject(exception)
} else if (json["body"] != null) { } else if (json["body"] != null) {
@Suppress("NAME_SHADOWING") val body: Map<*, *> @Suppress("NAME_SHADOWING") val body: Map<*, *>
if (json["body"] is Map<*, *>) { if (json["body"] is Map<*, *>) {
@ -340,13 +340,13 @@ public object OnionRequestAPI {
} }
if (statusCode != 200) { if (statusCode != 200) {
val exception = HTTPRequestFailedAtDestinationException(statusCode, body) val exception = HTTPRequestFailedAtDestinationException(statusCode, body)
return@Thread deferred.reject(exception) return@queue deferred.reject(exception)
} }
deferred.resolve(body) deferred.resolve(body)
} else { } else {
if (statusCode != 200) { if (statusCode != 200) {
val exception = HTTPRequestFailedAtDestinationException(statusCode, json) val exception = HTTPRequestFailedAtDestinationException(statusCode, json)
return@Thread deferred.reject(exception) return@queue deferred.reject(exception)
} }
deferred.resolve(json) deferred.resolve(json)
} }
@ -359,7 +359,7 @@ public object OnionRequestAPI {
} catch (exception: Exception) { } catch (exception: Exception) {
deferred.reject(exception) deferred.reject(exception)
} }
}.start() }
}.fail { exception -> }.fail { exception ->
deferred.reject(exception) deferred.reject(exception)
} }

@ -5,6 +5,7 @@ import nl.komponents.kovenant.deferred
import org.session.libsignal.service.internal.util.JsonUtil import org.session.libsignal.service.internal.util.JsonUtil
import org.session.libsignal.service.loki.api.utilities.EncryptionResult import org.session.libsignal.service.loki.api.utilities.EncryptionResult
import org.session.libsignal.service.loki.api.utilities.EncryptionUtilities import org.session.libsignal.service.loki.api.utilities.EncryptionUtilities
import org.session.libsignal.service.loki.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.toHexString import org.session.libsignal.service.loki.utilities.toHexString
import java.nio.Buffer import java.nio.Buffer
import java.nio.ByteBuffer import java.nio.ByteBuffer
@ -32,7 +33,7 @@ object OnionRequestEncryption {
*/ */
internal fun encryptPayloadForDestination(payload: Map<*, *>, destination: OnionRequestAPI.Destination): Promise<EncryptionResult, Exception> { internal fun encryptPayloadForDestination(payload: Map<*, *>, destination: OnionRequestAPI.Destination): Promise<EncryptionResult, Exception> {
val deferred = deferred<EncryptionResult, Exception>() val deferred = deferred<EncryptionResult, Exception>()
Thread { ThreadUtils.queue {
try { try {
// Wrapping isn't needed for file server or open group onion requests // Wrapping isn't needed for file server or open group onion requests
when (destination) { when (destination) {
@ -52,7 +53,7 @@ object OnionRequestEncryption {
} catch (exception: Exception) { } catch (exception: Exception) {
deferred.reject(exception) deferred.reject(exception)
} }
}.start() }
return deferred.promise return deferred.promise
} }
@ -61,7 +62,7 @@ object OnionRequestEncryption {
*/ */
internal fun encryptHop(lhs: OnionRequestAPI.Destination, rhs: OnionRequestAPI.Destination, previousEncryptionResult: EncryptionResult): Promise<EncryptionResult, Exception> { internal fun encryptHop(lhs: OnionRequestAPI.Destination, rhs: OnionRequestAPI.Destination, previousEncryptionResult: EncryptionResult): Promise<EncryptionResult, Exception> {
val deferred = deferred<EncryptionResult, Exception>() val deferred = deferred<EncryptionResult, Exception>()
Thread { ThreadUtils.queue {
try { try {
val payload: MutableMap<String, Any> val payload: MutableMap<String, Any>
when (rhs) { when (rhs) {
@ -88,7 +89,7 @@ object OnionRequestEncryption {
} catch (exception: Exception) { } catch (exception: Exception) {
deferred.reject(exception) deferred.reject(exception)
} }
}.start() }
return deferred.promise return deferred.promise
} }
} }

@ -16,6 +16,7 @@ import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol
import org.session.libsignal.service.loki.utilities.DownloadUtilities import org.session.libsignal.service.loki.utilities.DownloadUtilities
import org.session.libsignal.service.loki.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.createContext import org.session.libsignal.service.loki.utilities.createContext
import org.session.libsignal.service.loki.utilities.retryIfNeeded import org.session.libsignal.service.loki.utilities.retryIfNeeded
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@ -27,7 +28,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
companion object { companion object {
private val moderators: HashMap<String, HashMap<Long, Set<String>>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs) private val moderators: HashMap<String, HashMap<Long, Set<String>>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
val sharedContext = Kovenant.createContext("LokiPublicChatAPISharedContext") val sharedContext = Kovenant.createContext()
// region Settings // region Settings
private val fallbackBatchCount = 64 private val fallbackBatchCount = 64
@ -38,15 +39,15 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
private val channelInfoType = "net.patter-app.settings" private val channelInfoType = "net.patter-app.settings"
private val attachmentType = "net.app.core.oembed" private val attachmentType = "net.app.core.oembed"
@JvmStatic @JvmStatic
public val publicChatMessageType = "network.loki.messenger.publicChat" val publicChatMessageType = "network.loki.messenger.publicChat"
@JvmStatic @JvmStatic
public val profilePictureType = "network.loki.messenger.avatar" val profilePictureType = "network.loki.messenger.avatar"
fun getDefaultChats(): List<PublicChat> { fun getDefaultChats(): List<PublicChat> {
return listOf() // Don't auto-join any open groups right now return listOf() // Don't auto-join any open groups right now
} }
public fun isUserModerator(hexEncodedPublicKey: String, channel: Long, server: String): Boolean { fun isUserModerator(hexEncodedPublicKey: String, channel: Long, server: String): Boolean {
if (moderators[server] != null && moderators[server]!![channel] != null) { if (moderators[server] != null && moderators[server]!![channel] != null) {
return moderators[server]!![channel]!!.contains(hexEncodedPublicKey) return moderators[server]!![channel]!!.contains(hexEncodedPublicKey)
} }
@ -56,7 +57,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
// region Public API // region Public API
public fun getMessages(channel: Long, server: String): Promise<List<PublicChatMessage>, Exception> { fun getMessages(channel: Long, server: String): Promise<List<PublicChatMessage>, Exception> {
Log.d("Loki", "Getting messages for open group with ID: $channel on server: $server.") Log.d("Loki", "Getting messages for open group with ID: $channel on server: $server.")
val parameters = mutableMapOf<String, Any>( "include_annotations" to 1 ) val parameters = mutableMapOf<String, Any>( "include_annotations" to 1 )
val lastMessageServerID = apiDatabase.getLastMessageServerID(channel, server) val lastMessageServerID = apiDatabase.getLastMessageServerID(channel, server)
@ -160,7 +161,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun getDeletedMessageServerIDs(channel: Long, server: String): Promise<List<Long>, Exception> { fun getDeletedMessageServerIDs(channel: Long, server: String): Promise<List<Long>, Exception> {
Log.d("Loki", "Getting deleted messages for open group with ID: $channel on server: $server.") Log.d("Loki", "Getting deleted messages for open group with ID: $channel on server: $server.")
val parameters = mutableMapOf<String, Any>() val parameters = mutableMapOf<String, Any>()
val lastDeletionServerID = apiDatabase.getLastDeletionServerID(channel, server) val lastDeletionServerID = apiDatabase.getLastDeletionServerID(channel, server)
@ -191,9 +192,9 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun sendMessage(message: PublicChatMessage, channel: Long, server: String): Promise<PublicChatMessage, Exception> { fun sendMessage(message: PublicChatMessage, channel: Long, server: String): Promise<PublicChatMessage, Exception> {
val deferred = deferred<PublicChatMessage, Exception>() val deferred = deferred<PublicChatMessage, Exception>()
Thread { ThreadUtils.queue {
val signedMessage = message.sign(userPrivateKey) val signedMessage = message.sign(userPrivateKey)
if (signedMessage == null) { if (signedMessage == null) {
deferred.reject(SnodeAPI.Error.MessageSigningFailed) deferred.reject(SnodeAPI.Error.MessageSigningFailed)
@ -224,11 +225,11 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
deferred.reject(it) deferred.reject(it)
} }
} }
}.start() }
return deferred.promise return deferred.promise
} }
public fun deleteMessage(messageServerID: Long, channel: Long, server: String, isSentByUser: Boolean): Promise<Long, Exception> { fun deleteMessage(messageServerID: Long, channel: Long, server: String, isSentByUser: Boolean): Promise<Long, Exception> {
return retryIfNeeded(maxRetryCount) { return retryIfNeeded(maxRetryCount) {
val isModerationRequest = !isSentByUser val isModerationRequest = !isSentByUser
Log.d("Loki", "Deleting message with ID: $messageServerID from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).") Log.d("Loki", "Deleting message with ID: $messageServerID from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).")
@ -240,7 +241,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun deleteMessages(messageServerIDs: List<Long>, channel: Long, server: String, isSentByUser: Boolean): Promise<List<Long>, Exception> { fun deleteMessages(messageServerIDs: List<Long>, channel: Long, server: String, isSentByUser: Boolean): Promise<List<Long>, Exception> {
return retryIfNeeded(maxRetryCount) { return retryIfNeeded(maxRetryCount) {
val isModerationRequest = !isSentByUser val isModerationRequest = !isSentByUser
val parameters = mapOf( "ids" to messageServerIDs.joinToString(",") ) val parameters = mapOf( "ids" to messageServerIDs.joinToString(",") )
@ -253,7 +254,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun getModerators(channel: Long, server: String): Promise<Set<String>, Exception> { fun getModerators(channel: Long, server: String): Promise<Set<String>, Exception> {
return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/get_moderators").then(sharedContext) { json -> return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/get_moderators").then(sharedContext) { json ->
try { try {
@Suppress("UNCHECKED_CAST") val moderators = json["moderators"] as? List<String> @Suppress("UNCHECKED_CAST") val moderators = json["moderators"] as? List<String>
@ -271,7 +272,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun getChannelInfo(channel: Long, server: String): Promise<PublicChatInfo, Exception> { fun getChannelInfo(channel: Long, server: String): Promise<PublicChatInfo, Exception> {
return retryIfNeeded(maxRetryCount) { return retryIfNeeded(maxRetryCount) {
val parameters = mapOf( "include_annotations" to 1 ) val parameters = mapOf( "include_annotations" to 1 )
execute(HTTPVerb.GET, server, "/channels/$channel", parameters = parameters).then(sharedContext) { json -> execute(HTTPVerb.GET, server, "/channels/$channel", parameters = parameters).then(sharedContext) { json ->
@ -295,7 +296,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: PublicChatInfo, isForcedUpdate: Boolean) { fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: PublicChatInfo, isForcedUpdate: Boolean) {
apiDatabase.setUserCount(channel, server, info.memberCount) apiDatabase.setUserCount(channel, server, info.memberCount)
openGroupDatabase.updateTitle(groupID, info.displayName) openGroupDatabase.updateTitle(groupID, info.displayName)
// Download and update profile picture if needed // Download and update profile picture if needed
@ -307,7 +308,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun downloadOpenGroupProfilePicture(server: String, endpoint: String): ByteArray? { fun downloadOpenGroupProfilePicture(server: String, endpoint: String): ByteArray? {
val url = "${server.removeSuffix("/")}/${endpoint.removePrefix("/")}" val url = "${server.removeSuffix("/")}/${endpoint.removePrefix("/")}"
Log.d("Loki", "Downloading open group profile picture from \"$url\".") Log.d("Loki", "Downloading open group profile picture from \"$url\".")
val outputStream = ByteArrayOutputStream() val outputStream = ByteArrayOutputStream()
@ -323,7 +324,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun join(channel: Long, server: String): Promise<Unit, Exception> { fun join(channel: Long, server: String): Promise<Unit, Exception> {
return retryIfNeeded(maxRetryCount) { return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.POST, server, "/channels/$channel/subscribe").then { execute(HTTPVerb.POST, server, "/channels/$channel/subscribe").then {
Log.d("Loki", "Joined channel with ID: $channel on server: $server.") Log.d("Loki", "Joined channel with ID: $channel on server: $server.")
@ -331,7 +332,7 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun leave(channel: Long, server: String): Promise<Unit, Exception> { fun leave(channel: Long, server: String): Promise<Unit, Exception> {
return retryIfNeeded(maxRetryCount) { return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.DELETE, server, "/channels/$channel/subscribe").then { execute(HTTPVerb.DELETE, server, "/channels/$channel/subscribe").then {
Log.d("Loki", "Left channel with ID: $channel on server: $server.") Log.d("Loki", "Left channel with ID: $channel on server: $server.")
@ -339,7 +340,15 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun getDisplayNames(publicKeys: Set<String>, server: String): Promise<Map<String, String>, Exception> { fun ban(publicKey: String, server: String): Promise<Unit,Exception> {
return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.POST, server, "/loki/v1/moderation/blacklist/@$publicKey").then {
Log.d("Loki", "Banned user with ID: $publicKey from $server")
}
}
}
fun getDisplayNames(publicKeys: Set<String>, server: String): Promise<Map<String, String>, Exception> {
return getUserProfiles(publicKeys, server, false).map(sharedContext) { json -> return getUserProfiles(publicKeys, server, false).map(sharedContext) { json ->
val mapping = mutableMapOf<String, String>() val mapping = mutableMapOf<String, String>()
for (user in json) { for (user in json) {
@ -353,17 +362,17 @@ class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray
} }
} }
public fun setDisplayName(newDisplayName: String?, server: String): Promise<Unit, Exception> { fun setDisplayName(newDisplayName: String?, server: String): Promise<Unit, Exception> {
Log.d("Loki", "Updating display name on server: $server.") Log.d("Loki", "Updating display name on server: $server.")
val parameters = mapOf( "name" to (newDisplayName ?: "") ) val parameters = mapOf( "name" to (newDisplayName ?: "") )
return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters).map { Unit } return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters).map { Unit }
} }
public fun setProfilePicture(server: String, profileKey: ByteArray, url: String?): Promise<Unit, Exception> { fun setProfilePicture(server: String, profileKey: ByteArray, url: String?): Promise<Unit, Exception> {
return setProfilePicture(server, Base64.encodeBytes(profileKey), url) return setProfilePicture(server, Base64.encodeBytes(profileKey), url)
} }
public fun setProfilePicture(server: String, profileKey: String, url: String?): Promise<Unit, Exception> { fun setProfilePicture(server: String, profileKey: String, url: String?): Promise<Unit, Exception> {
Log.d("Loki", "Updating profile picture on server: $server.") Log.d("Loki", "Updating profile picture on server: $server.")
val value = when (url) { val value = when (url) {
null -> null null -> null

@ -2,25 +2,14 @@
package org.session.libsignal.service.loki.utilities package org.session.libsignal.service.loki.utilities
import nl.komponents.kovenant.* import nl.komponents.kovenant.*
import nl.komponents.kovenant.jvm.asDispatcher
import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.libsignal.logging.Log
import kotlin.math.max import java.util.concurrent.Executors
// Try to use all available threads minus one for the callback fun Kovenant.createContext(): Context {
private val recommendedThreadCount: Int
get() = Runtime.getRuntime().availableProcessors() - 1
fun Kovenant.createContext(contextName: String, threadCount: Int = max(recommendedThreadCount, 1)): Context {
return createContext { return createContext {
callbackContext.dispatcher = buildDispatcher { callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher()
name = "${contextName}CallbackDispatcher" workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher()
// Ref: http://kovenant.komponents.nl/api/core_usage/#execution-order
// Having 1 concurrent task ensures we have in-order callback handling
concurrentTasks = 1
}
workerContext.dispatcher = buildDispatcher {
name = "${contextName}WorkerDispatcher"
concurrentTasks = threadCount
}
multipleCompletion = { v1, v2 -> multipleCompletion = { v1, v2 ->
Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.") Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.")
} }

@ -0,0 +1,18 @@
package org.session.libsignal.service.loki.utilities
import java.util.concurrent.Executors
object ThreadUtils {
internal val executorPool = Executors.newCachedThreadPool()
@JvmStatic
fun queue(target: Runnable) {
executorPool.execute(target)
}
fun queue(target: ()->Unit) {
executorPool.execute(target)
}
}
Loading…
Cancel
Save