mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-17 20:11:47 +00:00
Updated the code to ignore outdated legacy group control message changes
This commit is contained in:
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database
|
||||
import android.content.Context
|
||||
import androidx.core.content.contentValuesOf
|
||||
import androidx.core.database.getBlobOrNull
|
||||
import androidx.core.database.getLongOrNull
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
|
||||
class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) {
|
||||
@@ -11,21 +12,23 @@ 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 TIMESTAMP = "timestamp" // Milliseconds
|
||||
|
||||
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, PRIMARY KEY($VARIANT, $PUBKEY));"
|
||||
"CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, $TIMESTAMP INTEGER NOT NULL DEFAULT 0, PRIMARY KEY($VARIANT, $PUBKEY));"
|
||||
|
||||
private const val VARIANT_AND_PUBKEY_WHERE = "$VARIANT = ? AND $PUBKEY = ?"
|
||||
}
|
||||
|
||||
fun storeConfig(variant: String, publicKey: String, data: ByteArray) {
|
||||
fun storeConfig(variant: String, publicKey: String, data: ByteArray, timestamp: Long) {
|
||||
val db = writableDatabase
|
||||
val contentValues = contentValuesOf(
|
||||
VARIANT to variant,
|
||||
PUBKEY to publicKey,
|
||||
DATA to data,
|
||||
TIMESTAMP to timestamp
|
||||
)
|
||||
db.insertOrUpdate(TABLE_NAME, contentValues, VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey))
|
||||
}
|
||||
@@ -40,4 +43,11 @@ class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(co
|
||||
}
|
||||
}
|
||||
|
||||
fun retrieveConfigLastUpdateTimestamp(variant: String, publicKey: String): Long {
|
||||
val db = readableDatabase
|
||||
val cursor = db.query(TABLE_NAME, arrayOf(TIMESTAMP), VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey),null, null, null)
|
||||
if (cursor == null) return 0
|
||||
if (!cursor.moveToFirst()) return 0
|
||||
return (cursor.getLongOrNull(cursor.getColumnIndex(TIMESTAMP)) ?: 0)
|
||||
}
|
||||
}
|
@@ -411,6 +411,10 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
|
||||
notifyUpdates(forConfigObject)
|
||||
}
|
||||
|
||||
override fun canPerformConfigChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean {
|
||||
return configFactory.canPerformChange(variant, publicKey, changeTimestampMs)
|
||||
}
|
||||
|
||||
fun notifyUpdates(forConfigObject: ConfigBase) {
|
||||
when (forConfigObject) {
|
||||
is UserProfile -> updateUser(forConfigObject)
|
||||
@@ -869,7 +873,8 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
|
||||
encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
|
||||
encSecKey = latestKeyPair.privateKey.serialize(),
|
||||
priority = if (isPinned(threadID)) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE,
|
||||
disappearingTimer = recipientSettings.expireMessages.toLong()
|
||||
disappearingTimer = recipientSettings.expireMessages.toLong(),
|
||||
joinedAt = (existingGroup.formationTimestamp / 1000L)
|
||||
)
|
||||
userGroups.set(groupInfo)
|
||||
}
|
||||
@@ -1263,6 +1268,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
|
||||
override fun deleteConversation(threadID: Long) {
|
||||
val recipient = getRecipientForThread(threadID)
|
||||
val threadDB = DatabaseComponent.get(context).threadDatabase()
|
||||
val groupDB = DatabaseComponent.get(context).groupDatabase()
|
||||
threadDB.deleteConversation(threadID)
|
||||
if (recipient != null) {
|
||||
if (recipient.isContactRecipient) {
|
||||
@@ -1276,9 +1282,11 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
|
||||
// TODO: handle closed group
|
||||
val volatile = configFactory.convoVolatile ?: return
|
||||
val groups = configFactory.userGroups ?: return
|
||||
val closedGroup = getGroup(recipient.address.toGroupString())
|
||||
val groupID = recipient.address.toGroupString()
|
||||
val closedGroup = getGroup(groupID)
|
||||
val groupPublicKey = GroupUtil.doubleDecodeGroupId(recipient.address.serialize())
|
||||
if (closedGroup != null) {
|
||||
groupDB.delete(groupID) // TODO: Should we delete the group? (seems odd to leave it)
|
||||
volatile.eraseLegacyClosedGroup(groupPublicKey)
|
||||
groups.eraseLegacyGroup(groupPublicKey)
|
||||
} else {
|
||||
|
@@ -21,6 +21,13 @@ class ConfigFactory(
|
||||
private val maybeGetUserInfo: () -> Pair<ByteArray, String>?
|
||||
) :
|
||||
ConfigFactoryProtocol {
|
||||
companion object {
|
||||
// This is a buffer period within which we will process messages which would result in a
|
||||
// config change, any message which would normally result in a config change which was sent
|
||||
// before `lastConfigMessage.timestamp - configChangeBufferPeriod` will not actually have
|
||||
// it's changes applied (control text will still be added though)
|
||||
val configChangeBufferPeriod: Long = (2 * 60 * 1000)
|
||||
}
|
||||
|
||||
fun keyPairChanged() { // this should only happen restoring or clearing data
|
||||
_userConfig?.free()
|
||||
@@ -136,48 +143,58 @@ class ConfigFactory(
|
||||
listOfNotNull(user, contacts, convoVolatile, userGroups)
|
||||
|
||||
|
||||
private fun persistUserConfigDump() = synchronized(userLock) {
|
||||
private fun persistUserConfigDump(timestamp: Long) = synchronized(userLock) {
|
||||
val dumped = user?.dump() ?: return
|
||||
val (_, publicKey) = maybeGetUserInfo() ?: return
|
||||
configDatabase.storeConfig(SharedConfigMessage.Kind.USER_PROFILE.name, publicKey, dumped)
|
||||
configDatabase.storeConfig(SharedConfigMessage.Kind.USER_PROFILE.name, publicKey, dumped, timestamp)
|
||||
}
|
||||
|
||||
private fun persistContactsConfigDump() = synchronized(contactsLock) {
|
||||
private fun persistContactsConfigDump(timestamp: Long) = synchronized(contactsLock) {
|
||||
val dumped = contacts?.dump() ?: return
|
||||
val (_, publicKey) = maybeGetUserInfo() ?: return
|
||||
configDatabase.storeConfig(SharedConfigMessage.Kind.CONTACTS.name, publicKey, dumped)
|
||||
configDatabase.storeConfig(SharedConfigMessage.Kind.CONTACTS.name, publicKey, dumped, timestamp)
|
||||
}
|
||||
|
||||
private fun persistConvoVolatileConfigDump() = synchronized(convoVolatileLock) {
|
||||
private fun persistConvoVolatileConfigDump(timestamp: Long) = synchronized(convoVolatileLock) {
|
||||
val dumped = convoVolatile?.dump() ?: return
|
||||
val (_, publicKey) = maybeGetUserInfo() ?: return
|
||||
configDatabase.storeConfig(
|
||||
SharedConfigMessage.Kind.CONVO_INFO_VOLATILE.name,
|
||||
publicKey,
|
||||
dumped
|
||||
dumped,
|
||||
timestamp
|
||||
)
|
||||
}
|
||||
|
||||
private fun persistUserGroupsConfigDump() = synchronized(userGroupsLock) {
|
||||
private fun persistUserGroupsConfigDump(timestamp: Long) = synchronized(userGroupsLock) {
|
||||
val dumped = userGroups?.dump() ?: return
|
||||
val (_, publicKey) = maybeGetUserInfo() ?: return
|
||||
configDatabase.storeConfig(SharedConfigMessage.Kind.GROUPS.name, publicKey, dumped)
|
||||
configDatabase.storeConfig(SharedConfigMessage.Kind.GROUPS.name, publicKey, dumped, timestamp)
|
||||
}
|
||||
|
||||
override fun persist(forConfigObject: ConfigBase) {
|
||||
override fun persist(forConfigObject: ConfigBase, timestamp: Long) {
|
||||
try {
|
||||
listeners.forEach { listener ->
|
||||
listener.notifyUpdates(forConfigObject)
|
||||
}
|
||||
when (forConfigObject) {
|
||||
is UserProfile -> persistUserConfigDump()
|
||||
is Contacts -> persistContactsConfigDump()
|
||||
is ConversationVolatileConfig -> persistConvoVolatileConfigDump()
|
||||
is UserGroupsConfig -> persistUserGroupsConfigDump()
|
||||
is UserProfile -> persistUserConfigDump(timestamp)
|
||||
is Contacts -> persistContactsConfigDump(timestamp)
|
||||
is ConversationVolatileConfig -> persistConvoVolatileConfigDump(timestamp)
|
||||
is UserGroupsConfig -> persistUserGroupsConfigDump(timestamp)
|
||||
else -> throw UnsupportedOperationException("Can't support type of ${forConfigObject::class.simpleName} yet")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("Loki", "failed to persist ${forConfigObject.javaClass.simpleName}", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun canPerformChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean {
|
||||
if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return true
|
||||
|
||||
val lastUpdateTimestampMs = configDatabase.retrieveConfigLastUpdateTimestamp(variant, publicKey)
|
||||
|
||||
// Ensure the change occurred after the last config message was handled (minus the buffer period)
|
||||
return (changeTimestampMs >= (lastUpdateTimestampMs - ConfigFactory.configChangeBufferPeriod))
|
||||
}
|
||||
}
|
@@ -32,6 +32,7 @@ import nl.komponents.kovenant.ui.alwaysUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.session.libsession.avatars.AvatarHelper
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.*
|
||||
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
@@ -242,7 +243,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
}
|
||||
if (userConfig != null && userConfig.needsDump()) {
|
||||
configFactory.persist(userConfig)
|
||||
configFactory.persist(userConfig, SnodeAPI.nowWithOffset)
|
||||
}
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
|
||||
}
|
||||
|
Reference in New Issue
Block a user