diff --git a/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPI.kt b/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPI.kt index 0cff14e41c..3c700ef719 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPI.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPI.kt @@ -5,11 +5,10 @@ import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.then -import org.session.libsession.messaging.Configuration +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.utilities.DotNetAPI import org.session.libsession.messaging.fileserver.FileServerAPI -import org.session.libsession.snode.SnodeAPI import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.util.Base64 @@ -56,7 +55,7 @@ object OpenGroupAPI: DotNetAPI() { // region Public API public fun getMessages(channel: Long, server: String): Promise, Exception> { Log.d("Loki", "Getting messages for open group with ID: $channel on server: $server.") - val storage = Configuration.shared.storage + val storage = MessagingConfiguration.shared.storage val parameters = mutableMapOf( "include_annotations" to 1 ) val lastMessageServerID = storage.getLastMessageServerID(channel, server) if (lastMessageServerID != null) { @@ -161,7 +160,7 @@ object OpenGroupAPI: DotNetAPI() { public fun getDeletedMessageServerIDs(channel: Long, server: String): Promise, Exception> { Log.d("Loki", "Getting deleted messages for open group with ID: $channel on server: $server.") - val storage = Configuration.shared.storage + val storage = MessagingConfiguration.shared.storage val parameters = mutableMapOf() val lastDeletionServerID = storage.getLastDeletionServerID(channel, server) if (lastDeletionServerID != null) { @@ -193,7 +192,7 @@ object OpenGroupAPI: DotNetAPI() { public fun sendMessage(message: OpenGroupMessage, channel: Long, server: String): Promise { val deferred = deferred() - val storage = Configuration.shared.storage + val storage = MessagingConfiguration.shared.storage val userKeyPair = storage.getUserKeyPair() ?: throw Error.Generic val userDisplayName = storage.getUserDisplayName() ?: throw Error.Generic Thread { @@ -287,7 +286,7 @@ object OpenGroupAPI: DotNetAPI() { val memberCount = countInfo["subscribers"] as? Int ?: (countInfo["subscribers"] as? Long)?.toInt() ?: (countInfo["subscribers"] as String).toInt() val profilePictureURL = info["avatar"] as String val publicChatInfo = OpenGroupInfo(displayName, profilePictureURL, memberCount) - Configuration.shared.storage.setUserCount(channel, server, memberCount) + MessagingConfiguration.shared.storage.setUserCount(channel, server, memberCount) publicChatInfo } catch (exception: Exception) { Log.d("Loki", "Couldn't parse info for open group with ID: $channel on server: $server.") @@ -298,7 +297,7 @@ object OpenGroupAPI: DotNetAPI() { } public fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: OpenGroupInfo, isForcedUpdate: Boolean) { - val storage = Configuration.shared.storage + val storage = MessagingConfiguration.shared.storage storage.setUserCount(channel, server, info.memberCount) storage.updateTitle(groupID, info.displayName) // Download and update profile picture if needed diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt index 264d8c4cc2..a52f696c3e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt @@ -1,6 +1,6 @@ package org.session.libsession.messaging.sending_receiving -import org.session.libsession.messaging.Configuration +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.sending_receiving.MessageReceiver.Error import org.session.libsession.utilities.AESGCM @@ -21,26 +21,26 @@ import javax.crypto.spec.SecretKeySpec object MessageReceiverDecryption { internal fun decryptWithSignalProtocol(envelope: SignalServiceProtos.Envelope): Pair { - val storage = Configuration.shared.signalStorage - val sskDatabase = Configuration.shared.sskDatabase - val sessionResetImp = Configuration.shared.sessionResetImp - val certificateValidator = Configuration.shared.certificateValidator + val storage = MessagingConfiguration.shared.signalStorage + val sskDatabase = MessagingConfiguration.shared.sskDatabase + val sessionResetImp = MessagingConfiguration.shared.sessionResetImp + val certificateValidator = MessagingConfiguration.shared.certificateValidator val data = envelope.content if (data.count() == 0) { throw Error.NoData } - val userPublicKey = Configuration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey + val userPublicKey = MessagingConfiguration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey val localAddress = SignalServiceAddress(userPublicKey) val cipher = SignalServiceCipher(localAddress, storage, sskDatabase, sessionResetImp, certificateValidator) val result = cipher.decrypt(SignalServiceEnvelope(envelope)) - return Pair(result, result.sender) + return Pair(ByteArray(1), result.sender) // TODO: Return real plaintext } internal fun decryptWithSharedSenderKeys(envelope: SignalServiceProtos.Envelope): Pair { // 1. ) Check preconditions val groupPublicKey = envelope.source - if (!Configuration.shared.storage.isClosedGroup(groupPublicKey)) { throw Error.InvalidGroupPublicKey } + if (!MessagingConfiguration.shared.storage.isClosedGroup(groupPublicKey)) { throw Error.InvalidGroupPublicKey } val data = envelope.content if (data.count() == 0) { throw Error.NoData } - val groupPrivateKey = Configuration.shared.storage.getClosedGroupPrivateKey(groupPublicKey) ?: throw Error.NoGroupPrivateKey + val groupPrivateKey = MessagingConfiguration.shared.storage.getClosedGroupPrivateKey(groupPublicKey) ?: throw Error.NoGroupPrivateKey // 2. ) Parse the wrapper val wrapper = SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.parseFrom(data) val ivAndCiphertext = wrapper.ciphertext.toByteArray() @@ -54,7 +54,7 @@ object MessageReceiverDecryption { // 4. ) Parse the closed group ciphertext message val closedGroupCiphertextMessage = ClosedGroupCiphertextMessage.from(closedGroupCiphertextMessageAsData) ?: throw Error.ParsingFailed val senderPublicKey = closedGroupCiphertextMessage.senderPublicKey.toHexString() - if (senderPublicKey == Configuration.shared.storage.getUserPublicKey()) { throw Error.SelfSend } + if (senderPublicKey == MessagingConfiguration.shared.storage.getUserPublicKey()) { throw Error.SelfSend } // 5. ) Use the info inside the closed group ciphertext message to decrypt the actual message content val plaintext = SharedSenderKeysImplementation.shared.decrypt(closedGroupCiphertextMessage.ivAndCiphertext, groupPublicKey, senderPublicKey, closedGroupCiphertextMessage.keyIndex) // 6. ) Return diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index 54f70d8590..1f767babdb 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -1,6 +1,6 @@ package org.session.libsession.messaging.sending_receiving -import org.session.libsession.messaging.Configuration +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.control.ClosedGroupUpdate @@ -9,9 +9,9 @@ import org.session.libsession.messaging.messages.control.ReadReceipt import org.session.libsession.messaging.messages.control.TypingIndicator import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI -import org.session.libsession.utilities.LKGroupUtilities +import org.session.libsession.messaging.threads.Address +import org.session.libsession.utilities.GroupUtil import org.session.libsignal.libsignal.util.Hex -import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchet @@ -37,7 +37,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content, } private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) { - + // TODO } private fun MessageReceiver.handleTypingIndicator(message: TypingIndicator) { @@ -89,8 +89,8 @@ private fun MessageReceiver.handleClosedGroupUpdate(message: ClosedGroupUpdate) } private fun MessageReceiver.handleNewGroup(message: ClosedGroupUpdate) { - val storage = Configuration.shared.storage - val sskDatabase = Configuration.shared.sskDatabase + val storage = MessagingConfiguration.shared.storage + val sskDatabase = MessagingConfiguration.shared.sskDatabase val kind = message.kind!! as ClosedGroupUpdate.Kind.New val groupPublicKey = kind.groupPublicKey.toHexString() val name = kind.name @@ -122,27 +122,24 @@ private fun MessageReceiver.handleNewGroup(message: ClosedGroupUpdate) { MessageSender.requestSenderKey(groupPublicKey, publicKey) } // Create the group - val groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) - val groupDB = DatabaseFactory.getGroupDatabase(context) - if (groupDB.getGroup(groupID).orNull() != null) { + val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey) + if (storage.getGroup(groupID) != null) { // Update the group - groupDB.updateTitle(groupID, name) - groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) + storage.updateTitle(groupID, name) + storage.updateMembers(groupID, members.map { Address.fromSerialized(it) }) } else { - groupDB.create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), - null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) + storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }), + null, null, LinkedList(admins.map { Address.fromSerialized(it) })) } - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) + storage.setProfileSharing(Address.fromSerialized(groupID), true) // Add the group to the user's set of public keys to poll for sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) // Notify the PN server - PushNotificationAPI.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) + PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) // Notify the user + /* TODO insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, members) - - + */ } private fun MessageReceiver.handleGroupUpdate(message: ClosedGroupUpdate) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index 1d18f3e9df..21f7893c2f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -1,10 +1,9 @@ package org.session.libsession.messaging.sending_receiving -import com.google.protobuf.MessageOrBuilder import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred -import org.session.libsession.messaging.Configuration +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.Message @@ -64,7 +63,7 @@ object MessageSender { fun sendToSnodeDestination(destination: Destination, message: Message): Promise { val deferred = deferred() val promise = deferred.promise - val storage = Configuration.shared.storage + val storage = MessagingConfiguration.shared.storage val preconditionFailure = Exception("Destination should not be open groups!") var snodeMessage: SnodeMessage? = null message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } /* Visible messages will already have their sent timestamp set */ @@ -152,7 +151,7 @@ object MessageSender { fun sendToOpenGroupDestination(destination: Destination, message: Message): Promise { val deferred = deferred() val promise = deferred.promise - val storage = Configuration.shared.storage + val storage = MessagingConfiguration.shared.storage val preconditionFailure = Exception("Destination should not be contacts or closed groups!") message.sentTimestamp = System.currentTimeMillis() message.sender = storage.getUserPublicKey() diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 3aa4cbb0fd..8604c3d49c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -2,16 +2,17 @@ package org.session.libsession.messaging.sending_receiving -import android.content.Context import android.util.Log import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred -import org.session.libsession.messaging.Configuration -import org.session.libsession.messaging.messages.Destination +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.messages.control.ClosedGroupUpdate import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI -import org.session.libsession.utilities.LKGroupUtilities +import org.session.libsession.messaging.sending_receiving.MessageSender.Error +import org.session.libsession.messaging.threads.Address +import org.session.libsession.utilities.GroupUtil + import org.session.libsignal.libsignal.ecc.Curve import org.session.libsignal.libsignal.util.Hex @@ -26,8 +27,9 @@ import java.util.* fun MessageSender.createClosedGroup(name: String, members: Collection): Promise { val deferred = deferred() // Prepare + val storage = MessagingConfiguration.shared.storage val members = members - val userPublicKey = Configuration.shared.storage.getUserPublicKey()!! + val userPublicKey = storage.getUserPublicKey()!! // Generate a key pair for the group val groupKeyPair = Curve.generateKeyPair() val groupPublicKey = groupKeyPair.hexEncodedPublicKey // Includes the "05" prefix @@ -41,12 +43,9 @@ fun MessageSender.createClosedGroup(name: String, members: Collection): // Create the group val admins = setOf( userPublicKey ) val adminsAsData = admins.map { Hex.fromStringCondensed(it) } - val groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) - /* TODO: - DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), - null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) - */ + val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey) + storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }), null, null, LinkedList(admins.map { Address.fromSerialized(it) })) + storage.setProfileSharing(Address.fromSerialized(groupID), true) // Send a closed group update message to all members using established channels val promises = mutableListOf>() for (member in members) { @@ -55,16 +54,17 @@ fun MessageSender.createClosedGroup(name: String, members: Collection): senderKeys, membersAsData, adminsAsData) val closedGroupUpdate = ClosedGroupUpdate() closedGroupUpdate.kind = closedGroupUpdateKind - val promise = MessageSender.sendNonDurably(closedGroupUpdate, threadID) + val address = Address.fromSerialized(member) + val promise = MessageSender.sendNonDurably(closedGroupUpdate, address) promises.add(promise) } // Add the group to the user's set of public keys to poll for - Configuration.shared.sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey) + MessagingConfiguration.shared.sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey) // Notify the PN server PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) // Notify the user + val threadID =storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) /* TODO - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID) */ // Fulfill the promise @@ -75,46 +75,47 @@ fun MessageSender.createClosedGroup(name: String, members: Collection): fun MessageSender.update(groupPublicKey: String, members: Collection, name: String): Promise { val deferred = deferred() - val userPublicKey = Configuration.shared.storage.getUserPublicKey()!! - val sskDatabase = Configuration.shared.sskDatabase - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() + val storage = MessagingConfiguration.shared.storage + val userPublicKey = storage.getUserPublicKey()!! + val sskDatabase = MessagingConfiguration.shared.sskDatabase + val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey) + val group = storage.getGroup(groupID) if (group == null) { Log.d("Loki", "Can't update nonexistent closed group.") - return deferred.reject(Error.NoThread) + deferred.reject(Error.NoThread) + return deferred.promise } val oldMembers = group.members.map { it.serialize() }.toSet() val newMembers = members.minus(oldMembers) val membersAsData = members.map { Hex.fromStringCondensed(it) } val admins = group.admins.map { it.serialize() } val adminsAsData = admins.map { Hex.fromStringCondensed(it) } - val groupPrivateKey = DatabaseFactory.getSSKDatabase(context).getClosedGroupPrivateKey(groupPublicKey) + val groupPrivateKey = sskDatabase.getClosedGroupPrivateKey(groupPublicKey) if (groupPrivateKey == null) { Log.d("Loki", "Couldn't get private key for closed group.") - return@Thread deferred.reject(Error.NoPrivateKey) + deferred.reject(Error.NoPrivateKey) + return deferred.promise } val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() val removedMembers = oldMembers.minus(members) val isUserLeaving = removedMembers.contains(userPublicKey) - var newSenderKeys = listOf() + val newSenderKeys: List if (wasAnyUserRemoved) { if (isUserLeaving && removedMembers.count() != 1) { Log.d("Loki", "Can't remove self and others simultaneously.") - return@Thread deferred.reject(Error.InvalidUpdate) + deferred.reject(Error.InvalidClosedGroupUpdate) + return deferred.promise } - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, members) // Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually) - for (member in oldMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), + val promises = oldMembers.map { member -> + val closedGroupUpdateKind = ClosedGroupUpdate.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, setOf(), membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - job.setContext(context) - job.onRun() // Run the job immediately + val closedGroupUpdate = ClosedGroupUpdate() + closedGroupUpdate.kind = closedGroupUpdateKind + val address = Address.fromSerialized(member) + MessageSender.sendNonDurably(closedGroupUpdate, address).get() } + val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) for (pair in allOldRatchets) { val senderPublicKey = pair.first @@ -128,30 +129,30 @@ fun MessageSender.update(groupPublicKey: String, members: Collection, na // send it out to all members (minus the removed ones) using established channels. if (isUserLeaving) { sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) - groupDB.setActive(groupID, false) - groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey)) + storage.setActive(groupID, false) + storage.removeMember(groupID, Address.fromSerialized(userPublicKey)) // Notify the PN server - LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) + PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) } else { // Send closed group update messages to any new members using established channels for (member in newMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, + val closedGroupUpdateKind = ClosedGroupUpdate.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, Hex.fromStringCondensed(groupPrivateKey), listOf(), membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) + val closedGroupUpdate = ClosedGroupUpdate() + closedGroupUpdate.kind = closedGroupUpdateKind + val address = Address.fromSerialized(member) + MessageSender.sendNonDurably(closedGroupUpdate, address) } // Send out the user's new ratchet to all members (minus the removed ones) using established channels val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) for (member in members) { if (member == userPublicKey) { continue } - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) + val closedGroupUpdateKind = ClosedGroupUpdate.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) + val closedGroupUpdate = ClosedGroupUpdate() + closedGroupUpdate.kind = closedGroupUpdateKind + val address = Address.fromSerialized(member) + MessageSender.sendNonDurably(closedGroupUpdate, address) } } } else if (newMembers.isNotEmpty()) { @@ -161,49 +162,68 @@ fun MessageSender.update(groupPublicKey: String, members: Collection, na ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) } // Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group) - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, + val closedGroupUpdateKind = ClosedGroupUpdate.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, newSenderKeys, membersAsData, adminsAsData) - val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, newMembers) + val closedGroupUpdate = ClosedGroupUpdate() + closedGroupUpdate.kind = closedGroupUpdateKind + val address = Address.fromSerialized(groupID) + MessageSender.send(closedGroupUpdate, address) // Send closed group update messages to the new members using established channels var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) allSenderKeys = allSenderKeys.union(newSenderKeys) for (member in newMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, + val closedGroupUpdateKind = ClosedGroupUpdate.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, Hex.fromStringCondensed(groupPrivateKey), allSenderKeys, membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) + val closedGroupUpdate = ClosedGroupUpdate() + closedGroupUpdate.kind = closedGroupUpdateKind + val address = Address.fromSerialized(member) + MessageSender.send(closedGroupUpdate, address) } } else { val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, + val closedGroupUpdateKind = ClosedGroupUpdate.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, allSenderKeys, membersAsData, adminsAsData) - val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) + val closedGroupUpdate = ClosedGroupUpdate() + closedGroupUpdate.kind = closedGroupUpdateKind + val address = Address.fromSerialized(groupID) + MessageSender.send(closedGroupUpdate, address) } // Update the group - groupDB.updateTitle(groupID, name) + storage.updateTitle(groupID, name) if (!isUserLeaving) { // The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead - groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) + storage.updateMembers(groupID, members.map { Address.fromSerialized(it) }) } // Notify the user val infoType = if (isUserLeaving) SignalServiceProtos.GroupContext.Type.QUIT else SignalServiceProtos.GroupContext.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) + /* TODO insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID) + */ deferred.resolve(Unit) return deferred.promise } +fun MessageSender.leave(groupPublicKey: String) { + val storage = MessagingConfiguration.shared.storage + val userPublicKey = storage.getUserPublicKey()!! + val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey) + val group = storage.getGroup(groupID) + if (group == null) { + Log.d("Loki", "Can't leave nonexistent closed group.") + return + } + val name = group.title + val oldMembers = group.members.map { it.serialize() }.toSet() + val newMembers = oldMembers.minus(userPublicKey) + return update(groupPublicKey, newMembers, name).get() +} + fun MessageSender.requestSenderKey(groupPublicKey: String, senderPublicKey: String) { Log.d("Loki", "Requesting sender key for group public key: $groupPublicKey, sender public key: $senderPublicKey.") - // Send the request + val address = Address.fromSerialized(senderPublicKey) val closedGroupUpdateKind = ClosedGroupUpdate.Kind.SenderKeyRequest(Hex.fromStringCondensed(groupPublicKey)) val closedGroupUpdate = ClosedGroupUpdate() closedGroupUpdate.kind = closedGroupUpdateKind - MessageSender.send(closedGroupUpdate, Destination.ClosedGroup(groupPublicKey)) + MessageSender.send(closedGroupUpdate, address) } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderConvenience.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderConvenience.kt index de5b6fa89e..4efecc8d15 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderConvenience.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderConvenience.kt @@ -1,35 +1,39 @@ package org.session.libsession.messaging.sending_receiving import nl.komponents.kovenant.Promise +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.MessageSendJob import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.visible.VisibleMessage +import org.session.libsession.messaging.threads.Address import org.session.libsignal.service.api.messages.SignalServiceAttachment -fun MessageSender.send(message: VisibleMessage, attachments: List, threadID: String) { +fun MessageSender.send(message: VisibleMessage, attachments: List, address: Address) { prep(attachments, message) - send(message, threadID) + send(message, address) } -fun MessageSender.send(message: Message, threadID: String) { +fun MessageSender.send(message: Message, address: Address) { + val threadID = MessagingConfiguration.shared.storage.getOrCreateThreadIdFor(address) message.threadID = threadID - val destination = Destination.from(threadID) + val destination = Destination.from(address) val job = MessageSendJob(message, destination) JobQueue.shared.add(job) } -fun MessageSender.sendNonDurably(message: VisibleMessage, attachments: List, threadID: String): Promise { +fun MessageSender.sendNonDurably(message: VisibleMessage, attachments: List, address: Address): Promise { prep(attachments, message) // TODO: Deal with attachments - return sendNonDurably(message, threadID) + return sendNonDurably(message, address) } -fun MessageSender.sendNonDurably(message: Message, threadID: String): Promise { +fun MessageSender.sendNonDurably(message: Message, address: Address): Promise { + val threadID = MessagingConfiguration.shared.storage.getOrCreateThreadIdFor(address) message.threadID = threadID - val destination = Destination.from(threadID) + val destination = Destination.from(address) return MessageSender.send(message, destination) } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt index f1c9dd2c88..393f24de12 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt @@ -1,7 +1,7 @@ package org.session.libsession.messaging.sending_receiving import com.google.protobuf.ByteString -import org.session.libsession.messaging.Configuration +import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.sending_receiving.MessageSender.Error import org.session.libsession.messaging.utilities.UnidentifiedAccessUtil @@ -21,11 +21,11 @@ import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded object MessageSenderEncryption { internal fun encryptWithSignalProtocol(plaintext: ByteArray, message: Message, recipientPublicKey: String): ByteArray{ - val storage = Configuration.shared.signalStorage - val sskDatabase = Configuration.shared.sskDatabase - val sessionResetImp = Configuration.shared.sessionResetImp + val storage = MessagingConfiguration.shared.signalStorage + val sskDatabase = MessagingConfiguration.shared.sskDatabase + val sessionResetImp = MessagingConfiguration.shared.sessionResetImp val localAddress = SignalServiceAddress(recipientPublicKey) - val certificateValidator = Configuration.shared.certificateValidator + val certificateValidator = MessagingConfiguration.shared.certificateValidator val cipher = SignalServiceCipher(localAddress, storage, sskDatabase, sessionResetImp, certificateValidator) val signalProtocolAddress = SignalProtocolAddress(recipientPublicKey, 1) val unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(recipientPublicKey) @@ -36,7 +36,7 @@ object MessageSenderEncryption { internal fun encryptWithSharedSenderKeys(plaintext: ByteArray, groupPublicKey: String): ByteArray { // 1. ) Encrypt the data with the user's sender key - val userPublicKey = Configuration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey + val userPublicKey = MessagingConfiguration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey val ciphertextAndKeyIndex = SharedSenderKeysImplementation.shared.encrypt(plaintext, groupPublicKey, userPublicKey) val ivAndCiphertext = ciphertextAndKeyIndex.first val keyIndex = ciphertextAndKeyIndex.second diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt index 457b83ad88..dd1d73e04e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt @@ -1,16 +1,16 @@ package org.session.libsession.messaging.sending_receiving.notifications -import android.content.Context import nl.komponents.kovenant.functional.map import okhttp3.* -import org.session.libsession.messaging.Configuration +import org.session.libsession.messaging.MessagingConfiguration +import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.util.JsonUtil import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI import org.session.libsignal.service.loki.utilities.retryIfNeeded -import java.io.IOException object PushNotificationAPI { + val context = MessagingConfiguration.shared.context val server = "https://live.apns.getsession.org" val serverPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049" private val maxRetryCount = 4 @@ -46,8 +46,8 @@ object PushNotificationAPI { } } // Unsubscribe from all closed groups - val allClosedGroupPublicKeys = Configuration.shared.sskDatabase.getAllClosedGroupPublicKeys() - val userPublicKey = Configuration.shared.storage.getUserPublicKey()!! + val allClosedGroupPublicKeys = MessagingConfiguration.shared.sskDatabase.getAllClosedGroupPublicKeys() + val userPublicKey = MessagingConfiguration.shared.storage.getUserPublicKey()!! allClosedGroupPublicKeys.forEach { closedGroup -> performOperation(ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey) } @@ -76,7 +76,7 @@ object PushNotificationAPI { } } // Subscribe to all closed groups - val allClosedGroupPublicKeys = Configuration.shared.sskDatabase.getAllClosedGroupPublicKeys() + val allClosedGroupPublicKeys = MessagingConfiguration.shared.sskDatabase.getAllClosedGroupPublicKeys() allClosedGroupPublicKeys.forEach { closedGroup -> performOperation(ClosedGroupOperation.Subscribe, closedGroup, publicKey) }