diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 4b0e87fb8a..6a65f6a6e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -34,10 +34,9 @@ import androidx.lifecycle.ProcessLifecycleOwner; import org.conscrypt.Conscrypt; import org.session.libsession.avatars.AvatarHelper; import org.session.libsession.database.MessageDataProvider; -import org.session.libsession.database.StorageProtocol; import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier; -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2; +import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2; import org.session.libsession.messaging.sending_receiving.pollers.Poller; import org.session.libsession.snode.SnodeModule; import org.session.libsession.utilities.Address; @@ -283,7 +282,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO if (poller != null) { poller.stopIfNeeded(); } - ClosedGroupPollerV2.getShared().stopAll(); + LegacyClosedGroupPollerV2.getShared().stopAll(); } @Override @@ -446,7 +445,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO if (poller != null) { poller.startIfNeeded(); } - ClosedGroupPollerV2.getShared().start(); + LegacyClosedGroupPollerV2.getShared().start(); } private void resubmitProfilePictureIfNeeded() { 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 f048d2a96e..0ef7ade23b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -56,7 +56,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview import org.session.libsession.messaging.sending_receiving.notifications.PushRegistryV1 -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 +import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2 import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.messaging.utilities.UpdateMessageData @@ -124,8 +124,11 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co } address.isClosedGroup -> { val sessionId = address.serialize() - val closedGroup = groups.getClosedGroup(sessionId) - TODO("Set the closed group's convo volatile info") + groups.getClosedGroup(sessionId) ?: return Log.d("Closed group doesn't exist locally", NullPointerException()) + val conversation = Conversation.ClosedGroup( + sessionId, 0, false + ) + volatile.set(conversation) } address.isOpenGroup -> { // these should be added on the group join / group info fetch @@ -465,7 +468,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co is ConversationVolatileConfig -> updateConvoVolatile(forConfigObject) is UserGroupsConfig -> updateUserGroups(forConfigObject) is GroupInfoConfig -> updateGroupInfo(forConfigObject) - is GroupKeysConfig -> updateGroupKeys(forConfigObject) + // is GroupKeysConfig -> updateGroupKeys(forConfigObject) is GroupMembersConfig -> updateGroupMembers(forConfigObject) } } @@ -545,7 +548,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co is Conversation.OneToOne -> getThreadIdFor(conversation.sessionId, null, null, createThread = false) is Conversation.LegacyGroup -> getThreadIdFor("", conversation.groupId,null, createThread = false) is Conversation.Community -> getThreadIdFor("",null, "${conversation.baseCommunityInfo.baseUrl.removeSuffix("/")}.${conversation.baseCommunityInfo.room}", createThread = false) - is Conversation.ClosedGroup -> getThreadIdFor(conversation.sessionId, null, null, createThread = false) + is Conversation.ClosedGroup -> getThreadIdFor(conversation.sessionId, null, null, createThread = false) // New groups will be managed bia libsession } if (threadId != null) { if (conversation.lastRead > getLastSeen(threadId)) { @@ -607,6 +610,15 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co } } + val newClosedGroups = userGroups.allClosedGroupInfo() + for (closedGroup in newClosedGroups) { + val recipient = Recipient.from(context, Address.fromSerialized(closedGroup.groupSessionId.hexString()), false) + setRecipientApprovedMe(recipient, true) + setRecipientApproved(recipient, true) + val threadId = getOrCreateThreadIdFor(recipient.address) + setPinned(threadId, closedGroup.priority == PRIORITY_PINNED) + } + for (group in lgc) { val existingGroup = existingClosedGroups.firstOrNull { GroupUtil.doubleDecodeGroupId(it.encodedId) == group.sessionId.hexString() } val existingThread = existingGroup?.let { getThreadId(existingGroup.encodedId) } @@ -643,7 +655,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co 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.hexString()) + LegacyClosedGroupPollerV2.shared.startPolling(group.sessionId.hexString()) } } } @@ -883,8 +895,9 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp) } - override fun createNewGroup(groupName: String, groupDescription: String, members: Set): Optional { + override fun createNewGroup(groupName: String, groupDescription: String, members: Set): Optional { val userGroups = configFactory.userGroups ?: return Optional.absent() + val convoVolatile = configFactory.convoVolatile ?: return Optional.absent() val ourSessionId = getUserPublicKey() ?: return Optional.absent() val userKp = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return Optional.absent() @@ -916,7 +929,9 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val newGroupRecipient = group.groupSessionId.hexString() val configTtl = 1 * 24 * 60 * 60 * 1000L // TODO: just testing here, 1 day so we don't fill large space on network // Test the sending - val keyPush = groupKeys.pendingPush() ?: return Optional.absent() + val keyPush = groupKeys.pendingConfig() ?: return Optional.absent() + val pendingKey = groupKeys.pendingKey() ?: return Optional.absent() + val keysSnodeMessage = SnodeMessage( newGroupRecipient, Base64.encodeBytes(keyPush), @@ -932,7 +947,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val (infoPush, infoSeqNo) = groupInfo.push() val infoSnodeMessage = SnodeMessage( newGroupRecipient, - Base64.encodeBytes(keyPush), + Base64.encodeBytes(infoPush), configTtl, groupCreationTimestamp ) @@ -968,13 +983,28 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val responseList = (response["results"] as List) val keyResponse = responseList[0] + val keyHash = (keyResponse["body"] as Map)["hash"] as String + val keyTimestamp = (keyResponse["body"] as Map)["t"] as Long val infoResponse = responseList[1] + val infoHash = (infoResponse["body"] as Map)["hash"] as String val memberResponse = responseList[2] + val memberHash = (memberResponse["body"] as Map)["hash"] as String // TODO: check response success + groupKeys.loadKey(keyPush, keyHash, keyTimestamp, groupInfo, groupMembers) + groupInfo.confirmPushed(infoSeqNo, infoHash) + groupMembers.confirmPushed(memberSeqNo, memberHash) + configFactory.saveGroupConfigs(groupKeys, groupInfo, groupMembers) // now check poller to be all + convoVolatile.set(Conversation.ClosedGroup(newGroupRecipient, groupCreationTimestamp, false)) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) Log.d("Group Config", "Saved group config for $newGroupRecipient") - return Optional.of(true) + groupKeys.free() + groupInfo.free() + groupMembers.free() + val groupRecipient = Recipient.from(context, Address.fromSerialized(newGroupRecipient), false) + setRecipientApprovedMe(groupRecipient, true) + setRecipientApproved(groupRecipient, true) + return Optional.of(groupRecipient) } catch (e: Exception) { Log.e("Group Config", e) Log.e("Group Config", "Deleting group from our group") @@ -1648,7 +1678,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co } override fun getRecipientApproved(address: Address): Boolean { - return DatabaseComponent.get(context).recipientDatabase().getApproved(address) + return address.isClosedGroup || DatabaseComponent.get(context).recipientDatabase().getApproved(address) } override fun setRecipientApproved(recipient: Recipient, approved: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index d358e323dd..7a28abe604 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -503,7 +503,8 @@ public class ThreadDatabase extends Database { } public Cursor getApprovedConversationList() { - String where = "((" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+ LEGACY_CLOSED_GROUP_PREFIX +"%') OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " + + String where = "((" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+ LEGACY_CLOSED_GROUP_PREFIX +"%' OR "+RecipientDatabase.TABLE_NAME+"."+RecipientDatabase.ADDRESS+" LIKE '"+ IdPrefix.GROUP.getValue() +"%') " + + "OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " + "AND " + ARCHIVED + " = 0 "; return getConversationList(where); } @@ -830,9 +831,7 @@ public class ThreadDatabase extends Database { } private boolean deleteThreadOnEmpty(long threadId) { - return false; // TODO: test the deletion / clearing logic here to make sure this is the desired functionality -// Recipient threadRecipient = getRecipientForThreadId(threadId); -// return threadRecipient != null && !threadRecipient.isOpenGroupRecipient(); + return false; } private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt index d5db05df69..46e13c4548 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt @@ -350,6 +350,6 @@ class ConfigFactory( ) { val pubKey = groupInfo.id().hexString() val timestamp = SnodeAPI.nowWithOffset - configDatabase.storeGroupConfigs(pubKey, groupKeys.dump(), groupInfo.dump(), groupMembers.dump(), timestamp) + configDatabase.storeGroupConfigs(pubKey, groupKeys.keyDump(), groupInfo.dump(), groupMembers.dump(), timestamp) } } \ No newline at end of file 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 d89c0b04bc..e50461c9e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ClosedGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ClosedGroupManager.kt @@ -4,7 +4,7 @@ import android.content.Context import network.loki.messenger.libsession_util.ConfigBase import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.sending_receiving.notifications.PushRegistryV1 -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 +import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupRecord import org.session.libsession.utilities.GroupUtil @@ -26,7 +26,7 @@ object ClosedGroupManager { // Notify the PN server PushRegistryV1.unsubscribeGroup(closedGroupPublicKey = groupPublicKey, publicKey = userPublicKey) // Stop polling - ClosedGroupPollerV2.shared.stopPolling(groupPublicKey) + LegacyClosedGroupPollerV2.shared.stopPolling(groupPublicKey) storage.cancelPendingMessageSendJobs(threadId) ApplicationContext.getInstance(context).messageNotifier.updateNotification(context) if (delete) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt index 1562054ff4..303b1a6124 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt @@ -77,9 +77,8 @@ class CreateGroupFragment : Fragment() { } } - private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) { + private fun openConversationActivity(context: Context, recipient: Recipient) { val intent = Intent(context, ConversationActivityV2::class.java) - intent.putExtra(ConversationActivityV2.THREAD_ID, threadId) intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address) context.startActivity(intent) } @@ -94,7 +93,10 @@ class CreateGroupFragment : Fragment() { createGroupState, onCreate = { newGroup -> // launch something to create here - viewModel.tryCreateGroup(newGroup) + val groupRecipient = viewModel.tryCreateGroup(newGroup) + groupRecipient?.let { recipient -> + openConversationActivity(requireContext(), recipient) + } }, onClose = { delegate.onDialogClosePressed() diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt index 6cc236d44f..a4e680b9d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt @@ -40,7 +40,7 @@ class CreateGroupViewModel @Inject constructor( } } - fun tryCreateGroup(createGroupState: CreateGroupState) { + fun tryCreateGroup(createGroupState: CreateGroupState): Recipient? { _viewState.postValue(CreateGroupFragment.ViewState(true, null, null)) val name = createGroupState.groupName @@ -49,14 +49,16 @@ class CreateGroupViewModel @Inject constructor( // do some validations if (name.isEmpty()) { - return _viewState.postValue( + _viewState.postValue( CreateGroupFragment.ViewState(false, R.string.error, null) ) + return null } // TODO: add future validation for empty group ? we'll add ourselves anyway ig // make a group - storage.createNewGroup(name, description, members) + val newGroup = storage.createNewGroup(name, description, members) // TODO: handle optional + return newGroup.orNull() } fun filter(query: String): List { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/BackgroundPollWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/BackgroundPollWorker.kt index e583fb0ca5..b9adac4f40 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/BackgroundPollWorker.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/BackgroundPollWorker.kt @@ -7,8 +7,8 @@ import androidx.work.Constraints import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType -import androidx.work.PeriodicWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.Worker import androidx.work.WorkerParameters @@ -18,7 +18,7 @@ import nl.komponents.kovenant.functional.bind import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.jobs.BatchMessageReceiveJob import org.session.libsession.messaging.jobs.MessageReceiveParameters -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 +import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2 import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.TextSecurePreferences @@ -121,7 +121,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor // Closed groups if (requestTargets.contains(Targets.CLOSED_GROUPS)) { - val closedGroupPoller = ClosedGroupPollerV2() // Intentionally don't use shared + val closedGroupPoller = LegacyClosedGroupPollerV2() // Intentionally don't use shared val storage = MessagingModuleConfiguration.shared.storage val allGroupPublicKeys = storage.getAllClosedGroupPublicKeys() allGroupPublicKeys.iterator().forEach { closedGroupPoller.poll(it) } diff --git a/libsession-util/src/main/cpp/contacts.h b/libsession-util/src/main/cpp/contacts.h index c5496a68c8..ef074263fd 100644 --- a/libsession-util/src/main/cpp/contacts.h +++ b/libsession-util/src/main/cpp/contacts.h @@ -13,7 +13,7 @@ inline session::config::Contacts *ptrToContacts(JNIEnv *env, jobject obj) { inline jobject serialize_contact(JNIEnv *env, session::config::contact_info info) { jclass contactClass = env->FindClass("network/loki/messenger/libsession_util/util/Contact"); - jmethodID constructor = env->GetMethodID(contactClass, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZLnetwork/loki/messenger/libsession_util/util/UserPic;ILnetwork/loki/messenger/libsession_util/util/ExpiryMode;)V"); + jmethodID constructor = env->GetMethodID(contactClass, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZLnetwork/loki/messenger/libsession_util/util/UserPic;JLnetwork/loki/messenger/libsession_util/util/ExpiryMode;)V"); jstring id = env->NewStringUTF(info.session_id.data()); jstring name = env->NewStringUTF(info.name.data()); jstring nickname = env->NewStringUTF(info.nickname.data()); @@ -24,7 +24,7 @@ inline jobject serialize_contact(JNIEnv *env, session::config::contact_info info auto created = info.created; jobject profilePic = util::serialize_user_pic(env, info.profile_picture); jobject returnObj = env->NewObject(contactClass, constructor, id, name, nickname, approved, - approvedMe, blocked, profilePic, info.priority, + approvedMe, blocked, profilePic, (jlong)info.priority, util::serialize_expiry(env, info.exp_mode, info.exp_timer)); return returnObj; } @@ -42,14 +42,14 @@ deserialize_contact(JNIEnv *env, jobject info, session::config::Contacts *conf) getBlocked = env->GetFieldID(contactClass, "blocked", "Z"); getUserPic = env->GetFieldID(contactClass, "profilePicture", "Lnetwork/loki/messenger/libsession_util/util/UserPic;"); - getPriority = env->GetFieldID(contactClass, "priority", "I"); + getPriority = env->GetFieldID(contactClass, "priority", "J"); getExpiry = env->GetFieldID(contactClass, "expiryMode", "Lnetwork/loki/messenger/libsession_util/util/ExpiryMode;"); jstring name, nickname, session_id; session_id = static_cast(env->GetObjectField(info, getId)); name = static_cast(env->GetObjectField(info, getName)); nickname = static_cast(env->GetObjectField(info, getNick)); bool approved, approvedMe, blocked, hidden; - int priority = env->GetIntField(info, getPriority); + int priority = env->GetLongField(info, getPriority); approved = env->GetBooleanField(info, getApproved); approvedMe = env->GetBooleanField(info, getApprovedMe); blocked = env->GetBooleanField(info, getBlocked); diff --git a/libsession-util/src/main/cpp/group_keys.cpp b/libsession-util/src/main/cpp/group_keys.cpp index 43dc98e304..cc94c5c901 100644 --- a/libsession-util/src/main/cpp/group_keys.cpp +++ b/libsession-util/src/main/cpp/group_keys.cpp @@ -72,20 +72,19 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_groupKeys(JNIEnv *e extern "C" JNIEXPORT void JNICALL Java_network_loki_messenger_libsession_1util_GroupKeysConfig_loadKey(JNIEnv *env, jobject thiz, + jbyteArray message, jstring hash, - jbyteArray data, - jbyteArray msg_id, jlong timestamp_ms, jobject info_jobject, jobject members_jobject) { std::lock_guard lock{util::util_mutex_}; auto keys = ptrToKeys(env, thiz); + auto message_bytes = util::ustring_from_bytes(env, message); auto hash_bytes = env->GetStringUTFChars(hash, nullptr); - auto data_bytes = util::ustring_from_bytes(env, data); - auto msg_bytes = util::ustring_from_bytes(env, msg_id); auto info = ptrToInfo(env, info_jobject); auto members = ptrToMembers(env, members_jobject); - keys->load_key_message(hash_bytes, data_bytes, timestamp_ms, *info, *members); + keys->load_key_message(hash_bytes, message_bytes, timestamp_ms, *info, *members); + env->ReleaseStringUTFChars(hash, hash_bytes); } @@ -112,8 +111,8 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_pendingKey(JNIEnv * extern "C" JNIEXPORT jbyteArray JNICALL -Java_network_loki_messenger_libsession_1util_GroupKeysConfig_pendingPush(JNIEnv *env, - jobject thiz) { +Java_network_loki_messenger_libsession_1util_GroupKeysConfig_pendingConfig(JNIEnv *env, + jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto keys = ptrToKeys(env, thiz); auto pending = keys->pending_config(); @@ -135,4 +134,20 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_rekey(JNIEnv *env, auto rekey = keys->rekey(*info, *members); auto rekey_bytes = util::bytes_from_ustring(env, rekey.data()); return rekey_bytes; -} \ No newline at end of file +} + +extern "C" +JNIEXPORT jbyteArray JNICALL +Java_network_loki_messenger_libsession_1util_GroupKeysConfig_keyDump(JNIEnv *env, jobject thiz) { + auto keys = ptrToKeys(env, thiz); + auto dump = keys->dump(); + auto byte_array = util::bytes_from_ustring(env, dump); + return byte_array; +} + +extern "C" +JNIEXPORT void JNICALL +Java_network_loki_messenger_libsession_1util_GroupKeysConfig_free(JNIEnv *env, jobject thiz) { + auto ptr = ptrToKeys(env, thiz); + delete ptr; +} 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 a38dc97162..c74f954215 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 @@ -28,7 +28,6 @@ sealed class ConfigBase(protected val /* yucky */ pointer: Long) { is ConversationVolatileConfig -> Kind.CONVO_INFO_VOLATILE is UserGroupsConfig -> Kind.GROUPS is GroupInfoConfig -> Kind.CLOSED_GROUP_INFO - is GroupKeysConfig -> Kind.ENCRYPTION_KEYS is GroupMembersConfig -> Kind.CLOSED_GROUP_MEMBERS } @@ -275,7 +274,7 @@ class GroupMembersConfig(pointer: Long): ConfigBase(pointer), Closeable { } } -class GroupKeysConfig(pointer: Long): ConfigBase(pointer), Closeable { +class GroupKeysConfig(private val pointer: Long): Closeable { companion object { init { System.loadLibrary("session_util") @@ -292,16 +291,16 @@ class GroupKeysConfig(pointer: Long): ConfigBase(pointer), Closeable { } external fun groupKeys(): Stack external fun keyDump(): ByteArray - external fun loadKey(hash: String, - data: ByteArray, - msgId: ByteArray, + external fun loadKey(message: ByteArray, + hash: String, timestampMs: Long, info: GroupInfoConfig, members: GroupMembersConfig) external fun needsRekey(): Boolean external fun pendingKey(): ByteArray? - external fun pendingPush(): ByteArray? + external fun pendingConfig(): ByteArray? external fun rekey(info: GroupInfoConfig, members: GroupMembersConfig): ByteArray + external fun free() override fun close() { free() } 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 6e56af1a12..bc93703f32 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -156,7 +156,7 @@ interface StorageProtocol { fun setExpirationTimer(address: String, duration: Int) // Closed Groups - fun createNewGroup(groupName: String, groupDescription: String, members: Set): Optional + fun createNewGroup(groupName: String, groupDescription: String, members: Set): Optional fun getMembers(groupPublicKey: String): List // Groups 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 1a7e3d8eaa..698e23f2bb 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 @@ -9,7 +9,7 @@ import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.messages.control.ClosedGroupControlMessage import org.session.libsession.messaging.sending_receiving.MessageSender.Error import org.session.libsession.messaging.sending_receiving.notifications.PushRegistryV1 -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 +import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2 import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address.Companion.fromSerialized @@ -96,7 +96,7 @@ fun MessageSender.create( // Notify the PN server PushRegistryV1.register(device = device, publicKey = userPublicKey) // Start polling - ClosedGroupPollerV2.shared.startPolling(groupPublicKey) + LegacyClosedGroupPollerV2.shared.startPolling(groupPublicKey) // Fulfill the promise deferred.resolve(groupID) } 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 31e6993273..2c4f3c2937 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 @@ -25,7 +25,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.PointerAtt import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview import org.session.libsession.messaging.sending_receiving.notifications.PushRegistryV1 -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 +import org.session.libsession.messaging.sending_receiving.pollers.LegacyClosedGroupPollerV2 import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.messaging.utilities.WebRtcUtils @@ -580,7 +580,7 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, sentTimestamp) } // Start polling - ClosedGroupPollerV2.shared.startPolling(groupPublicKey) + LegacyClosedGroupPollerV2.shared.startPolling(groupPublicKey) } private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGroupControlMessage) { @@ -886,7 +886,7 @@ fun MessageReceiver.disableLocalGroupAndUnsubscribe(groupPublicKey: String, grou // Notify the PN server PushRegistryV1.unsubscribeGroup(groupPublicKey, publicKey = userPublicKey) // Stop polling - ClosedGroupPollerV2.shared.stopPolling(groupPublicKey) + LegacyClosedGroupPollerV2.shared.stopPolling(groupPublicKey) if (delete) { val threadId = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt new file mode 100644 index 0000000000..2a1e3c9afc --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt @@ -0,0 +1,48 @@ +package org.session.libsession.messaging.sending_receiving.pollers + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.session.libsession.snode.SnodeAPI +import org.session.libsession.utilities.ConfigFactoryProtocol +import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.SessionId + +class ClosedGroupPoller(private val executor: CoroutineScope, + private val closedGroupSessionId: SessionId, + private val configFactoryProtocol: ConfigFactoryProtocol) { + + companion object { + const val POLL_INTERVAL = 3_000L + } + + private var isRunning: Boolean = false + private var job: Job? = null + + fun start() { + job?.cancel() + job = executor.launch { + val nextPoll = poll() + delay(nextPoll) + } + } + + fun stop() { + job?.cancel() + } + + fun poll(): Long { + try { + val snode = SnodeAPI.getSingleTargetSnode(closedGroupSessionId.hexString()).get() + val info = configFactoryProtocol.getOrConstructGroupInfoConfig(closedGroupSessionId) + val members = configFactoryProtocol.getOrConstructGroupMemberConfig(closedGroupSessionId) + val keys = configFactoryProtocol.getGroupKeysConfig(closedGroupSessionId) + + } catch (e: Exception) { + Log.e("GroupPoller", "Polling failed for group", e) + } + return POLL_INTERVAL // this might change in future + } + +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/LegacyClosedGroupPollerV2.kt similarity index 98% rename from libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt rename to libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/LegacyClosedGroupPollerV2.kt index 293fbc8d8a..e79ae79961 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/LegacyClosedGroupPollerV2.kt @@ -22,7 +22,7 @@ import java.util.concurrent.ScheduledFuture import java.util.concurrent.TimeUnit import kotlin.math.min -class ClosedGroupPollerV2 { +class LegacyClosedGroupPollerV2 { private val executorService = Executors.newScheduledThreadPool(1) private var isPolling = mutableMapOf() private var futures = mutableMapOf>() @@ -36,7 +36,7 @@ class ClosedGroupPollerV2 { private val maxPollInterval = 4 * 60 * 1000 @JvmStatic - val shared = ClosedGroupPollerV2() + val shared = LegacyClosedGroupPollerV2() } class InsufficientSnodesException() : Exception("No snodes left to poll.") diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt index f0b20436fc..f069398b21 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt @@ -210,7 +210,6 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti val responseList = (rawResponses["results"] as List) // in case we had null configs, the array won't be fully populated // index of the sparse array key iterator should be the request index, with the key being the namespace - // TODO: add in specific ordering of config namespaces for processing listOfNotNull( configFactory.user?.configNamespace(), configFactory.contacts?.configNamespace(),