From 858893a0e926da61218a83cd13d7db0e1bcae6bd Mon Sep 17 00:00:00 2001 From: 0x330a <92654767+0x330a@users.noreply.github.com> Date: Tue, 28 Feb 2023 17:47:25 +1100 Subject: [PATCH] feat: incorporate hashes from library, more wrapper for user groups and serialization from c++ --- .../securesms/database/ConfigDatabase.kt | 14 ++++------- .../securesms/dependencies/ConfigFactory.kt | 21 +++++------------ .../sskenvironment/ProfileManager.kt | 2 ++ libsession-util/libsession-util | 2 +- libsession-util/src/main/cpp/config_base.cpp | 23 +++++-------------- libsession-util/src/main/cpp/contacts.h | 10 +++++--- libsession-util/src/main/cpp/user_groups.cpp | 16 +++++++++++-- libsession-util/src/main/cpp/user_groups.h | 8 +++++++ .../loki/messenger/libsession_util/Config.kt | 3 +-- .../messenger/libsession_util/util/Contact.kt | 3 ++- .../messaging/jobs/ConfigurationSyncJob.kt | 19 ++++++++------- .../sending_receiving/pollers/Poller.kt | 2 +- 12 files changed, 62 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ConfigDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ConfigDatabase.kt index 0b7ae9a275..d3898645be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ConfigDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ConfigDatabase.kt @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.database import android.content.Context import androidx.core.content.contentValuesOf import androidx.core.database.getBlobOrNull -import androidx.core.database.getStringOrNull import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) { @@ -12,35 +11,32 @@ class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(co private const val VARIANT = "variant" private const val PUBKEY = "publicKey" private const val DATA = "data" - private const val COMBINED_MESSAGE_HASHES = "combined_message_hashes" private const val TABLE_NAME = "configs_table" const val CREATE_CONFIG_TABLE_COMMAND = - "CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, $COMBINED_MESSAGE_HASHES TEXT, PRIMARY KEY($VARIANT, $PUBKEY));" + "CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, PRIMARY KEY($VARIANT, $PUBKEY));" private const val VARIANT_AND_PUBKEY_WHERE = "$VARIANT = ? AND $PUBKEY = ?" } - fun storeConfig(variant: String, publicKey: String, data: ByteArray, hashes: List) { + fun storeConfig(variant: String, publicKey: String, data: ByteArray) { val db = writableDatabase val contentValues = contentValuesOf( VARIANT to variant, PUBKEY to publicKey, DATA to data, - COMBINED_MESSAGE_HASHES to hashes.joinToString(",") ) db.insertOrUpdate(TABLE_NAME, contentValues, VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey)) } - fun retrieveConfigAndHashes(variant: String, publicKey: String): Pair>? { + fun retrieveConfigAndHashes(variant: String, publicKey: String): ByteArray? { val db = readableDatabase - val query = db.query(TABLE_NAME, arrayOf(DATA, COMBINED_MESSAGE_HASHES), VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey),null, null, null) + val query = db.query(TABLE_NAME, arrayOf(DATA), VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey),null, null, null) return query?.use { cursor -> if (!cursor.moveToFirst()) return@use null val bytes = cursor.getBlobOrNull(cursor.getColumnIndex(DATA)) ?: return@use null - val hashes = cursor.getStringOrNull(cursor.getColumnIndex(COMBINED_MESSAGE_HASHES))?.split(",") ?: emptyList() - bytes to hashes + bytes } } 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 1cf146f557..415a84ba0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt @@ -47,10 +47,7 @@ class ConfigFactory(private val context: Context, Log.d("Loki-DBG", "Getting user configs and hashes") val userDump = configDatabase.retrieveConfigAndHashes(SharedConfigMessage.Kind.USER_PROFILE.name, publicKey) _userConfig = if (userDump != null) { - val (bytes, hashes) = userDump - userHashes.clear() - userHashes.addAll(hashes) - UserProfile.newInstance(secretKey, bytes) + UserProfile.newInstance(secretKey, userDump) } else { UserProfile.newInstance(secretKey) } @@ -63,10 +60,7 @@ class ConfigFactory(private val context: Context, val (secretKey, publicKey) = maybeGetUserInfo() ?: return@synchronized null val contactsDump = configDatabase.retrieveConfigAndHashes(SharedConfigMessage.Kind.CONTACTS.name, publicKey) _contacts = if (contactsDump != null) { - val (bytes, hashes) = contactsDump - contactsHashes.clear() - contactsHashes.addAll(hashes) - Contacts.newInstance(secretKey, bytes) + Contacts.newInstance(secretKey, contactsDump) } else { Contacts.newInstance(secretKey) } @@ -79,10 +73,7 @@ class ConfigFactory(private val context: Context, val (secretKey, publicKey) = maybeGetUserInfo() ?: return@synchronized null val convoDump = configDatabase.retrieveConfigAndHashes(SharedConfigMessage.Kind.CONVO_INFO_VOLATILE.name, publicKey) _convoVolatileConfig = if (convoDump != null) { - val (bytes, hashes) = convoDump - convoHashes.clear() - convoHashes.addAll(hashes) - ConversationVolatileConfig.newInstance(secretKey, bytes) + ConversationVolatileConfig.newInstance(secretKey, convoDump) } else { ConversationVolatileConfig.newInstance(secretKey) } @@ -94,19 +85,19 @@ class ConfigFactory(private val context: Context, private fun persistUserConfigDump() = synchronized(userLock) { val dumped = user?.dump() ?: return val (_, publicKey) = maybeGetUserInfo() ?: return - configDatabase.storeConfig(SharedConfigMessage.Kind.USER_PROFILE.name, publicKey, dumped, userHashes.toList()) + configDatabase.storeConfig(SharedConfigMessage.Kind.USER_PROFILE.name, publicKey, dumped) } private fun persistContactsConfigDump() = synchronized(contactsLock) { val dumped = contacts?.dump() ?: return val (_, publicKey) = maybeGetUserInfo() ?: return - configDatabase.storeConfig(SharedConfigMessage.Kind.CONTACTS.name, publicKey, dumped, contactsHashes.toList()) + configDatabase.storeConfig(SharedConfigMessage.Kind.CONTACTS.name, publicKey, dumped) } private fun persistConvoVolatileConfigDump() = synchronized (convoVolatileLock) { val dumped = convoVolatile?.dump() ?: return val (_, publicKey) = maybeGetUserInfo() ?: return - configDatabase.storeConfig(SharedConfigMessage.Kind.CONVO_INFO_VOLATILE.name, publicKey, dumped, convoHashes.toList()) + configDatabase.storeConfig(SharedConfigMessage.Kind.CONVO_INFO_VOLATILE.name, publicKey, dumped) } override fun persist(forConfigObject: ConfigBase) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt index 82bab07448..e873fb8087 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt @@ -4,6 +4,7 @@ import android.content.Context import network.loki.messenger.libsession_util.util.UserPic import org.session.libsession.messaging.contacts.Contact import org.session.libsession.utilities.SSKEnvironment +import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.dependencies.ConfigFactory @@ -84,6 +85,7 @@ class ProfileManager(private val context: Context, private val configFactory: Co override fun contactUpdatedInternal(contact: Contact) { val contactConfig = configFactory.contacts ?: return + if (contact.sessionID == TextSecurePreferences.getLocalNumber(context)) return contactConfig.upsertContact(contact.sessionID) { this.name = contact.name.orEmpty() this.nickname = contact.nickname.orEmpty() diff --git a/libsession-util/libsession-util b/libsession-util/libsession-util index c76d7e06f6..5a7569861d 160000 --- a/libsession-util/libsession-util +++ b/libsession-util/libsession-util @@ -1 +1 @@ -Subproject commit c76d7e06f6c1c258fe930bcd8d567b3bd4067262 +Subproject commit 5a7569861d1b39d866ba7d8c7440db1908e84798 diff --git a/libsession-util/src/main/cpp/config_base.cpp b/libsession-util/src/main/cpp/config_base.cpp index fdc994246d..3d2fbbb917 100644 --- a/libsession-util/src/main/cpp/config_base.cpp +++ b/libsession-util/src/main/cpp/config_base.cpp @@ -120,29 +120,18 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_00024Companion_kindFor(J return nullptr; } } -extern "C" -JNIEXPORT void JNICALL -Java_network_loki_messenger_libsession_1util_ConfigBase_removeObsoleteHashes(JNIEnv *env, - jobject thiz, - jobjectArray to_remove) { - auto conf = ptrToConfigBase(env, thiz); - size_t number = env->GetArrayLength(to_remove); - for (int i = 0; i < number; i++) { - auto jElement = (jstring) env->GetObjectArrayElement(to_remove, i); - auto element_as_string = env->GetStringUTFChars(jElement, nullptr); - // TODO: uncomment when this is re-implemented -// conf->confirm_removed(element_as_string); - env->ReleaseStringUTFChars(jElement, element_as_string); - } -} + extern "C" JNIEXPORT jobject JNICALL -Java_network_loki_messenger_libsession_1util_ConfigBase_obsoleteHashes(JNIEnv *env, jobject thiz) { +Java_network_loki_messenger_libsession_1util_ConfigBase_currentHashes(JNIEnv *env, jobject thiz) { auto conf = ptrToConfigBase(env, thiz); jclass stack = env->FindClass("java/util/Stack"); jmethodID init = env->GetMethodID(stack, "", "()V"); jobject our_stack = env->NewObject(stack, init); jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - // TODO: implement obsoleteHashes() + auto vec = conf->current_hashes(); + for (std::string element: vec) { + env->CallObjectMethod(our_stack, push, env->NewStringUTF(element.data())); + } return our_stack; } \ No newline at end of file diff --git a/libsession-util/src/main/cpp/contacts.h b/libsession-util/src/main/cpp/contacts.h index 9aaeb78e77..9c070975b8 100644 --- a/libsession-util/src/main/cpp/contacts.h +++ b/libsession-util/src/main/cpp/contacts.h @@ -14,7 +14,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;)V"); + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZLnetwork/loki/messenger/libsession_util/util/UserPic;I)V"); jstring id = env->NewStringUTF(info.session_id.data()); jstring name = env->NewStringUTF(info.name.data()); jstring nickname = env->NewStringUTF(info.nickname.data()); @@ -23,14 +23,14 @@ inline jobject serialize_contact(JNIEnv *env, session::config::contact_info info approvedMe = info.approved_me; blocked = info.blocked; jobject profilePic = util::serialize_user_pic(env, info.profile_picture); - jobject returnObj = env->NewObject(contactClass, constructor, id, name, nickname, approved, approvedMe, blocked, profilePic); + jobject returnObj = env->NewObject(contactClass, constructor, id, name, nickname, approved, approvedMe, blocked, profilePic, info.priority); return returnObj; } inline session::config::contact_info* deserialize_contact(JNIEnv *env, jobject info) { jclass contactClass = env->FindClass("network/loki/messenger/libsession_util/util/Contact"); - jfieldID getId, getName, getNick, getApproved, getApprovedMe, getBlocked, getUserPic; + jfieldID getId, getName, getNick, getApproved, getApprovedMe, getBlocked, getUserPic, getPriority; getId = env->GetFieldID(contactClass, "id", "Ljava/lang/String;"); getName = env->GetFieldID(contactClass, "name", "Ljava/lang/String;"); getNick = env->GetFieldID(contactClass, "nickname", "Ljava/lang/String;"); @@ -38,11 +38,13 @@ inline session::config::contact_info* deserialize_contact(JNIEnv *env, jobject i getApprovedMe = env->GetFieldID(contactClass, "approvedMe", "Z"); getBlocked = env->GetFieldID(contactClass, "blocked", "Z"); getUserPic = env->GetFieldID(contactClass, "profilePicture", "Lnetwork/loki/messenger/libsession_util/util/UserPic;"); + getPriority = env->GetFieldID(contactClass, "priority", "I"); 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; + int priority = env->GetIntField(info, getPriority); approved = env->GetBooleanField(info, getApproved); approvedMe = env->GetBooleanField(info, getApprovedMe); blocked = env->GetBooleanField(info, getBlocked); @@ -86,6 +88,8 @@ inline session::config::contact_info* deserialize_contact(JNIEnv *env, jobject i env->ReleaseStringUTFChars(nickname, nickname_bytes); } + contact_info->priority = priority; + return contact_info; } diff --git a/libsession-util/src/main/cpp/user_groups.cpp b/libsession-util/src/main/cpp/user_groups.cpp index ed946cb481..728ca27fbc 100644 --- a/libsession-util/src/main/cpp/user_groups.cpp +++ b/libsession-util/src/main/cpp/user_groups.cpp @@ -31,8 +31,6 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_00024Companion_new jmethodID constructor = env->GetMethodID(contactsClass, "", "(J)V"); jobject newConfig = env->NewObject(contactsClass, constructor, reinterpret_cast(user_groups)); - user_groups->get_or_construct_legacy_group() - return newConfig; } #pragma clang diagnostic pop @@ -50,7 +48,21 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_getCommunityInfo(J jobject thiz, jstring base_url, jstring room) { + auto conf = ptrToUserGroups(env, thiz); + auto base_url_bytes = env->GetStringUTFChars(base_url, nullptr); + auto room_bytes = env->GetStringUTFChars(room, nullptr); + + auto community = conf->get_community(base_url_bytes, room_bytes); + + jobject community_info = nullptr; + + if (community) { + serialize_legacy_group_info() + } + // TODO: implement getCommunityInfo() + env->ReleaseStringUTFChars(base_url, base_url_bytes); + env->ReleaseStringUTFChars(room, room_bytes); } extern "C" diff --git a/libsession-util/src/main/cpp/user_groups.h b/libsession-util/src/main/cpp/user_groups.h index efc928a427..49c47da536 100644 --- a/libsession-util/src/main/cpp/user_groups.h +++ b/libsession-util/src/main/cpp/user_groups.h @@ -3,6 +3,7 @@ #include "jni.h" #include "util.h" +#include "conversation.h" #include "session/config/user_groups.hpp" inline session::config::UserGroups* ptrToUserGroups(JNIEnv *env, jobject obj) { @@ -51,4 +52,11 @@ inline jobject serialize_members(JNIEnv *env, std::map member return nullptr; } +inline jobject serialize_community_info(JNIEnv *env, session::config::community_info info) { + auto priority = info.priority; + auto open_group = session::config::community::parse_full_url(info.full_url()); + auto serialized_community = serialize_open_group(env, (session::config::community) info); + return nullptr; +} + #endif //SESSION_ANDROID_USER_GROUPS_H 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 19e6c64189..dcb63fd259 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 @@ -34,8 +34,7 @@ sealed class ConfigBase(protected val /* yucky */ pointer: Long) { external fun encryptionDomain(): String external fun confirmPushed(seqNo: Long, newHash: String) external fun merge(toMerge: Array>): Int - external fun obsoleteHashes(): List - external fun removeObsoleteHashes(toRemove: Array) + external fun currentHashes(): List external fun configNamespace(): Int diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Contact.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Contact.kt index 300476480f..580488a4ec 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Contact.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Contact.kt @@ -7,5 +7,6 @@ data class Contact( var approved: Boolean = false, var approvedMe: Boolean = false, var blocked: Boolean = false, - var profilePicture: UserPic = UserPic.DEFAULT + var profilePicture: UserPic = UserPic.DEFAULT, + var priority: Int ) \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt index 7d693e9816..90d2e134ac 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt @@ -57,6 +57,15 @@ data class ConfigurationSyncJob(val destination: Destination): Job { // don't run anything if we don't need to push anything if (configsRequiringPush.isEmpty()) return delegate.handleJobSucceeded(this, dispatcherName) + // need to get the current hashes before we call `push()` + val toDeleteRequest = configsRequiringPush.map { base -> + // accumulate by adding together + base.currentHashes() + }.reduce(List::plus).let { toDeleteFromAllNamespaces -> + if (toDeleteFromAllNamespaces.isEmpty()) null + else SnodeAPI.buildAuthenticatedDeleteBatchInfo(destination.destinationPublicKey(), toDeleteFromAllNamespaces) + } + // allow null results here so the list index matches configsRequiringPush val batchObjects: List?> = configsRequiringPush.map { config -> val (data, seqNo) = config.push() @@ -72,14 +81,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job { message to authenticated // to keep track of seqNo for calling confirmPushed later } - val toDeleteRequest = configsRequiringPush.map { base -> - base.obsoleteHashes() - // accumulate by adding together - }.reduce(List::plus).let { toDeleteFromAllNamespaces -> - if (toDeleteFromAllNamespaces.isEmpty()) null - else SnodeAPI.buildAuthenticatedDeleteBatchInfo(destination.destinationPublicKey(), toDeleteFromAllNamespaces) - } - if (batchObjects.any { it == null }) { // stop running here, something like a signing error occurred return delegate.handleJobFailedPermanently(this, dispatcherName, NullPointerException("One or more requests had a null batch request info")) @@ -137,8 +138,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job { // confirm pushed seqno val thisSeqNo = toPushMessage.seqNo config.confirmPushed(thisSeqNo, insertHash) - // wipe any of the existing hashes which we deleted (they may or may not be in this namespace) - config.removeObsoleteHashes(deletedHashes.toTypedArray()) Log.d(TAG, "Successfully removed the deleted hashes from ${config.javaClass.simpleName}") // dump and write config after successful if (config.needsDump()) { // usually this will be true? 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 9a93cf3285..d3c2dc5ae6 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 @@ -137,7 +137,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti namespace, updateLatestHash = false, updateStoredHashes = false, - ).filter { (_, hash) -> !forConfigObject.obsoleteHashes().contains(hash) } + ).filter { (_, hash) -> !forConfigObject.currentHashes().contains(hash) } if (messages.isEmpty()) { // no new messages to process