mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 01:18:26 +00:00
sync update to libsession & clean
This commit is contained in:
parent
1f1ffdafdd
commit
9eacdd7b3e
@ -12,7 +12,7 @@ import android.util.Pair;
|
|||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
|
|
||||||
import org.session.libsession.utilities.ThreadUtil;
|
import org.session.libsession.utilities.ThreadUtils;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsession.utilities.concurrent.ListenableFuture;
|
import org.session.libsession.utilities.concurrent.ListenableFuture;
|
||||||
import org.session.libsession.utilities.concurrent.SettableFuture;
|
import org.session.libsession.utilities.concurrent.SettableFuture;
|
||||||
@ -25,7 +25,7 @@ public class AudioRecorder {
|
|||||||
|
|
||||||
private static final String TAG = AudioRecorder.class.getSimpleName();
|
private static final String TAG = AudioRecorder.class.getSimpleName();
|
||||||
|
|
||||||
private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor();
|
private static final ExecutorService executor = ThreadUtils.newDynamicSingleThreadedExecutor();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.library'
|
id 'com.android.library'
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'kotlin-kapt'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -48,8 +47,6 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
||||||
// annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
|
|
||||||
// kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
|
||||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||||
implementation 'com.annimon:stream:1.1.8'
|
implementation 'com.annimon:stream:1.1.8'
|
||||||
implementation 'com.makeramen:roundedimageview:2.1.0'
|
implementation 'com.makeramen:roundedimageview:2.1.0'
|
||||||
|
@ -2,14 +2,14 @@ package org.session.libsession.messaging.opengroups
|
|||||||
|
|
||||||
import org.session.libsignal.service.internal.util.JsonUtil
|
import org.session.libsignal.service.internal.util.JsonUtil
|
||||||
|
|
||||||
public data class OpenGroup(
|
data class OpenGroup(
|
||||||
public val channel: Long,
|
val channel: Long,
|
||||||
private val serverURL: String,
|
private val serverURL: String,
|
||||||
public val displayName: String,
|
val displayName: String,
|
||||||
public val isDeletable: Boolean
|
val isDeletable: Boolean
|
||||||
) {
|
) {
|
||||||
public val server get() = serverURL.toLowerCase()
|
val server get() = serverURL.toLowerCase()
|
||||||
public val id get() = getId(channel, server)
|
val id get() = getId(channel, server)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ public data class OpenGroup(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun toJSON(): Map<String, Any> {
|
fun toJSON(): Map<String, Any> {
|
||||||
return mapOf( "channel" to channel, "server" to server, "displayName" to displayName, "isDeletable" to isDeletable )
|
return mapOf( "channel" to channel, "server" to server, "displayName" to displayName, "isDeletable" to isDeletable )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,16 @@ import org.session.libsession.messaging.MessagingConfiguration
|
|||||||
|
|
||||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||||
import org.session.libsession.messaging.fileserver.FileServerAPI
|
import org.session.libsession.messaging.fileserver.FileServerAPI
|
||||||
|
import org.session.libsession.utilities.ThreadUtils
|
||||||
|
import org.session.libsession.utilities.createContext
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.logging.Log
|
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.internal.util.Hex
|
import org.session.libsignal.service.internal.util.Hex
|
||||||
import org.session.libsignal.service.internal.util.JsonUtil
|
import org.session.libsignal.service.internal.util.JsonUtil
|
||||||
|
import org.session.libsignal.service.loki.api.LokiDotNetAPI
|
||||||
|
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI
|
||||||
import org.session.libsignal.service.loki.utilities.DownloadUtilities
|
import org.session.libsignal.service.loki.utilities.DownloadUtilities
|
||||||
import org.session.libsignal.service.loki.utilities.createContext
|
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
|
||||||
import org.session.libsignal.service.loki.utilities.retryIfNeeded
|
import org.session.libsignal.service.loki.utilities.retryIfNeeded
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -25,7 +27,7 @@ import java.util.*
|
|||||||
object OpenGroupAPI: DotNetAPI() {
|
object OpenGroupAPI: DotNetAPI() {
|
||||||
|
|
||||||
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
|
||||||
@ -36,15 +38,15 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
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 openGroupMessageType = "network.loki.messenger.publicChat"
|
val openGroupMessageType = "network.loki.messenger.publicChat"
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public val profilePictureType = "network.loki.messenger.avatar"
|
val profilePictureType = "network.loki.messenger.avatar"
|
||||||
|
|
||||||
fun getDefaultChats(): List<OpenGroup> {
|
fun getDefaultChats(): List<OpenGroup> {
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -53,7 +55,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Public API
|
// region Public API
|
||||||
public fun getMessages(channel: Long, server: String): Promise<List<OpenGroupMessage>, Exception> {
|
fun getMessages(channel: Long, server: String): Promise<List<OpenGroupMessage>, 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 storage = MessagingConfiguration.shared.storage
|
val storage = MessagingConfiguration.shared.storage
|
||||||
val parameters = mutableMapOf<String, Any>( "include_annotations" to 1 )
|
val parameters = mutableMapOf<String, Any>( "include_annotations" to 1 )
|
||||||
@ -158,7 +160,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 storage = MessagingConfiguration.shared.storage
|
val storage = MessagingConfiguration.shared.storage
|
||||||
val parameters = mutableMapOf<String, Any>()
|
val parameters = mutableMapOf<String, Any>()
|
||||||
@ -190,12 +192,12 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun sendMessage(message: OpenGroupMessage, channel: Long, server: String): Promise<OpenGroupMessage, Exception> {
|
fun sendMessage(message: OpenGroupMessage, channel: Long, server: String): Promise<OpenGroupMessage, Exception> {
|
||||||
val deferred = deferred<OpenGroupMessage, Exception>()
|
val deferred = deferred<OpenGroupMessage, Exception>()
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingConfiguration.shared.storage
|
||||||
val userKeyPair = storage.getUserKeyPair() ?: throw Error.Generic
|
val userKeyPair = storage.getUserKeyPair() ?: throw Error.Generic
|
||||||
val userDisplayName = storage.getUserDisplayName() ?: throw Error.Generic
|
val userDisplayName = storage.getUserDisplayName() ?: throw Error.Generic
|
||||||
Thread {
|
ThreadUtils.queue {
|
||||||
val signedMessage = message.sign(userKeyPair.second)
|
val signedMessage = message.sign(userKeyPair.second)
|
||||||
if (signedMessage == null) {
|
if (signedMessage == null) {
|
||||||
deferred.reject(Error.SigningFailed)
|
deferred.reject(Error.SigningFailed)
|
||||||
@ -225,11 +227,11 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
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).")
|
||||||
@ -241,7 +243,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(",") )
|
||||||
@ -254,7 +256,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
@ -272,7 +274,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun getChannelInfo(channel: Long, server: String): Promise<OpenGroupInfo, Exception> {
|
fun getChannelInfo(channel: Long, server: String): Promise<OpenGroupInfo, 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 ->
|
||||||
@ -296,7 +298,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: OpenGroupInfo, isForcedUpdate: Boolean) {
|
fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: OpenGroupInfo, isForcedUpdate: Boolean) {
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingConfiguration.shared.storage
|
||||||
storage.setUserCount(channel, server, info.memberCount)
|
storage.setUserCount(channel, server, info.memberCount)
|
||||||
storage.updateTitle(groupID, info.displayName)
|
storage.updateTitle(groupID, info.displayName)
|
||||||
@ -309,7 +311,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
@ -325,7 +327,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.")
|
||||||
@ -333,7 +335,7 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.")
|
||||||
@ -341,7 +343,15 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -355,17 +365,17 @@ object OpenGroupAPI: DotNetAPI() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.session.libsession.messaging.opengroups
|
package org.session.libsession.messaging.opengroups
|
||||||
|
|
||||||
public data class OpenGroupInfo (
|
data class OpenGroupInfo (
|
||||||
public val displayName: String,
|
val displayName: String,
|
||||||
public val profilePictureURL: String,
|
val profilePictureURL: String,
|
||||||
public val memberCount: Int
|
val memberCount: Int
|
||||||
)
|
)
|
||||||
|
@ -7,18 +7,18 @@ import org.session.libsignal.service.internal.util.Hex
|
|||||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||||
import org.whispersystems.curve25519.Curve25519
|
import org.whispersystems.curve25519.Curve25519
|
||||||
|
|
||||||
public data class OpenGroupMessage(
|
data class OpenGroupMessage(
|
||||||
public val serverID: Long?,
|
val serverID: Long?,
|
||||||
public val senderPublicKey: String,
|
val senderPublicKey: String,
|
||||||
public val displayName: String,
|
val displayName: String,
|
||||||
public val body: String,
|
val body: String,
|
||||||
public val timestamp: Long,
|
val timestamp: Long,
|
||||||
public val type: String,
|
val type: String,
|
||||||
public val quote: Quote?,
|
val quote: Quote?,
|
||||||
public val attachments: MutableList<Attachment>,
|
val attachments: MutableList<Attachment>,
|
||||||
public val profilePicture: ProfilePicture?,
|
val profilePicture: ProfilePicture?,
|
||||||
public val signature: Signature?,
|
val signature: Signature?,
|
||||||
public val serverTimestamp: Long,
|
val serverTimestamp: Long,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// region Settings
|
// region Settings
|
||||||
@ -98,52 +98,52 @@ public data class OpenGroupMessage(
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Types
|
// region Types
|
||||||
public data class ProfilePicture(
|
data class ProfilePicture(
|
||||||
public val profileKey: ByteArray,
|
val profileKey: ByteArray,
|
||||||
public val url: String,
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
public data class Quote(
|
data class Quote(
|
||||||
public val quotedMessageTimestamp: Long,
|
val quotedMessageTimestamp: Long,
|
||||||
public val quoteePublicKey: String,
|
val quoteePublicKey: String,
|
||||||
public val quotedMessageBody: String,
|
val quotedMessageBody: String,
|
||||||
public val quotedMessageServerID: Long? = null,
|
val quotedMessageServerID: Long? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
public data class Signature(
|
data class Signature(
|
||||||
public val data: ByteArray,
|
val data: ByteArray,
|
||||||
public val version: Long,
|
val version: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
public data class Attachment(
|
data class Attachment(
|
||||||
public val kind: Kind,
|
val kind: Kind,
|
||||||
public val server: String,
|
val server: String,
|
||||||
public val serverID: Long,
|
val serverID: Long,
|
||||||
public val contentType: String,
|
val contentType: String,
|
||||||
public val size: Int,
|
val size: Int,
|
||||||
public val fileName: String,
|
val fileName: String,
|
||||||
public val flags: Int,
|
val flags: Int,
|
||||||
public val width: Int,
|
val width: Int,
|
||||||
public val height: Int,
|
val height: Int,
|
||||||
public val caption: String?,
|
val caption: String?,
|
||||||
public val url: String,
|
val url: String,
|
||||||
/**
|
/**
|
||||||
Guaranteed to be non-`nil` if `kind` is `LinkPreview`.
|
Guaranteed to be non-`nil` if `kind` is `LinkPreview`.
|
||||||
*/
|
*/
|
||||||
public val linkPreviewURL: String?,
|
val linkPreviewURL: String?,
|
||||||
/**
|
/**
|
||||||
Guaranteed to be non-`nil` if `kind` is `LinkPreview`.
|
Guaranteed to be non-`nil` if `kind` is `LinkPreview`.
|
||||||
*/
|
*/
|
||||||
public val linkPreviewTitle: String?,
|
val linkPreviewTitle: String?,
|
||||||
) {
|
) {
|
||||||
public val dotNetAPIType = when {
|
val dotNetAPIType = when {
|
||||||
contentType.startsWith("image") -> "photo"
|
contentType.startsWith("image") -> "photo"
|
||||||
contentType.startsWith("video") -> "video"
|
contentType.startsWith("video") -> "video"
|
||||||
contentType.startsWith("audio") -> "audio"
|
contentType.startsWith("audio") -> "audio"
|
||||||
else -> "other"
|
else -> "other"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum class Kind(val rawValue: String) {
|
enum class Kind(val rawValue: String) {
|
||||||
Attachment("attachment"), LinkPreview("preview")
|
Attachment("attachment"), LinkPreview("preview")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import org.session.libsignal.service.loki.api.*
|
|||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||||
import org.session.libsignal.service.loki.api.utilities.*
|
import org.session.libsignal.service.loki.api.utilities.*
|
||||||
import org.session.libsession.utilities.AESGCM.EncryptionResult
|
import org.session.libsession.utilities.AESGCM.EncryptionResult
|
||||||
|
import org.session.libsession.utilities.ThreadUtils
|
||||||
import org.session.libsession.utilities.getBodyForOnionRequest
|
import org.session.libsession.utilities.getBodyForOnionRequest
|
||||||
import org.session.libsession.utilities.getHeadersForOnionRequest
|
import org.session.libsession.utilities.getHeadersForOnionRequest
|
||||||
import org.session.libsignal.service.loki.utilities.*
|
import org.session.libsignal.service.loki.utilities.*
|
||||||
@ -83,12 +84,12 @@ 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 {
|
||||||
@ -99,7 +100,7 @@ object OnionRequestAPI {
|
|||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
deferred.reject(exception)
|
deferred.reject(exception)
|
||||||
}
|
}
|
||||||
}.start()
|
}
|
||||||
return deferred.promise
|
return deferred.promise
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,10 +314,10 @@ 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 = AESGCM.decrypt(ivAndCiphertext, destinationSymmetricKey)
|
val plaintext = AESGCM.decrypt(ivAndCiphertext, destinationSymmetricKey)
|
||||||
@ -326,7 +327,7 @@ 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<*, *>) {
|
||||||
@ -341,13 +342,13 @@ 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)
|
||||||
}
|
}
|
||||||
@ -360,7 +361,7 @@ 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.libsession.utilities.AESGCM.EncryptionResult
|
import org.session.libsession.utilities.AESGCM.EncryptionResult
|
||||||
import org.session.libsession.utilities.AESGCM
|
import org.session.libsession.utilities.AESGCM
|
||||||
|
import org.session.libsession.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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,24 +7,22 @@ import nl.komponents.kovenant.functional.bind
|
|||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
|
|
||||||
import org.session.libsession.snode.utilities.getRandomElement
|
import org.session.libsession.snode.utilities.getRandomElement
|
||||||
|
import org.session.libsession.utilities.ThreadUtils
|
||||||
|
import org.session.libsession.utilities.createContext
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.logging.Log
|
import org.session.libsignal.libsignal.logging.Log
|
||||||
import org.session.libsignal.service.internal.util.Base64
|
|
||||||
import org.session.libsignal.service.loki.api.MessageWrapper
|
|
||||||
import org.session.libsignal.service.loki.api.utilities.HTTP
|
import org.session.libsignal.service.loki.api.utilities.HTTP
|
||||||
import org.session.libsignal.service.loki.utilities.createContext
|
|
||||||
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
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope
|
|
||||||
|
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
||||||
object SnodeAPI {
|
object SnodeAPI {
|
||||||
val database = SnodeConfiguration.shared.storage
|
val database = SnodeConfiguration.shared.storage
|
||||||
val broadcaster = SnodeConfiguration.shared.broadcaster
|
val broadcaster = SnodeConfiguration.shared.broadcaster
|
||||||
val sharedContext = Kovenant.createContext("LokiAPISharedContext")
|
val sharedContext = Kovenant.createContext()
|
||||||
val messageSendingContext = Kovenant.createContext("LokiAPIMessageSendingContext")
|
val messageSendingContext = Kovenant.createContext()
|
||||||
val messagePollingContext = Kovenant.createContext("LokiAPIMessagePollingContext")
|
val messagePollingContext = Kovenant.createContext()
|
||||||
|
|
||||||
internal var snodeFailureCount: MutableMap<Snode, Int> = mutableMapOf()
|
internal var snodeFailureCount: MutableMap<Snode, Int> = mutableMapOf()
|
||||||
internal var snodePool: Set<Snode>
|
internal var snodePool: Set<Snode>
|
||||||
@ -57,7 +55,7 @@ object SnodeAPI {
|
|||||||
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)
|
||||||
@ -66,12 +64,12 @@ object SnodeAPI {
|
|||||||
val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException
|
val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException
|
||||||
if (httpRequestFailedException != null) {
|
if (httpRequestFailedException != null) {
|
||||||
val error = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey)
|
val error = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey)
|
||||||
if (error != null) { return@Thread deferred.reject(exception) }
|
if (error != null) { 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +89,7 @@ object SnodeAPI {
|
|||||||
)
|
)
|
||||||
val deferred = deferred<Snode, Exception>()
|
val deferred = deferred<Snode, Exception>()
|
||||||
deferred<org.session.libsignal.service.loki.api.Snode, Exception>(SnodeAPI.sharedContext)
|
deferred<org.session.libsignal.service.loki.api.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<*, *>
|
||||||
@ -125,7 +123,7 @@ object SnodeAPI {
|
|||||||
} 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())
|
||||||
|
@ -1,11 +1,48 @@
|
|||||||
@file:JvmName("PromiseUtilities")
|
@file:JvmName("PromiseUtilities")
|
||||||
package org.session.libsession.utilities
|
package org.session.libsession.utilities
|
||||||
|
|
||||||
|
import nl.komponents.kovenant.Context
|
||||||
|
import nl.komponents.kovenant.Kovenant
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.deferred
|
import nl.komponents.kovenant.deferred
|
||||||
|
import nl.komponents.kovenant.jvm.asDispatcher
|
||||||
import org.session.libsignal.libsignal.logging.Log
|
import org.session.libsignal.libsignal.logging.Log
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
|
fun Kovenant.createContext(): Context {
|
||||||
|
return createContext {
|
||||||
|
callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher()
|
||||||
|
workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher()
|
||||||
|
multipleCompletion = { v1, v2 ->
|
||||||
|
Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V, E : Throwable> Promise<V, E>.get(defaultValue: V): V {
|
||||||
|
return try {
|
||||||
|
get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V, E : Throwable> Promise<V, E>.recover(callback: (exception: E) -> V): Promise<V, E> {
|
||||||
|
val deferred = deferred<V, E>()
|
||||||
|
success {
|
||||||
|
deferred.resolve(it)
|
||||||
|
}.fail {
|
||||||
|
try {
|
||||||
|
val value = callback(it)
|
||||||
|
deferred.resolve(value)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
deferred.reject(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deferred.promise
|
||||||
|
}
|
||||||
|
|
||||||
fun <V, E> Promise<V, E>.successBackground(callback: (value: V) -> Unit): Promise<V, E> {
|
fun <V, E> Promise<V, E>.successBackground(callback: (value: V) -> Unit): Promise<V, E> {
|
||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package org.session.libsession.utilities;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class ThreadUtil {
|
|
||||||
|
|
||||||
public static ExecutorService newDynamicSingleThreadedExecutor() {
|
|
||||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
|
|
||||||
new LinkedBlockingQueue<Runnable>());
|
|
||||||
executor.allowCoreThreadTimeOut(true);
|
|
||||||
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
package org.session.libsession.utilities
|
||||||
|
|
||||||
|
import java.util.concurrent.*
|
||||||
|
|
||||||
|
object ThreadUtils {
|
||||||
|
|
||||||
|
internal val executorPool = Executors.newCachedThreadPool()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun queue(target: Runnable) {
|
||||||
|
executorPool.execute(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queue(target: () -> Unit) {
|
||||||
|
executorPool.execute(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newDynamicSingleThreadedExecutor(): ExecutorService {
|
||||||
|
val executor = ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
|
||||||
|
LinkedBlockingQueue())
|
||||||
|
executor.allowCoreThreadTimeOut(true)
|
||||||
|
return executor
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user