mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
WIP: clean up V1 multi device
This commit is contained in:
parent
c5cc191ff3
commit
3285975b1a
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.activities
|
||||
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||
|
||||
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<List<String>>(context) {
|
||||
|
@ -44,12 +44,9 @@ import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.TextSecurePreferences.getBooleanPreference
|
||||
import org.session.libsession.utilities.TextSecurePreferences.setBooleanPreference
|
||||
import org.session.libsession.utilities.Util
|
||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager
|
||||
import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol
|
||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
|
||||
import org.session.libsignal.utilities.ThreadUtils
|
||||
import org.session.libsignal.service.loki.utilities.toHexString
|
||||
import org.thoughtcrime.securesms.loki.dialogs.*
|
||||
@ -177,20 +174,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
if (userPublicKey != null) {
|
||||
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
|
||||
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
application.publicChatManager.startPollersIfNeeded()
|
||||
}
|
||||
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application)
|
||||
MultiDeviceProtocol.configureIfNeeded(apiDB)
|
||||
IP2Country.configureIfNeeded(this)
|
||||
application.registerForFCMIfNeeded(false)
|
||||
// Preload device links to make message sending quicker
|
||||
val publicKeys = ContactUtilities.getAllContacts(this).filter { contact ->
|
||||
!contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave
|
||||
}.map {
|
||||
it.recipient.address.toString()
|
||||
}.toSet()
|
||||
FileServerAPI.shared.getDeviceLinks(publicKeys)
|
||||
// Observe blocked contacts changed events
|
||||
val broadcastReceiver = object : BroadcastReceiver() {
|
||||
|
||||
|
@ -27,7 +27,6 @@ import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatMessage
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
@ -161,14 +160,11 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
||||
fun pollForNewMessages(): Promise<Unit, Exception> {
|
||||
fun processIncomingMessage(message: PublicChatMessage) {
|
||||
// If the sender of the current message is not a slave device, set the display name in the database
|
||||
val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.senderPublicKey)
|
||||
if (masterHexEncodedPublicKey == null) {
|
||||
val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName)
|
||||
}
|
||||
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.senderPublicKey
|
||||
val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName)
|
||||
val senderHexEncodedPublicKey = message.senderPublicKey
|
||||
val serviceDataMessage = getDataMessage(message)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false, false)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false)
|
||||
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
||||
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||
} else {
|
||||
@ -221,8 +217,6 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
||||
}
|
||||
if (isPollOngoing) { return Promise.of(Unit) }
|
||||
isPollOngoing = true
|
||||
val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey)
|
||||
var uniqueDevices = setOf<String>()
|
||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
|
||||
@ -240,20 +234,10 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
||||
*/
|
||||
Promise.of(messages)
|
||||
}
|
||||
promise.successBackground {
|
||||
/*
|
||||
val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
|
||||
// This will return null if the current device is a master device
|
||||
MultiDeviceProtocol.shared.getMasterDevice(it)
|
||||
}.toSet()
|
||||
// Fetch the display names of the master devices
|
||||
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
|
||||
*/
|
||||
}
|
||||
promise.successBackground { messages ->
|
||||
// Process messages in the background
|
||||
messages.forEach { message ->
|
||||
if (userDevices.contains(message.senderPublicKey)) {
|
||||
if (message.senderPublicKey == userHexEncodedPublicKey) {
|
||||
processOutgoingMessage(message)
|
||||
} else {
|
||||
processIncomingMessage(message)
|
||||
|
@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
import org.session.libsignal.service.loki.api.Snode
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||
import org.session.libsignal.service.loki.utilities.toHexString
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
@ -459,48 +458,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(closedGroupPublicKeysTable, "${Companion.groupPublicKey} = ?", wrap(groupPublicKey))
|
||||
}
|
||||
|
||||
// region Deprecated
|
||||
override fun getDeviceLinks(publicKey: String): Set<DeviceLink> {
|
||||
return setOf()
|
||||
/*
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.getAll(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) { cursor ->
|
||||
val masterHexEncodedPublicKey = cursor.getString(masterPublicKey)
|
||||
val slaveHexEncodedPublicKey = cursor.getString(slavePublicKey)
|
||||
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
|
||||
val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
|
||||
DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
|
||||
}.toSet()
|
||||
*/
|
||||
}
|
||||
|
||||
override fun clearDeviceLinks(publicKey: String) {
|
||||
/*
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey ))
|
||||
*/
|
||||
}
|
||||
|
||||
override fun addDeviceLink(deviceLink: DeviceLink) {
|
||||
/*
|
||||
val database = databaseHelper.writableDatabase
|
||||
val values = ContentValues()
|
||||
values.put(masterPublicKey, deviceLink.masterPublicKey)
|
||||
values.put(slavePublicKey, deviceLink.slavePublicKey)
|
||||
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
|
||||
if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
|
||||
database.insertOrUpdate(deviceLinkCache, values, "$masterPublicKey = ? AND $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
|
||||
*/
|
||||
}
|
||||
|
||||
override fun removeDeviceLink(deviceLink: DeviceLink) {
|
||||
/*
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
|
||||
*/
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
// region Convenience
|
||||
|
@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.libsignal.loki.SessionResetStatus
|
||||
import org.session.libsignal.service.api.messages.SignalServiceContent
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
import java.util.*
|
||||
|
||||
object SessionManagementProtocol {
|
||||
@ -95,13 +94,12 @@ object SessionManagementProtocol {
|
||||
|
||||
@JvmStatic
|
||||
fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) {
|
||||
val masterDevicePublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey
|
||||
val masterDeviceAsRecipient = recipient(context, masterDevicePublicKey)
|
||||
if (masterDeviceAsRecipient.isGroupRecipient) { return }
|
||||
val recipient = recipient(context, publicKey)
|
||||
if (recipient.isGroupRecipient) { return }
|
||||
if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) {
|
||||
return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey)
|
||||
}
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(masterDeviceAsRecipient)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
||||
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey)
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@ import org.session.libsignal.utilities.Base64
|
||||
import org.session.libsignal.utilities.JsonUtil
|
||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import org.session.libsignal.service.loki.utilities.*
|
||||
import java.net.URL
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -17,21 +16,6 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : DotNetAPI() {
|
||||
|
||||
companion object {
|
||||
// region Settings
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
private val deviceLinkType = "network.loki.messenger.devicemapping"
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
private val deviceLinkRequestCache = ConcurrentHashMap<String, Promise<Set<DeviceLink>, Exception>>()
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
private val deviceLinkUpdateInterval = 60 * 1000
|
||||
private val lastDeviceLinkUpdate = ConcurrentHashMap<String, Long>()
|
||||
|
||||
internal val fileServerPublicKey = "62509D59BDEEC404DD0D489C1E15BA8F94FD3D619B01C1BF48A9922BFCB7311C"
|
||||
internal val maxRetryCount = 4
|
||||
|
||||
@ -63,172 +47,6 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat
|
||||
// endregion
|
||||
}
|
||||
|
||||
// region Device Link Update Result
|
||||
sealed class DeviceLinkUpdateResult {
|
||||
class Success(val publicKey: String, val deviceLinks: Set<DeviceLink>) : DeviceLinkUpdateResult()
|
||||
class Failure(val publicKey: String, val error: Exception) : DeviceLinkUpdateResult()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region API
|
||||
public fun hasDeviceLinkCacheExpired(referenceTime: Long = System.currentTimeMillis(), publicKey: String): Boolean {
|
||||
return !lastDeviceLinkUpdate.containsKey(publicKey) || (referenceTime - lastDeviceLinkUpdate[publicKey]!! > deviceLinkUpdateInterval)
|
||||
}
|
||||
|
||||
fun getDeviceLinks(publicKey: String, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
|
||||
return Promise.of(setOf())
|
||||
/*
|
||||
if (deviceLinkRequestCache.containsKey(publicKey) && !isForcedUpdate) {
|
||||
val result = deviceLinkRequestCache[publicKey]
|
||||
if (result != null) { return result } // A request was already pending
|
||||
}
|
||||
val promise = getDeviceLinks(setOf(publicKey), isForcedUpdate)
|
||||
deviceLinkRequestCache[publicKey] = promise
|
||||
promise.always {
|
||||
deviceLinkRequestCache.remove(publicKey)
|
||||
}
|
||||
return promise
|
||||
*/
|
||||
}
|
||||
|
||||
fun getDeviceLinks(publicKeys: Set<String>, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
|
||||
return Promise.of(setOf())
|
||||
/*
|
||||
val validPublicKeys = publicKeys.filter { PublicKeyValidation.isValid(it) }
|
||||
val now = System.currentTimeMillis()
|
||||
// IMPORTANT: Don't fetch device links for the current user (i.e. don't remove the it != userHexEncodedPublicKey) check below
|
||||
val updatees = validPublicKeys.filter { it != userPublicKey && (hasDeviceLinkCacheExpired(now, it) || isForcedUpdate) }.toSet()
|
||||
val cachedDeviceLinks = validPublicKeys.minus(updatees).flatMap { database.getDeviceLinks(it) }.toSet()
|
||||
if (updatees.isEmpty()) {
|
||||
return Promise.of(cachedDeviceLinks)
|
||||
} else {
|
||||
return getUserProfiles(updatees, server, true).map(SnodeAPI.sharedContext) { data ->
|
||||
data.map dataMap@ { node ->
|
||||
val publicKey = node["username"] as String
|
||||
val annotations = node["annotations"] as List<Map<*, *>>
|
||||
val deviceLinksAnnotation = annotations.find {
|
||||
annotation -> (annotation["type"] as String) == deviceLinkType
|
||||
} ?: return@dataMap DeviceLinkUpdateResult.Success(publicKey, setOf())
|
||||
val value = deviceLinksAnnotation["value"] as Map<*, *>
|
||||
val deviceLinksAsJSON = value["authorisations"] as List<Map<*, *>>
|
||||
val deviceLinks = deviceLinksAsJSON.mapNotNull { deviceLinkAsJSON ->
|
||||
try {
|
||||
val masterPublicKey = deviceLinkAsJSON["primaryDevicePubKey"] as String
|
||||
val slavePublicKey = deviceLinkAsJSON["secondaryDevicePubKey"] as String
|
||||
var requestSignature: ByteArray? = null
|
||||
var authorizationSignature: ByteArray? = null
|
||||
if (deviceLinkAsJSON["requestSignature"] != null) {
|
||||
val base64EncodedSignature = deviceLinkAsJSON["requestSignature"] as String
|
||||
requestSignature = Base64.decode(base64EncodedSignature)
|
||||
}
|
||||
if (deviceLinkAsJSON["grantSignature"] != null) {
|
||||
val base64EncodedSignature = deviceLinkAsJSON["grantSignature"] as String
|
||||
authorizationSignature = Base64.decode(base64EncodedSignature)
|
||||
}
|
||||
val deviceLink = DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature)
|
||||
val isValid = deviceLink.verify()
|
||||
if (!isValid) {
|
||||
Log.d("Loki", "Ignoring invalid device link: $deviceLinkAsJSON.")
|
||||
return@mapNotNull null
|
||||
}
|
||||
deviceLink
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to parse device links for $publicKey from $deviceLinkAsJSON due to error: $e.")
|
||||
null
|
||||
}
|
||||
}.toSet()
|
||||
DeviceLinkUpdateResult.Success(publicKey, deviceLinks)
|
||||
}
|
||||
}.recover { e ->
|
||||
publicKeys.map { DeviceLinkUpdateResult.Failure(it, e) }
|
||||
}.success { updateResults ->
|
||||
for (updateResult in updateResults) {
|
||||
if (updateResult is DeviceLinkUpdateResult.Success) {
|
||||
database.clearDeviceLinks(updateResult.publicKey)
|
||||
updateResult.deviceLinks.forEach { database.addDeviceLink(it) }
|
||||
} else {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}.map(SnodeAPI.sharedContext) { updateResults ->
|
||||
val deviceLinks = mutableListOf<DeviceLink>()
|
||||
for (updateResult in updateResults) {
|
||||
when (updateResult) {
|
||||
is DeviceLinkUpdateResult.Success -> {
|
||||
lastDeviceLinkUpdate[updateResult.publicKey] = now
|
||||
deviceLinks.addAll(updateResult.deviceLinks)
|
||||
}
|
||||
is DeviceLinkUpdateResult.Failure -> {
|
||||
if (updateResult.error is SnodeAPI.Error.ParsingFailed) {
|
||||
lastDeviceLinkUpdate[updateResult.publicKey] = now // Don't infinitely update in case of a parsing failure
|
||||
}
|
||||
deviceLinks.addAll(database.getDeviceLinks(updateResult.publicKey)) // Fall back on cached device links in case of a failure
|
||||
}
|
||||
}
|
||||
}
|
||||
// Updatees that didn't show up in the response provided by the file server are assumed to not have any device links
|
||||
val excludedUpdatees = updatees.filter { updatee ->
|
||||
updateResults.find { updateResult ->
|
||||
when (updateResult) {
|
||||
is DeviceLinkUpdateResult.Success -> updateResult.publicKey == updatee
|
||||
is DeviceLinkUpdateResult.Failure -> updateResult.publicKey == updatee
|
||||
}
|
||||
} == null
|
||||
}
|
||||
excludedUpdatees.forEach {
|
||||
lastDeviceLinkUpdate[it] = now
|
||||
}
|
||||
deviceLinks.union(cachedDeviceLinks)
|
||||
}.recover {
|
||||
publicKeys.flatMap { database.getDeviceLinks(it) }.toSet()
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
fun setDeviceLinks(deviceLinks: Set<DeviceLink>): Promise<Unit, Exception> {
|
||||
return Promise.of(Unit)
|
||||
/*
|
||||
val isMaster = deviceLinks.find { it.masterPublicKey == userPublicKey } != null
|
||||
val deviceLinksAsJSON = deviceLinks.map { it.toJSON() }
|
||||
val value = if (deviceLinks.isNotEmpty()) mapOf( "isPrimary" to isMaster, "authorisations" to deviceLinksAsJSON ) else null
|
||||
val annotation = mapOf( "type" to deviceLinkType, "value" to value )
|
||||
val parameters = mapOf( "annotations" to listOf( annotation ) )
|
||||
return retryIfNeeded(maxRetryCount) {
|
||||
execute(HTTPVerb.PATCH, server, "/users/me", parameters = parameters)
|
||||
}.map { Unit }
|
||||
*/
|
||||
}
|
||||
|
||||
fun addDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
|
||||
return Promise.of(Unit)
|
||||
/*
|
||||
Log.d("Loki", "Updating device links.")
|
||||
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
|
||||
val mutableDeviceLinks = deviceLinks.toMutableSet()
|
||||
mutableDeviceLinks.add(deviceLink)
|
||||
setDeviceLinks(mutableDeviceLinks)
|
||||
}.success {
|
||||
database.addDeviceLink(deviceLink)
|
||||
}.map { Unit }
|
||||
*/
|
||||
}
|
||||
|
||||
fun removeDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
|
||||
return Promise.of(Unit)
|
||||
/*
|
||||
Log.d("Loki", "Updating device links.")
|
||||
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
|
||||
val mutableDeviceLinks = deviceLinks.toMutableSet()
|
||||
mutableDeviceLinks.remove(deviceLink)
|
||||
setDeviceLinks(mutableDeviceLinks)
|
||||
}.success {
|
||||
database.removeDeviceLink(deviceLink)
|
||||
}.map { Unit }
|
||||
*/
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Open Group Server Public Key
|
||||
fun getPublicKeyForOpenGroupServer(openGroupServer: String): Promise<String, Exception> {
|
||||
val publicKey = database.getOpenGroupPublicKey(openGroupServer)
|
||||
|
@ -38,7 +38,6 @@ import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.color.MaterialColor;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
import org.session.libsession.messaging.avatars.ContactColors;
|
||||
import org.session.libsession.messaging.avatars.ContactPhoto;
|
||||
import org.session.libsession.messaging.avatars.GroupRecordContactPhoto;
|
||||
@ -550,12 +549,7 @@ public class Recipient implements RecipientModifiedListener {
|
||||
}
|
||||
|
||||
public synchronized boolean isBlocked() {
|
||||
String masterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(this.address.serialize());
|
||||
if (masterPublicKey != null) {
|
||||
return Recipient.from(context, Address.Companion.fromSerialized(masterPublicKey), false).blocked;
|
||||
} else {
|
||||
return blocked;
|
||||
}
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
|
@ -78,8 +78,6 @@ import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
|
||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol;
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol;
|
||||
import org.session.libsignal.service.loki.utilities.Broadcaster;
|
||||
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
|
||||
import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory;
|
||||
@ -248,7 +246,7 @@ public class SignalServiceMessageSender {
|
||||
long timestamp = message.getTimestamp();
|
||||
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
|
||||
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
|
||||
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), message.getDeviceLink().isPresent(), useFallbackEncryption, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
|
||||
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), useFallbackEncryption, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
|
||||
|
||||
// // Loki - This shouldn't get invoked for note to self
|
||||
// boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent();
|
||||
@ -298,18 +296,6 @@ public class SignalServiceMessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
// Loki - This shouldn't get invoked for note to self
|
||||
if (needsSyncInResults && SyncMessagesProtocol.shared.shouldSyncMessage(message)) {
|
||||
byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.<SignalServiceAddress>absent(), timestamp, results);
|
||||
// Loki - Customize multi device logic
|
||||
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
||||
for (String device : linkedDevices) {
|
||||
SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
|
||||
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store);
|
||||
sendMessage(deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryption);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@ -341,14 +327,6 @@ public class SignalServiceMessageSender {
|
||||
} else {
|
||||
throw new IOException("Unsupported sync message!");
|
||||
}
|
||||
|
||||
// Loki - Customize multi device logic
|
||||
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
||||
for (String device : linkedDevices) {
|
||||
SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
|
||||
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, device, store);
|
||||
sendMessageToPrivateChat(0, deviceAsAddress, Optional.absent(), timestamp, content, false, message.getTTL(), useFallbackEncryption, false, false, Optional.absent());
|
||||
}
|
||||
}
|
||||
|
||||
public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
|
||||
@ -462,10 +440,6 @@ public class SignalServiceMessageSender {
|
||||
builder.setFlags(DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE);
|
||||
}
|
||||
|
||||
if (message.isDeviceUnlinkingRequest()) {
|
||||
builder.setFlags(DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE);
|
||||
}
|
||||
|
||||
if (message.getExpiresInSeconds() > 0) {
|
||||
builder.setExpireTimer(message.getExpiresInSeconds());
|
||||
}
|
||||
@ -881,7 +855,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
try {
|
||||
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store);
|
||||
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, false, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
|
||||
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
|
||||
results.add(result);
|
||||
} catch (UnregisteredUserException e) {
|
||||
Log.w(TAG, e);
|
||||
@ -905,7 +879,7 @@ public class SignalServiceMessageSender {
|
||||
throws IOException
|
||||
{
|
||||
// Loki - This method is only invoked for various types of control messages
|
||||
return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, false, useFallbackEncryption, false,Optional.absent());
|
||||
return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, useFallbackEncryption, false,Optional.absent());
|
||||
}
|
||||
|
||||
public SendMessageResult sendMessage(final long messageID,
|
||||
@ -915,7 +889,6 @@ public class SignalServiceMessageSender {
|
||||
byte[] content,
|
||||
boolean online,
|
||||
int ttl,
|
||||
boolean isDeviceLinkMessage,
|
||||
boolean useFallbackEncryption,
|
||||
boolean isClosedGroup,
|
||||
boolean notifyPNServer,
|
||||
|
@ -91,7 +91,6 @@ import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupUtilities;
|
||||
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@ -226,30 +225,6 @@ public class SignalServiceCipher {
|
||||
content.setDataMessage(signalServiceDataMessage);
|
||||
}
|
||||
|
||||
return content;
|
||||
} else if (message.hasDeviceLinkMessage()) {
|
||||
SignalServiceProtos.DeviceLinkMessage protoDeviceLinkMessage = message.getDeviceLinkMessage();
|
||||
String masterPublicKey = protoDeviceLinkMessage.getPrimaryPublicKey();
|
||||
String slavePublicKey = protoDeviceLinkMessage.getSecondaryPublicKey();
|
||||
byte[] requestSignature = protoDeviceLinkMessage.hasRequestSignature() ? protoDeviceLinkMessage.getRequestSignature().toByteArray() : null;
|
||||
byte[] authorizationSignature = protoDeviceLinkMessage.hasAuthorizationSignature() ? protoDeviceLinkMessage.getAuthorizationSignature().toByteArray() : null;
|
||||
DeviceLink deviceLink = new DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature);
|
||||
SignalServiceCipher.Metadata metadata = plaintext.getMetadata();
|
||||
SignalServiceContent content = new SignalServiceContent(deviceLink, metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp());
|
||||
|
||||
content.setPreKeyBundleMessage(preKeyBundleMessage);
|
||||
|
||||
if (message.hasSyncMessage() && message.getSyncMessage().hasContacts()) {
|
||||
SignalServiceSyncMessage syncMessage = createSynchronizeMessage(metadata, message.getSyncMessage());
|
||||
content.setSyncMessage(syncMessage);
|
||||
}
|
||||
|
||||
if (message.hasDataMessage()) {
|
||||
setProfile(message.getDataMessage(), content);
|
||||
SignalServiceDataMessage signalServiceDataMessage = createSignalServiceMessage(metadata, message.getDataMessage());
|
||||
content.setDataMessage(signalServiceDataMessage);
|
||||
}
|
||||
|
||||
return content;
|
||||
} else if (message.hasDataMessage()) {
|
||||
DataMessage dataMessage = message.getDataMessage();
|
||||
@ -259,8 +234,7 @@ public class SignalServiceCipher {
|
||||
plaintext.getMetadata().getSender(),
|
||||
plaintext.getMetadata().getSenderDevice(),
|
||||
plaintext.getMetadata().getTimestamp(),
|
||||
plaintext.getMetadata().isNeedsReceipt(),
|
||||
signalServiceDataMessage.isDeviceUnlinkingRequest());
|
||||
plaintext.getMetadata().isNeedsReceipt());
|
||||
|
||||
content.setPreKeyBundleMessage(preKeyBundleMessage);
|
||||
|
||||
@ -425,10 +399,8 @@ public class SignalServiceCipher {
|
||||
previews,
|
||||
sticker,
|
||||
null,
|
||||
null,
|
||||
closedGroupUpdate,
|
||||
closedGroupUpdateV2,
|
||||
isDeviceUnlinkingRequest,
|
||||
syncTarget);
|
||||
}
|
||||
|
||||
|
@ -7,15 +7,9 @@
|
||||
package org.session.libsignal.service.api.messages;
|
||||
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceNullMessage;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceTypingMessage;
|
||||
import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
|
||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
|
||||
|
||||
public class SignalServiceContent {
|
||||
@ -25,8 +19,6 @@ public class SignalServiceContent {
|
||||
private final boolean needsReceipt;
|
||||
|
||||
// Loki
|
||||
private final boolean isDeviceUnlinkingRequest;
|
||||
|
||||
private Optional<SignalServiceDataMessage> message;
|
||||
private Optional<SignalServiceSyncMessage> synchronizeMessage;
|
||||
private final Optional<SignalServiceCallMessage> callMessage;
|
||||
@ -35,13 +27,12 @@ public class SignalServiceContent {
|
||||
private final Optional<SignalServiceTypingMessage> typingMessage;
|
||||
|
||||
// Loki
|
||||
private final Optional<DeviceLink> deviceLink;
|
||||
public Optional<SignalServiceProtos.Content> configurationMessageProto = Optional.absent();
|
||||
public Optional<PreKeyBundleMessage> preKeyBundleMessage = Optional.absent();
|
||||
public Optional<String> senderDisplayName = Optional.absent();
|
||||
public Optional<String> senderProfilePictureURL = Optional.absent();
|
||||
|
||||
public SignalServiceContent(SignalServiceDataMessage message, String sender, int senderDevice, long timestamp, boolean needsReceipt, boolean isDeviceUnlinkingRequest) {
|
||||
public SignalServiceContent(SignalServiceDataMessage message, String sender, int senderDevice, long timestamp, boolean needsReceipt) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
@ -52,8 +43,6 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.absent();
|
||||
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
|
||||
}
|
||||
|
||||
public SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, String sender, int senderDevice, long timestamp) {
|
||||
@ -67,8 +56,6 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.absent();
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public SignalServiceContent(SignalServiceCallMessage callMessage, String sender, int senderDevice, long timestamp) {
|
||||
@ -82,8 +69,6 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.absent();
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public SignalServiceContent(SignalServiceReceiptMessage receiptMessage, String sender, int senderDevice, long timestamp) {
|
||||
@ -97,8 +82,6 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.of(receiptMessage);
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.absent();
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public SignalServiceContent(SignalServiceTypingMessage typingMessage, String sender, int senderDevice, long timestamp) {
|
||||
@ -112,23 +95,6 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.of(typingMessage);
|
||||
this.deviceLink = Optional.absent();
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public SignalServiceContent(DeviceLink deviceLink, String sender, int senderDevice, long timestamp) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.needsReceipt = false;
|
||||
this.message = Optional.absent();
|
||||
this.synchronizeMessage = Optional.absent();
|
||||
this.callMessage = Optional.absent();
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.fromNullable(deviceLink);
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public SignalServiceContent(SignalServiceProtos.Content configurationMessageProto, String sender, int senderDevice, long timestamp) {
|
||||
@ -142,9 +108,7 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.absent();
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.absent();
|
||||
this.configurationMessageProto = Optional.fromNullable(configurationMessageProto);
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public SignalServiceContent(SignalServiceNullMessage nullMessage, String sender, int senderDevice, long timestamp) {
|
||||
@ -158,8 +122,6 @@ public class SignalServiceContent {
|
||||
this.nullMessage = Optional.of(nullMessage);
|
||||
this.readMessage = Optional.absent();
|
||||
this.typingMessage = Optional.absent();
|
||||
this.deviceLink = Optional.absent();
|
||||
this.isDeviceUnlinkingRequest = false;
|
||||
}
|
||||
|
||||
public Optional<SignalServiceDataMessage> getDataMessage() {
|
||||
@ -203,9 +165,6 @@ public class SignalServiceContent {
|
||||
public Optional<SignalServiceNullMessage> getNullMessage() { return nullMessage; }
|
||||
|
||||
// Loki
|
||||
public boolean isDeviceUnlinkingRequest() { return isDeviceUnlinkingRequest; }
|
||||
|
||||
public Optional<DeviceLink> getDeviceLink() { return deviceLink; }
|
||||
|
||||
public void setPreKeyBundleMessage(PreKeyBundleMessage preKeyBundleMessage) { this.preKeyBundleMessage = Optional.fromNullable(preKeyBundleMessage); }
|
||||
|
||||
|
@ -13,7 +13,6 @@ import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateV2;
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate;
|
||||
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -37,10 +36,8 @@ public class SignalServiceDataMessage {
|
||||
private final Optional<Sticker> sticker;
|
||||
// Loki
|
||||
private final Optional<PreKeyBundle> preKeyBundle;
|
||||
private final Optional<DeviceLink> deviceLink;
|
||||
private final Optional<ClosedGroupUpdate> closedGroupUpdate;
|
||||
private final Optional<ClosedGroupUpdateV2> closedGroupUpdateV2;
|
||||
private final boolean isDeviceUnlinkingRequest;
|
||||
private final Optional<String> syncTarget;
|
||||
|
||||
/**
|
||||
@ -135,7 +132,7 @@ public class SignalServiceDataMessage {
|
||||
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
|
||||
Sticker sticker)
|
||||
{
|
||||
this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null, false, null);
|
||||
this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,9 +151,9 @@ public class SignalServiceDataMessage {
|
||||
String body, boolean endSession, int expiresInSeconds,
|
||||
boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate,
|
||||
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
|
||||
Sticker sticker, PreKeyBundle preKeyBundle, DeviceLink deviceLink,
|
||||
Sticker sticker, PreKeyBundle preKeyBundle,
|
||||
ClosedGroupUpdate closedGroupUpdate, ClosedGroupUpdateV2 closedGroupUpdateV2,
|
||||
boolean isDeviceUnlinkingRequest, String syncTarget)
|
||||
String syncTarget)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
this.body = Optional.fromNullable(body);
|
||||
@ -169,10 +166,8 @@ public class SignalServiceDataMessage {
|
||||
this.quote = Optional.fromNullable(quote);
|
||||
this.sticker = Optional.fromNullable(sticker);
|
||||
this.preKeyBundle = Optional.fromNullable(preKeyBundle);
|
||||
this.deviceLink = Optional.fromNullable(deviceLink);
|
||||
this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate);
|
||||
this.closedGroupUpdateV2 = Optional.fromNullable(closedGroupUpdateV2);
|
||||
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
|
||||
this.syncTarget = Optional.fromNullable(syncTarget);
|
||||
|
||||
if (attachments != null && !attachments.isEmpty()) {
|
||||
@ -273,26 +268,18 @@ public class SignalServiceDataMessage {
|
||||
}
|
||||
|
||||
// Loki
|
||||
public boolean isDeviceUnlinkingRequest() {
|
||||
return isDeviceUnlinkingRequest;
|
||||
}
|
||||
|
||||
public Optional<ClosedGroupUpdate> getClosedGroupUpdate() { return closedGroupUpdate; }
|
||||
|
||||
public Optional<ClosedGroupUpdateV2> getClosedGroupUpdateV2() { return closedGroupUpdateV2; }
|
||||
|
||||
public Optional<PreKeyBundle> getPreKeyBundle() { return preKeyBundle; }
|
||||
|
||||
public Optional<DeviceLink> getDeviceLink() { return deviceLink; }
|
||||
|
||||
public boolean hasVisibleContent() {
|
||||
return (body.isPresent() && !body.get().isEmpty())
|
||||
|| (attachments.isPresent() && !attachments.get().isEmpty());
|
||||
}
|
||||
|
||||
public int getTTL() {
|
||||
if (deviceLink.isPresent()) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceLink); }
|
||||
else if (isDeviceUnlinkingRequest) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceUnlinkingRequest); }
|
||||
return TTLUtilities.getTTL(TTLUtilities.MessageType.Regular);
|
||||
}
|
||||
|
||||
@ -312,9 +299,7 @@ public class SignalServiceDataMessage {
|
||||
private Quote quote;
|
||||
private Sticker sticker;
|
||||
private PreKeyBundle preKeyBundle;
|
||||
private DeviceLink deviceLink;
|
||||
private String syncTarget;
|
||||
private boolean isDeviceUnlinkingRequest;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
@ -411,25 +396,14 @@ public class SignalServiceDataMessage {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withDeviceLink(DeviceLink deviceLink) {
|
||||
this.deviceLink = deviceLink;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder asDeviceUnlinkingRequest(boolean isDeviceUnlinkingRequest) {
|
||||
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SignalServiceDataMessage build() {
|
||||
if (timestamp == 0) timestamp = System.currentTimeMillis();
|
||||
// closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob)
|
||||
return new SignalServiceDataMessage(timestamp, group, attachments, body, endSession,
|
||||
expiresInSeconds, expirationUpdate, profileKey,
|
||||
profileKeyUpdate, quote, sharedContacts, previews,
|
||||
sticker, preKeyBundle, deviceLink,
|
||||
null, null,
|
||||
isDeviceUnlinkingRequest, syncTarget);
|
||||
sticker, preKeyBundle, null, null,
|
||||
syncTarget);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import org.session.libsignal.utilities.JsonUtil
|
||||
import org.session.libsignal.service.loki.api.LokiDotNetAPI
|
||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import org.session.libsignal.service.loki.utilities.*
|
||||
import java.net.URL
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -18,20 +17,6 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat
|
||||
|
||||
companion object {
|
||||
// region Settings
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
private val deviceLinkType = "network.loki.messenger.devicemapping"
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
private val deviceLinkRequestCache = ConcurrentHashMap<String, Promise<Set<DeviceLink>, Exception>>()
|
||||
/**
|
||||
* Deprecated.
|
||||
*/
|
||||
private val deviceLinkUpdateInterval = 60 * 1000
|
||||
private val lastDeviceLinkUpdate = ConcurrentHashMap<String, Long>()
|
||||
|
||||
internal val fileServerPublicKey = "62509D59BDEEC404DD0D489C1E15BA8F94FD3D619B01C1BF48A9922BFCB7311C"
|
||||
internal val maxRetryCount = 4
|
||||
|
||||
@ -62,172 +47,6 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat
|
||||
// endregion
|
||||
}
|
||||
|
||||
// region Device Link Update Result
|
||||
sealed class DeviceLinkUpdateResult {
|
||||
class Success(val publicKey: String, val deviceLinks: Set<DeviceLink>) : DeviceLinkUpdateResult()
|
||||
class Failure(val publicKey: String, val error: Exception) : DeviceLinkUpdateResult()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region API
|
||||
public fun hasDeviceLinkCacheExpired(referenceTime: Long = System.currentTimeMillis(), publicKey: String): Boolean {
|
||||
return !lastDeviceLinkUpdate.containsKey(publicKey) || (referenceTime - lastDeviceLinkUpdate[publicKey]!! > deviceLinkUpdateInterval)
|
||||
}
|
||||
|
||||
fun getDeviceLinks(publicKey: String, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
|
||||
return Promise.of(setOf())
|
||||
/*
|
||||
if (deviceLinkRequestCache.containsKey(publicKey) && !isForcedUpdate) {
|
||||
val result = deviceLinkRequestCache[publicKey]
|
||||
if (result != null) { return result } // A request was already pending
|
||||
}
|
||||
val promise = getDeviceLinks(setOf(publicKey), isForcedUpdate)
|
||||
deviceLinkRequestCache[publicKey] = promise
|
||||
promise.always {
|
||||
deviceLinkRequestCache.remove(publicKey)
|
||||
}
|
||||
return promise
|
||||
*/
|
||||
}
|
||||
|
||||
fun getDeviceLinks(publicKeys: Set<String>, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
|
||||
return Promise.of(setOf())
|
||||
/*
|
||||
val validPublicKeys = publicKeys.filter { PublicKeyValidation.isValid(it) }
|
||||
val now = System.currentTimeMillis()
|
||||
// IMPORTANT: Don't fetch device links for the current user (i.e. don't remove the it != userHexEncodedPublicKey) check below
|
||||
val updatees = validPublicKeys.filter { it != userPublicKey && (hasDeviceLinkCacheExpired(now, it) || isForcedUpdate) }.toSet()
|
||||
val cachedDeviceLinks = validPublicKeys.minus(updatees).flatMap { database.getDeviceLinks(it) }.toSet()
|
||||
if (updatees.isEmpty()) {
|
||||
return Promise.of(cachedDeviceLinks)
|
||||
} else {
|
||||
return getUserProfiles(updatees, server, true).map(SnodeAPI.sharedContext) { data ->
|
||||
data.map dataMap@ { node ->
|
||||
val publicKey = node["username"] as String
|
||||
val annotations = node["annotations"] as List<Map<*, *>>
|
||||
val deviceLinksAnnotation = annotations.find {
|
||||
annotation -> (annotation["type"] as String) == deviceLinkType
|
||||
} ?: return@dataMap DeviceLinkUpdateResult.Success(publicKey, setOf())
|
||||
val value = deviceLinksAnnotation["value"] as Map<*, *>
|
||||
val deviceLinksAsJSON = value["authorisations"] as List<Map<*, *>>
|
||||
val deviceLinks = deviceLinksAsJSON.mapNotNull { deviceLinkAsJSON ->
|
||||
try {
|
||||
val masterPublicKey = deviceLinkAsJSON["primaryDevicePubKey"] as String
|
||||
val slavePublicKey = deviceLinkAsJSON["secondaryDevicePubKey"] as String
|
||||
var requestSignature: ByteArray? = null
|
||||
var authorizationSignature: ByteArray? = null
|
||||
if (deviceLinkAsJSON["requestSignature"] != null) {
|
||||
val base64EncodedSignature = deviceLinkAsJSON["requestSignature"] as String
|
||||
requestSignature = Base64.decode(base64EncodedSignature)
|
||||
}
|
||||
if (deviceLinkAsJSON["grantSignature"] != null) {
|
||||
val base64EncodedSignature = deviceLinkAsJSON["grantSignature"] as String
|
||||
authorizationSignature = Base64.decode(base64EncodedSignature)
|
||||
}
|
||||
val deviceLink = DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature)
|
||||
val isValid = deviceLink.verify()
|
||||
if (!isValid) {
|
||||
Log.d("Loki", "Ignoring invalid device link: $deviceLinkAsJSON.")
|
||||
return@mapNotNull null
|
||||
}
|
||||
deviceLink
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to parse device links for $publicKey from $deviceLinkAsJSON due to error: $e.")
|
||||
null
|
||||
}
|
||||
}.toSet()
|
||||
DeviceLinkUpdateResult.Success(publicKey, deviceLinks)
|
||||
}
|
||||
}.recover { e ->
|
||||
publicKeys.map { DeviceLinkUpdateResult.Failure(it, e) }
|
||||
}.success { updateResults ->
|
||||
for (updateResult in updateResults) {
|
||||
if (updateResult is DeviceLinkUpdateResult.Success) {
|
||||
database.clearDeviceLinks(updateResult.publicKey)
|
||||
updateResult.deviceLinks.forEach { database.addDeviceLink(it) }
|
||||
} else {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}.map(SnodeAPI.sharedContext) { updateResults ->
|
||||
val deviceLinks = mutableListOf<DeviceLink>()
|
||||
for (updateResult in updateResults) {
|
||||
when (updateResult) {
|
||||
is DeviceLinkUpdateResult.Success -> {
|
||||
lastDeviceLinkUpdate[updateResult.publicKey] = now
|
||||
deviceLinks.addAll(updateResult.deviceLinks)
|
||||
}
|
||||
is DeviceLinkUpdateResult.Failure -> {
|
||||
if (updateResult.error is SnodeAPI.Error.ParsingFailed) {
|
||||
lastDeviceLinkUpdate[updateResult.publicKey] = now // Don't infinitely update in case of a parsing failure
|
||||
}
|
||||
deviceLinks.addAll(database.getDeviceLinks(updateResult.publicKey)) // Fall back on cached device links in case of a failure
|
||||
}
|
||||
}
|
||||
}
|
||||
// Updatees that didn't show up in the response provided by the file server are assumed to not have any device links
|
||||
val excludedUpdatees = updatees.filter { updatee ->
|
||||
updateResults.find { updateResult ->
|
||||
when (updateResult) {
|
||||
is DeviceLinkUpdateResult.Success -> updateResult.publicKey == updatee
|
||||
is DeviceLinkUpdateResult.Failure -> updateResult.publicKey == updatee
|
||||
}
|
||||
} == null
|
||||
}
|
||||
excludedUpdatees.forEach {
|
||||
lastDeviceLinkUpdate[it] = now
|
||||
}
|
||||
deviceLinks.union(cachedDeviceLinks)
|
||||
}.recover {
|
||||
publicKeys.flatMap { database.getDeviceLinks(it) }.toSet()
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
fun setDeviceLinks(deviceLinks: Set<DeviceLink>): Promise<Unit, Exception> {
|
||||
return Promise.of(Unit)
|
||||
/*
|
||||
val isMaster = deviceLinks.find { it.masterPublicKey == userPublicKey } != null
|
||||
val deviceLinksAsJSON = deviceLinks.map { it.toJSON() }
|
||||
val value = if (deviceLinks.isNotEmpty()) mapOf( "isPrimary" to isMaster, "authorisations" to deviceLinksAsJSON ) else null
|
||||
val annotation = mapOf( "type" to deviceLinkType, "value" to value )
|
||||
val parameters = mapOf( "annotations" to listOf( annotation ) )
|
||||
return retryIfNeeded(maxRetryCount) {
|
||||
execute(HTTPVerb.PATCH, server, "/users/me", parameters = parameters)
|
||||
}.map { Unit }
|
||||
*/
|
||||
}
|
||||
|
||||
fun addDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
|
||||
return Promise.of(Unit)
|
||||
/*
|
||||
Log.d("Loki", "Updating device links.")
|
||||
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
|
||||
val mutableDeviceLinks = deviceLinks.toMutableSet()
|
||||
mutableDeviceLinks.add(deviceLink)
|
||||
setDeviceLinks(mutableDeviceLinks)
|
||||
}.success {
|
||||
database.addDeviceLink(deviceLink)
|
||||
}.map { Unit }
|
||||
*/
|
||||
}
|
||||
|
||||
fun removeDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
|
||||
return Promise.of(Unit)
|
||||
/*
|
||||
Log.d("Loki", "Updating device links.")
|
||||
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
|
||||
val mutableDeviceLinks = deviceLinks.toMutableSet()
|
||||
mutableDeviceLinks.remove(deviceLink)
|
||||
setDeviceLinks(mutableDeviceLinks)
|
||||
}.success {
|
||||
database.removeDeviceLink(deviceLink)
|
||||
}.map { Unit }
|
||||
*/
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Open Group Server Public Key
|
||||
fun getPublicKeyForOpenGroupServer(openGroupServer: String): Promise<String, Exception> {
|
||||
val publicKey = database.getOpenGroupPublicKey(openGroupServer)
|
||||
|
@ -2,7 +2,6 @@ package org.session.libsignal.service.loki.database
|
||||
|
||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
||||
import org.session.libsignal.service.loki.api.Snode
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import java.util.*
|
||||
|
||||
interface LokiAPIDatabaseProtocol {
|
||||
@ -38,11 +37,4 @@ interface LokiAPIDatabaseProtocol {
|
||||
fun getUserX25519KeyPair(): ECKeyPair
|
||||
fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): List<ECKeyPair>
|
||||
fun getLatestClosedGroupEncryptionKeyPair(groupPublicKey: String): ECKeyPair?
|
||||
|
||||
// region Deprecated
|
||||
fun getDeviceLinks(publicKey: String): Set<DeviceLink>
|
||||
fun clearDeviceLinks(publicKey: String)
|
||||
fun addDeviceLink(deviceLink: DeviceLink)
|
||||
fun removeDeviceLink(deviceLink: DeviceLink)
|
||||
// endregion
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.session.libsignal.service.loki.protocol.meta
|
||||
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
|
||||
public class SessionMetaProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) {
|
||||
|
||||
|
@ -1,72 +0,0 @@
|
||||
package org.session.libsignal.service.loki.protocol.shelved.multidevice
|
||||
|
||||
import org.whispersystems.curve25519.Curve25519
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.session.libsignal.utilities.Base64
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||
import java.util.*
|
||||
|
||||
data class DeviceLink(val masterPublicKey: String, val slavePublicKey: String, val requestSignature: ByteArray?, val authorizationSignature: ByteArray?) {
|
||||
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
||||
|
||||
val type: Type
|
||||
get() = when (authorizationSignature) {
|
||||
null -> Type.REQUEST
|
||||
else -> Type.AUTHORIZATION
|
||||
}
|
||||
|
||||
enum class Type(val rawValue: Int) { REQUEST(1), AUTHORIZATION(2) }
|
||||
|
||||
constructor(masterPublicKey: String, slavePublicKey: String) : this(masterPublicKey, slavePublicKey, null, null)
|
||||
|
||||
fun sign(type: Type, privateKey: ByteArray): DeviceLink? {
|
||||
val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey
|
||||
val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() }
|
||||
try {
|
||||
val signature = curve.calculateSignature(privateKey, data)
|
||||
return if (type == Type.REQUEST) copy(requestSignature = signature) else copy(authorizationSignature = signature)
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun verify(): Boolean {
|
||||
if (requestSignature == null && authorizationSignature == null) { return false }
|
||||
val signature = if (type == Type.REQUEST) requestSignature else authorizationSignature
|
||||
val issuer = if (type == Type.REQUEST) slavePublicKey else masterPublicKey
|
||||
val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey
|
||||
return try {
|
||||
val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() }
|
||||
val issuerPublicKey = Hex.fromStringCondensed(issuer.removing05PrefixIfNeeded())
|
||||
curve.verifySignature(issuerPublicKey, data, signature)
|
||||
} catch (e: Exception) {
|
||||
Log.w("LOKI", e.message)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun toJSON(): Map<String, Any> {
|
||||
val result = mutableMapOf( "primaryDevicePubKey" to masterPublicKey, "secondaryDevicePubKey" to slavePublicKey )
|
||||
if (requestSignature != null) { result["requestSignature"] = Base64.encodeBytes(requestSignature) }
|
||||
if (authorizationSignature != null) { result["grantSignature"] = Base64.encodeBytes(authorizationSignature) }
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other is DeviceLink) {
|
||||
return (masterPublicKey == other.masterPublicKey && slavePublicKey == other.slavePublicKey
|
||||
&& Arrays.equals(requestSignature, other.requestSignature) && Arrays.equals(authorizationSignature, other.authorizationSignature))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var hash = masterPublicKey.hashCode() xor slavePublicKey.hashCode()
|
||||
if (requestSignature != null) { hash = hash xor Arrays.hashCode(requestSignature) }
|
||||
if (authorizationSignature != null) { hash = hash xor Arrays.hashCode(authorizationSignature) }
|
||||
return hash
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package org.session.libsignal.service.loki.protocol.shelved.multidevice
|
||||
|
||||
class DeviceLinkingSession {
|
||||
private val listeners = mutableListOf<DeviceLinkingSessionListener>()
|
||||
var isListeningForLinkingRequests: Boolean = false
|
||||
private set
|
||||
|
||||
companion object {
|
||||
val shared = DeviceLinkingSession()
|
||||
}
|
||||
|
||||
fun addListener(listener: DeviceLinkingSessionListener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeListener(listener: DeviceLinkingSessionListener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
fun startListeningForLinkingRequests() {
|
||||
isListeningForLinkingRequests = true
|
||||
}
|
||||
|
||||
fun stopListeningForLinkingRequests() {
|
||||
isListeningForLinkingRequests = false
|
||||
}
|
||||
|
||||
fun processLinkingRequest(deviceLink: DeviceLink) {
|
||||
if (!isListeningForLinkingRequests || !deviceLink.verify()) { return }
|
||||
listeners.forEach { it.requestUserAuthorization(deviceLink) }
|
||||
}
|
||||
|
||||
fun processLinkingAuthorization(deviceLink: DeviceLink) {
|
||||
if (!isListeningForLinkingRequests || !deviceLink.verify()) { return }
|
||||
listeners.forEach { it.onDeviceLinkRequestAuthorized(deviceLink) }
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package org.session.libsignal.service.loki.protocol.shelved.multidevice
|
||||
|
||||
interface DeviceLinkingSessionListener {
|
||||
|
||||
fun requestUserAuthorization(deviceLink: DeviceLink) { }
|
||||
fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { }
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package org.session.libsignal.service.loki.protocol.shelved.multidevice
|
||||
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
|
||||
public class MultiDeviceProtocol(private val apiDatabase: LokiAPIDatabaseProtocol) {
|
||||
|
||||
// region Initialization
|
||||
companion object {
|
||||
|
||||
public lateinit var shared: MultiDeviceProtocol
|
||||
|
||||
public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol) {
|
||||
if (Companion::shared.isInitialized) { return; }
|
||||
shared = MultiDeviceProtocol(apiDatabase)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Utilities
|
||||
public fun getMasterDevice(publicKey: String): String? {
|
||||
return null
|
||||
/*
|
||||
val deviceLinks = apiDatabase.getDeviceLinks(publicKey)
|
||||
return deviceLinks.firstOrNull { it.slavePublicKey == publicKey }?.masterPublicKey
|
||||
*/
|
||||
}
|
||||
|
||||
public fun getSlaveDevices(publicKey: String): Set<String> {
|
||||
return setOf()
|
||||
/*
|
||||
val deviceLinks = apiDatabase.getDeviceLinks(publicKey)
|
||||
if (deviceLinks.isEmpty()) { return setOf() }
|
||||
return deviceLinks.map { it.slavePublicKey }.toSet()
|
||||
*/
|
||||
}
|
||||
|
||||
public fun getAllLinkedDevices(publicKey: String): Set<String> {
|
||||
return setOf( publicKey )
|
||||
/*
|
||||
val deviceLinks = apiDatabase.getDeviceLinks(publicKey)
|
||||
if (deviceLinks.isEmpty()) { return setOf( publicKey ) }
|
||||
return deviceLinks.flatMap { listOf( it.masterPublicKey, it.slavePublicKey ) }.toSet()
|
||||
*/
|
||||
}
|
||||
// endregion
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package org.session.libsignal.service.loki.protocol.shelved.syncmessages
|
||||
|
||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
|
||||
public class SyncMessagesProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) {
|
||||
|
||||
// region Initialization
|
||||
companion object {
|
||||
|
||||
public lateinit var shared: SyncMessagesProtocol
|
||||
|
||||
public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) {
|
||||
if (Companion::shared.isInitialized) { return; }
|
||||
shared = SyncMessagesProtocol(apiDatabase, userPublicKey)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Sending
|
||||
/**
|
||||
* Note: This is called only if based on Signal's logic we'd want to send a sync message.
|
||||
*/
|
||||
public fun shouldSyncMessage(message: SignalServiceDataMessage): Boolean {
|
||||
return false
|
||||
/*
|
||||
if (message.deviceLink.isPresent) { return false }
|
||||
val isOpenGroupMessage = message.group.isPresent && message.group.get().groupType == SignalServiceGroup.GroupType.PUBLIC_CHAT
|
||||
if (isOpenGroupMessage) { return false }
|
||||
val usesMultiDevice = apiDatabase.getDeviceLinks(userPublicKey).isNotEmpty()
|
||||
return usesMultiDevice
|
||||
*/
|
||||
}
|
||||
// endregion
|
||||
}
|
Loading…
Reference in New Issue
Block a user