diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index 18b620e478..645a6c86da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -85,7 +85,6 @@ import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray import java.io.Closeable import java.io.IOException -import java.util.HashMap import java.util.LinkedList import java.util.Queue import java.util.concurrent.Callable diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index f543f6fba2..61ecaaca96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -114,6 +114,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V254_AddChatFolderC import org.thoughtcrime.securesms.database.helpers.migration.V255_AddCallTableLogIndex import org.thoughtcrime.securesms.database.helpers.migration.V256_FixIncrementalDigestColumns import org.thoughtcrime.securesms.database.helpers.migration.V257_CreateBackupMediaSyncTable +import org.thoughtcrime.securesms.database.helpers.migration.V258_FixGroupRevokedInviteeUpdate /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -230,10 +231,11 @@ object SignalDatabaseMigrations { 254 to V254_AddChatFolderConstraint, 255 to V255_AddCallTableLogIndex, 256 to V256_FixIncrementalDigestColumns, - 257 to V257_CreateBackupMediaSyncTable + 257 to V257_CreateBackupMediaSyncTable, + 258 to V258_FixGroupRevokedInviteeUpdate ) - const val DATABASE_VERSION = 257 + const val DATABASE_VERSION = 258 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt new file mode 100644 index 0000000000..105b372a00 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import androidx.core.content.contentValuesOf +import net.zetetic.database.sqlcipher.SQLiteDatabase +import okio.IOException +import org.signal.core.util.forEach +import org.signal.core.util.logging.Log +import org.signal.core.util.requireBlob +import org.signal.core.util.requireLong +import org.thoughtcrime.securesms.backup.v2.proto.GroupInvitationRevokedUpdate +import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras +import org.whispersystems.signalservice.api.push.ServiceId + +/** + * Ensure we store ACIs only in the ACI fields and non-byte prefixed PNIs only in the PNI fields of + * [GroupInvitationRevokedUpdate.Invitee] in [GroupInvitationRevokedUpdate]. + */ +@Suppress("ClassName") +object V258_FixGroupRevokedInviteeUpdate : SignalDatabaseMigration { + + private val TAG = Log.tag(V258_FixGroupRevokedInviteeUpdate::class) + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + val messageExtrasFixes = mutableListOf>() + + db.query("message", arrayOf("_id", "message_extras"), "message_extras IS NOT NULL AND type & 0x10000 != 0", null, null, null, null) + .forEach { cursor -> + val blob = cursor.requireBlob("message_extras")!! + + val messageExtras: MessageExtras? = try { + MessageExtras.ADAPTER.decode(blob) + } catch (e: IOException) { + Log.w(TAG, "Unable to decode message extras", e) + null + } + + if (messageExtras?.gv2UpdateDescription?.groupChangeUpdate?.updates?.any { it.groupInvitationRevokedUpdate != null } != true) { + return@forEach + } + + val groupUpdateDescription = messageExtras.gv2UpdateDescription + val groupUpdate = groupUpdateDescription.groupChangeUpdate!! + val updates = groupUpdate.updates.toMutableList() + + updates + .replaceAll { change -> + if (change.groupInvitationRevokedUpdate != null) { + val invitees = change.groupInvitationRevokedUpdate.invitees.toMutableList() + + invitees.replaceAll { invitee -> + val inviteeAciFieldServiceId = ServiceId.parseOrNull(invitee.inviteeAci) + val inviteePniFieldServiceId = ServiceId.parseOrNull(invitee.inviteePni) + + if (inviteeAciFieldServiceId is ServiceId.PNI) { + // We have an obvious PNI in the ACI field, move to PNI field without byte prefix + invitee.copy(inviteeAci = null, inviteePni = inviteeAciFieldServiceId.toByteStringWithoutPrefix()) + } else if (inviteePniFieldServiceId is ServiceId.PNI) { + // We have a byte-prefixed encoded PNI in the PNI field, update to remove byte prefix + invitee.copy(inviteePni = inviteePniFieldServiceId.toByteStringWithoutPrefix()) + } else { + // ACI field doesn't have an obvious PNI and PNI doesn't have a byte-prefixed PNI, no fix needed + invitee + } + } + + change.copy(groupInvitationRevokedUpdate = change.groupInvitationRevokedUpdate.copy(invitees = invitees)) + } else { + change + } + } + + val updatedMessageExtras = messageExtras.copy( + gv2UpdateDescription = groupUpdateDescription.copy( + groupChangeUpdate = groupUpdate.copy( + updates = updates + ) + ) + ) + + messageExtrasFixes += cursor.requireLong("_id") to updatedMessageExtras.encode() + } + + messageExtrasFixes.forEach { (id, extras) -> + db.update("message", contentValuesOf("message_extras" to extras), "_id = $id", null) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index f646a448fc..8461f4aad7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -161,9 +161,10 @@ public class ApplicationMigrations { static final int SVR2_ENCLAVE_UPDATE_2 = 117; static final int WALLPAPER_MIGRATION_CLEANUP = 118; static final int AEP_INTRODUCTION = 119; + static final int GROUP_EXTRAS_DB_FIX = 120; } - public static final int CURRENT_VERSION = 119; + public static final int CURRENT_VERSION = 120; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -738,6 +739,10 @@ public class ApplicationMigrations { jobs.put(Version.AEP_INTRODUCTION, new AepMigrationJob()); } + if (lastSeenVersion < Version.GROUP_EXTRAS_DB_FIX) { + jobs.put(Version.GROUP_EXTRAS_DB_FIX, new DatabaseMigrationJob()); + } + return jobs; }