diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 98bd0c8e7e..f765a02ae1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -7,10 +7,7 @@ import network.loki.messenger.libsession_util.Contacts import network.loki.messenger.libsession_util.ConversationVolatileConfig import network.loki.messenger.libsession_util.UserGroupsConfig import network.loki.messenger.libsession_util.UserProfile -import network.loki.messenger.libsession_util.util.BaseCommunityInfo -import network.loki.messenger.libsession_util.util.Conversation -import network.loki.messenger.libsession_util.util.ExpiryMode -import network.loki.messenger.libsession_util.util.UserPic +import network.loki.messenger.libsession_util.util.* import org.session.libsession.avatars.AvatarHelper import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.BlindedIdMapping @@ -468,6 +465,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF // Notify the user val threadID = getOrCreateThreadIdFor(Address.fromSerialized(groupId)) insertOutgoingInfoMessage(context, groupId, SignalServiceGroup.Type.CREATION, title, members.map { it.serialize() }, admins.map { it.serialize() }, threadID, formationTimestamp) + // Don't create config group here, it's from a config update // Start polling ClosedGroupPollerV2.shared.startPolling(group.sessionId) } @@ -671,14 +669,55 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun createGroup(groupId: String, title: String?, members: List
, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List
, formationTimestamp: Long) { DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp) + } + + override fun createInitialConfigGroup(groupPublicKey: String, name: String, members: Map, formationTimestamp: Long, encryptionKeyPair: ECKeyPair) { val volatiles = configFactory.convoVolatile ?: return - val groupPublicKey = GroupUtil.doubleDecodeGroupId(groupId) + val userGroups = configFactory.userGroups ?: return val groupVolatileConfig = volatiles.getOrConstructLegacyGroup(groupPublicKey) groupVolatileConfig.lastRead = formationTimestamp volatiles.set(groupVolatileConfig) + val groupInfo = GroupInfo.LegacyGroupInfo( + sessionId = groupPublicKey, + name = name, + members = members, + hidden = false, + encPubKey = encryptionKeyPair.publicKey.serialize(), + encSecKey = encryptionKeyPair.privateKey.serialize(), + disappearingTimer = 0L + ) + // shouldn't exist, don't use getOrConstruct + copy + userGroups.set(groupInfo) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) } + override fun updateGroupConfig(groupPublicKey: String) { + val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey) + val groupAddress = fromSerialized(groupID) + // TODO: probably add a check in here for isActive? + // TODO: also check if local user is a member / maybe run delete otherwise? + val existingGroup = getGroup(groupID) + ?: return Log.w("Loki-DBG", "No existing group for ${groupPublicKey.take(4)}...${groupPublicKey.takeLast(4)} when updating group config") + val userGroups = configFactory.userGroups ?: return + val name = existingGroup.title + val admins = existingGroup.admins.map { it.serialize() } + val members = existingGroup.members.map { it.serialize() } + val membersMap = GroupUtil.createConfigMemberMap(admins = admins, members = members) + val latestKeyPair = getLatestClosedGroupEncryptionKeyPair(groupPublicKey) + ?: return Log.w("Loki-DBG", "No latest closed group encryption key pair for ${groupPublicKey.take(4)}...${groupPublicKey.takeLast(4)} when updating group config") + val recipientSettings = getRecipientSettings(groupAddress) ?: return + val threadID = getThreadId(groupAddress) ?: return + val groupInfo = userGroups.getOrConstructLegacyGroupInfo(groupPublicKey).copy( + name = name, + members = membersMap, + encPubKey = latestKeyPair.publicKey.serialize(), + encSecKey = latestKeyPair.privateKey.serialize(), + priority = if (isPinned(threadID)) 1 else 0, + disappearingTimer = recipientSettings.expireMessages.toLong() + ) + userGroups.set(groupInfo) + } + override fun isGroupActive(groupPublicKey: String): Boolean { return DatabaseComponent.get(context).groupDatabase().getGroup(GroupUtil.doubleEncodeGroupID(groupPublicKey)).orNull()?.isActive == true } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ClosedGroupManager.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ClosedGroupManager.kt index a61feaef1b..b11c3ebc91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ClosedGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ClosedGroupManager.kt @@ -1,11 +1,17 @@ package org.thoughtcrime.securesms.groups import android.content.Context +import network.loki.messenger.libsession_util.util.GroupInfo import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 import org.session.libsession.utilities.Address +import org.session.libsession.utilities.GroupRecord +import org.session.libsession.utilities.GroupUtil +import org.session.libsession.utilities.recipients.Recipient +import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.dependencies.ConfigFactory object ClosedGroupManager { @@ -28,4 +34,31 @@ object ClosedGroupManager { } } + fun ConfigFactory.removeLegacyGroup(group: GroupRecord): Boolean { + val groups = userGroups ?: return false + if (!group.isClosedGroup) return false + val groupPublicKey = GroupUtil.doubleEncodeGroupID(group.getId()) + return groups.eraseLegacyGroup(groupPublicKey) + } + + fun ConfigFactory.updateLegacyGroup(groupRecipientSettings: Recipient.RecipientSettings, group: GroupRecord) { + val groups = userGroups ?: return + if (!group.isClosedGroup) return + val storage = MessagingModuleConfiguration.shared.storage + val threadId = storage.getThreadId(group.encodedId) ?: return + val groupPublicKey = GroupUtil.doubleEncodeGroupID(group.getId()) + val latestKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: return + val legacyInfo = groups.getOrConstructLegacyGroupInfo(groupPublicKey) + val latestMemberMap = GroupUtil.createConfigMemberMap(group.members.map(Address::serialize), group.admins.map(Address::serialize)) + val toSet = legacyInfo.copy( + members = latestMemberMap, + name = group.title, + disappearingTimer = groupRecipientSettings.expireMessages.toLong(), + priority = if (storage.isPinned(threadId)) 1 else 0, + encSecKey = latestKeyPair.privateKey.serialize(), + encPubKey = latestKeyPair.publicKey.serialize() + ) + groups.set(toSet) + } + } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt index 62e762316b..5dd2074ba2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt @@ -16,6 +16,7 @@ import androidx.loader.app.LoaderManager import androidx.loader.content.Loader import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R import nl.komponents.kovenant.Promise import nl.komponents.kovenant.task @@ -28,16 +29,28 @@ import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.recipients.Recipient +import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.contacts.SelectContactsActivity +import org.thoughtcrime.securesms.database.Storage +import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.DatabaseComponent +import org.thoughtcrime.securesms.groups.ClosedGroupManager.updateLegacyGroup import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.util.fadeIn import org.thoughtcrime.securesms.util.fadeOut import java.io.IOException +import javax.inject.Inject +@AndroidEntryPoint class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { + + @Inject + lateinit var groupConfigFactory: ConfigFactory + @Inject + lateinit var storage: Storage + private val originalMembers = HashSet() private val zombies = HashSet() private val members = HashSet() @@ -306,6 +319,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { promise.successUi { loaderContainer.fadeOut() isLoading = false + updateGroupConfig() finish() }.failUi { exception -> val message = if (exception is MessageSender.Error) exception.description else "An error occurred" @@ -316,5 +330,13 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { } } - class GroupMembers(val members: List, val zombieMembers: List) { } + private fun updateGroupConfig() { + val latestRecipient = storage.getRecipientSettings(Address.fromSerialized(groupID)) + ?: return Log.w("Loki", "No recipient settings when trying to update group config") + val latestGroup = storage.getGroup(groupID) + ?: return Log.w("Loki", "No group record when trying to update group config") + groupConfigFactory.updateLegacyGroup(latestRecipient, latestGroup) + } + + class GroupMembers(val members: List, val zombieMembers: List) } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java index a3d0e6d252..d4c5acf4ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java @@ -6,6 +6,7 @@ import android.graphics.Bitmap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.DistributionTypes; import org.session.libsession.utilities.GroupUtil; @@ -16,11 +17,14 @@ import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import org.thoughtcrime.securesms.util.BitmapUtil; +import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; import java.util.Objects; import java.util.Set; +import network.loki.messenger.libsession_util.UserGroupsConfig; + public class GroupManager { public static long getOpenGroupThreadID(String id, @NonNull Context context) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 13c74980e0..bbdd48d129 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -625,7 +625,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), OpenGroupManager.delete(v2OpenGroup.server, v2OpenGroup.room, this@HomeActivity) } else { lifecycleScope.launch(Dispatchers.IO) { - threadDb.deleteConversation(threadID) + storage.deleteConversation(threadID) } } // Update the badge count diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index b880c10ad7..4e08a4510a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -257,13 +257,13 @@ class DefaultConversationRepository @Inject constructor( override suspend fun deleteThread(threadId: Long): ResultOf { sessionJobDb.cancelPendingMessageSendJobs(threadId) - threadDb.deleteConversation(threadId) + storage.deleteConversation(threadId) return ResultOf.Success(Unit) } override suspend fun deleteMessageRequest(thread: ThreadRecord): ResultOf { sessionJobDb.cancelPendingMessageSendJobs(thread.threadId) - threadDb.deleteConversation(thread.threadId) + storage.deleteConversation(thread.threadId) return ResultOf.Success(Unit) } @@ -290,7 +290,7 @@ class DefaultConversationRepository @Inject constructor( override fun declineMessageRequest(threadId: Long) { sessionJobDb.cancelPendingMessageSendJobs(threadId) - threadDb.deleteConversation(threadId) + storage.deleteConversation(threadId) } override fun hasReceived(threadId: Long): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt index 0850ba1f75..5a405a8bf7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt @@ -6,11 +6,7 @@ import network.loki.messenger.libsession_util.Contacts import network.loki.messenger.libsession_util.ConversationVolatileConfig import network.loki.messenger.libsession_util.UserGroupsConfig import network.loki.messenger.libsession_util.UserProfile -import network.loki.messenger.libsession_util.util.BaseCommunityInfo -import network.loki.messenger.libsession_util.util.Contact -import network.loki.messenger.libsession_util.util.Conversation -import network.loki.messenger.libsession_util.util.GroupInfo -import network.loki.messenger.libsession_util.util.UserPic +import network.loki.messenger.libsession_util.util.* import nl.komponents.kovenant.Promise import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.jobs.ConfigurationSyncJob @@ -21,6 +17,7 @@ import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.utilities.SessionId import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil +import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.Log @@ -163,7 +160,8 @@ object ConfigurationMessageUtilities { approved = settings.isApproved, approvedMe = settings.hasApprovedMe(), profilePicture = userPic ?: UserPic.DEFAULT, - priority = if (isPinned) 1 else 0 + priority = if (isPinned) 1 else 0, + expiryMode = if (settings.expireMessages == 0) ExpiryMode.NONE else ExpiryMode.AfterRead(settings.expireMessages.toLong()) ) contactConfig.set(contactInfo) } @@ -185,7 +183,7 @@ object ConfigurationMessageUtilities { val contact = when { recipient.isOpenGroupRecipient -> { val openGroup = storage.getOpenGroup(current.threadId) ?: continue - val (base, room, pubKey) = Conversation.Community.parseFullUrl(openGroup.joinURL) ?: continue + val (base, room, pubKey) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: continue convoConfig.getOrConstructCommunity(base, room, pubKey) } recipient.isClosedGroupRecipient -> { @@ -229,7 +227,9 @@ object ConfigurationMessageUtilities { } val allLgc = storage.getAllGroups().filter { it.isClosedGroup }.mapNotNull { group -> - val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString() + val groupAddress = Address.fromSerialized(group.encodedId) + val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupAddress.serialize()).toHexString() + val recipient = storage.getRecipientSettings(groupAddress) ?: return@mapNotNull null val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: return@mapNotNull null val threadId = storage.getThreadId(group.encodedId) val isPinned = threadId?.let { storage.isPinned(threadId) } ?: false @@ -243,7 +243,8 @@ object ConfigurationMessageUtilities { hidden = threadId == null, priority = if (isPinned) 1 else 0, encPubKey = encryptionKeyPair.publicKey.serialize(), - encSecKey = encryptionKeyPair.privateKey.serialize() + encSecKey = encryptionKeyPair.privateKey.serialize(), + disappearingTimer = recipient.expireMessages.toLong() ) } (allOpenGroups + allLgc).forEach { groupInfo -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MockDataGenerator.kt b/app/src/main/java/org/thoughtcrime/securesms/util/MockDataGenerator.kt index d4822ea6ab..06fda29306 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MockDataGenerator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MockDataGenerator.kt @@ -233,6 +233,7 @@ object MockDataGenerator { val encryptionKeyPair = Curve.generateKeyPair() storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, randomGroupPublicKey, System.currentTimeMillis()) storage.setExpirationTimer(groupId, 0) + storage.createInitialConfigGroup(randomGroupPublicKey, groupName, GroupUtil.createConfigMemberMap(members, setOf(adminUserId)), System.currentTimeMillis(), encryptionKeyPair) // Add the group created message if (userSessionId == adminUserId) { diff --git a/libsession-util/src/main/cpp/user_groups.cpp b/libsession-util/src/main/cpp/user_groups.cpp index 87bf697626..d2e416f399 100644 --- a/libsession-util/src/main/cpp/user_groups.cpp +++ b/libsession-util/src/main/cpp/user_groups.cpp @@ -232,4 +232,24 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_allLegacyGroupInfo auto conf = ptrToUserGroups(env, thiz); jobject legacy_stack = iterator_as_java_stack(env, conf->begin_legacy_groups(), conf->end()); return legacy_stack; +} +extern "C" +JNIEXPORT jboolean JNICALL +Java_network_loki_messenger_libsession_1util_UserGroupsConfig_eraseCommunity(JNIEnv *env, + jobject thiz, + jobject base_community_info) { + auto conf = ptrToUserGroups(env, thiz); + auto base_community = util::deserialize_base_community(env, base_community_info); + return conf->erase_community(base_community.base_url(),base_community.room()); +} +extern "C" +JNIEXPORT jboolean JNICALL +Java_network_loki_messenger_libsession_1util_UserGroupsConfig_eraseLegacyGroup(JNIEnv *env, + jobject thiz, + jstring session_id) { + auto conf = ptrToUserGroups(env, thiz); + auto session_id_bytes = env->GetStringUTFChars(session_id, nullptr); + bool return_bool = conf->erase_legacy_group(session_id_bytes); + env->ReleaseStringUTFChars(session_id, session_id_bytes); + return return_bool; } \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index 7aa5546a67..c3f92d1206 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -1,10 +1,6 @@ package network.loki.messenger.libsession_util -import network.loki.messenger.libsession_util.util.ConfigPush -import network.loki.messenger.libsession_util.util.Contact -import network.loki.messenger.libsession_util.util.Conversation -import network.loki.messenger.libsession_util.util.GroupInfo -import network.loki.messenger.libsession_util.util.UserPic +import network.loki.messenger.libsession_util.util.* import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage.Kind @@ -160,6 +156,8 @@ class UserGroupsConfig(pointer: Long): ConfigBase(pointer) { external fun set(legacyGroupInfo: GroupInfo.LegacyGroupInfo) external fun erase(communityInfo: GroupInfo.CommunityGroupInfo) external fun erase(legacyGroupInfo: GroupInfo.LegacyGroupInfo) + external fun eraseCommunity(baseCommunityInfo: BaseCommunityInfo): Boolean + external fun eraseLegacyGroup(sessionId: String): Boolean external fun sizeCommunityInfo(): Int external fun sizeLegacyGroupInfo(): Int external fun size(): Int diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index 0606ebbdd6..2539547977 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -121,6 +121,8 @@ interface StorageProtocol { // Closed Groups fun getGroup(groupID: String): GroupRecord? fun createGroup(groupID: String, title: String?, members: List
, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List
, formationTimestamp: Long) + fun createInitialConfigGroup(groupPublicKey: String, name: String, members: Map, formationTimestamp: Long, encryptionKeyPair: ECKeyPair) + fun updateGroupConfig(groupPublicKey: String) fun isGroupActive(groupPublicKey: String): Boolean fun setActive(groupID: String, value: Boolean) fun getZombieMembers(groupID: String): Set diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt index b6bb5170e1..d3c9effd35 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt @@ -73,6 +73,7 @@ fun MessageSender.create(name: String, members: Collection): Promise): Promise, name: String) { - val context = MessagingModuleConfiguration.shared.context - val storage = MessagingModuleConfiguration.shared.storage - val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey) - val group = storage.getGroup(groupID) ?: run { - Log.d("Loki", "Can't update nonexistent closed group.") - throw Error.NoThread - } - // Update name if needed - if (name != group.title) { setName(groupPublicKey, name) } - // Add members if needed - val addedMembers = members - group.members.map { it.serialize() } - if (!addedMembers.isEmpty()) { addMembers(groupPublicKey, addedMembers) } - // Remove members if needed - val removedMembers = group.members.map { it.serialize() } - members - if (removedMembers.isEmpty()) { removeMembers(groupPublicKey, removedMembers) } -} - fun MessageSender.setName(groupPublicKey: String, newName: String) { val context = MessagingModuleConfiguration.shared.context val storage = MessagingModuleConfiguration.shared.storage diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index e7019709bc..680a583bec 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -437,8 +437,23 @@ private fun MessageReceiver.handleClosedGroupControlMessage(message: ClosedGroup is ClosedGroupControlMessage.Kind.MembersRemoved -> handleClosedGroupMembersRemoved(message) is ClosedGroupControlMessage.Kind.MemberLeft -> handleClosedGroupMemberLeft(message) } + if (message.kind !is ClosedGroupControlMessage.Kind.New) { + // update the config + val closedGroupPublicKey = message.getPublicKey() + val storage = MessagingModuleConfiguration.shared.storage + storage.updateGroupConfig(closedGroupPublicKey) + } } +private fun ClosedGroupControlMessage.getPublicKey(): String = kind!!.let { when (it) { + is ClosedGroupControlMessage.Kind.New -> it.publicKey.toByteArray().toHexString() + is ClosedGroupControlMessage.Kind.EncryptionKeyPair -> it.publicKey?.toByteArray()?.toHexString() ?: groupPublicKey!! + is ClosedGroupControlMessage.Kind.MemberLeft -> groupPublicKey!! + is ClosedGroupControlMessage.Kind.MembersAdded -> groupPublicKey!! + is ClosedGroupControlMessage.Kind.MembersRemoved -> groupPublicKey!! + is ClosedGroupControlMessage.Kind.NameChange -> groupPublicKey!! +}} + private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMessage) { val kind = message.kind!! as? ClosedGroupControlMessage.Kind.New ?: return val recipient = Recipient.from(MessagingModuleConfiguration.shared.context, Address.fromSerialized(message.sender!!), false) @@ -476,6 +491,7 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli storage.addClosedGroupPublicKey(groupPublicKey) // Store the encryption key pair storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey, sentTimestamp) + storage.createInitialConfigGroup(groupPublicKey, name, GroupUtil.createConfigMemberMap(members, admins), formationTimestamp, encryptionKeyPair) // Set expiration timer storage.setExpirationTimer(groupID, expireTimer) // Notify the PN server diff --git a/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt b/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt index 3f1563e3d1..bfab2585de 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt @@ -1,5 +1,6 @@ package org.session.libsession.utilities +import network.loki.messenger.libsession_util.util.GroupInfo import org.session.libsignal.messages.SignalServiceGroup import org.session.libsignal.utilities.Hex import java.io.IOException @@ -102,4 +103,22 @@ object GroupUtil { fun doubleDecodeGroupId(groupID: String): String { return Hex.toStringCondensed(getDecodedGroupIDAsData(getDecodedGroupID(groupID))) } + + fun createConfigMemberMap( + members: Collection, + admins: Collection + ): Map { + // Start with admins + val memberMap = admins.associate { + it to true + }.toMutableMap() + + // Add the remaining members (there may be duplicates, so only add ones that aren't already in there from admins) + for (member in members) { + if (!memberMap.contains(member)) { + memberMap[member] = false + } + } + return memberMap + } } \ No newline at end of file