Update most of the backup integration tests.

This commit is contained in:
Greyson Parrelli
2024-11-26 16:28:48 -05:00
parent 7b0df17d9a
commit bbd6643733
332 changed files with 125 additions and 55 deletions

View File

@@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.backup.v2.exporters.ContactArchiveExporter
import org.thoughtcrime.securesms.backup.v2.exporters.GroupArchiveExporter
import org.thoughtcrime.securesms.backup.v2.proto.AccountData
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -30,26 +31,36 @@ import org.thoughtcrime.securesms.recipients.RecipientId
fun RecipientTable.getContactsForBackup(selfId: Long): ContactArchiveExporter {
val cursor = readableDatabase
.select(
RecipientTable.ID,
RecipientTable.ACI_COLUMN,
RecipientTable.PNI_COLUMN,
RecipientTable.USERNAME,
RecipientTable.E164,
RecipientTable.BLOCKED,
RecipientTable.HIDDEN,
RecipientTable.REGISTERED,
RecipientTable.UNREGISTERED_TIMESTAMP,
RecipientTable.PROFILE_KEY,
RecipientTable.PROFILE_SHARING,
RecipientTable.PROFILE_GIVEN_NAME,
RecipientTable.PROFILE_FAMILY_NAME,
RecipientTable.PROFILE_JOINED_NAME,
RecipientTable.MUTE_UNTIL,
RecipientTable.CHAT_COLORS,
RecipientTable.CUSTOM_CHAT_COLORS_ID,
RecipientTable.EXTRAS
"${RecipientTable.TABLE_NAME}.${RecipientTable.ID}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.ACI_COLUMN}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.PNI_COLUMN}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.USERNAME}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.E164}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.HIDDEN}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.REGISTERED}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.UNREGISTERED_TIMESTAMP}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_KEY}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_SHARING}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_GIVEN_NAME}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_FAMILY_NAME}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_JOINED_NAME}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.CHAT_COLORS}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}",
"${RecipientTable.TABLE_NAME}.${RecipientTable.EXTRAS}",
"${IdentityTable.TABLE_NAME}.${IdentityTable.IDENTITY_KEY}",
"${IdentityTable.TABLE_NAME}.${IdentityTable.VERIFIED}"
)
.from(
"""
${RecipientTable.TABLE_NAME} LEFT OUTER JOIN ${IdentityTable.TABLE_NAME} ON (
${RecipientTable.TABLE_NAME}.${RecipientTable.ACI_COLUMN} = ${IdentityTable.TABLE_NAME}.${IdentityTable.ADDRESS} OR (
${RecipientTable.TABLE_NAME}.${RecipientTable.ACI_COLUMN} IS NULL AND ${RecipientTable.TABLE_NAME}.${RecipientTable.PNI_COLUMN} = ${IdentityTable.TABLE_NAME}.${IdentityTable.ADDRESS}
)
)
"""
)
.from(RecipientTable.TABLE_NAME)
.where(
"""
${RecipientTable.TYPE} = ? AND (

View File

@@ -48,9 +48,9 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData
recipientId = cursor.requireLong(ThreadTable.RECIPIENT_ID),
archived = cursor.requireBoolean(ThreadTable.ARCHIVED),
pinnedOrder = cursor.requireInt(ThreadTable.PINNED),
expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME).seconds.inWholeMilliseconds,
expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME).seconds.inWholeMilliseconds.takeIf { it > 0 },
expireTimerVersion = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION),
muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL),
muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL).takeIf { it > 0 },
markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD,
dontNotifyForMentionsIfMuted = RecipientTable.MentionSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING),
style = ChatStyleConverter.constructRemoteChatStyle(

View File

@@ -220,8 +220,8 @@ class ChatItemArchiveExporter(
MessageTypes.isExpirationTimerUpdate(record.type) -> {
builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn))
builder.expireStartDate = 0
builder.expiresInMs = 0
builder.expireStartDate = null
builder.expiresInMs = null
}
MessageTypes.isProfileChange(record.type) -> {
@@ -394,8 +394,8 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien
chatId = record.threadId
authorId = record.fromRecipientId
dateSent = record.dateSent
expireStartDate = if (record.expireStarted > 0) record.expireStarted else 0
expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0
expireStartDate = record.expireStarted.takeIf { it > 0 }
expiresInMs = record.expiresIn.takeIf { it > 0 }
revisions = emptyList()
sms = record.type.isSmsType()
if (record.type.isDirectionlessType() || record.messageExtras?.gv2UpdateDescription != null) {
@@ -405,32 +405,32 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien
sendStatus = record.toRemoteSendStatus(isGroupThread, groupReceipts, exportState)
)
if (expiresInMs > 0 && outgoing?.sendStatus?.all { it.pending == null && it.failed == null } == true) {
if (expiresInMs != null && outgoing?.sendStatus?.all { it.pending == null && it.failed == null } == true) {
Log.w(TAG, "Outgoing expiring message was sent but the timer wasn't started! Fixing.")
expireStartDate = record.dateReceived
}
} else {
incoming = ChatItem.IncomingMessageDetails(
dateServerSent = max(record.dateServer, 0),
dateServerSent = record.dateServer.takeIf { it > 0 },
dateReceived = record.dateReceived,
read = record.read,
sealedSender = record.sealedSender
)
if (expiresInMs > 0 && incoming?.read == true && expireStartDate == 0L) {
if (expiresInMs != null && incoming?.read == true && expireStartDate == null) {
Log.w(TAG, "Incoming expiring message was read but the timer wasn't started! Fixing.")
expireStartDate = record.dateReceived
}
}
}
if (!MessageTypes.isExpirationTimerUpdate(record.type) && builder.expiresInMs > 0 && builder.expireStartDate + builder.expiresInMs < backupStartTime + 1.days.inWholeMilliseconds) {
if (!MessageTypes.isExpirationTimerUpdate(record.type) && builder.expiresInMs != null && builder.expireStartDate != null && builder.expireStartDate!! + builder.expiresInMs!! < backupStartTime + 1.days.inWholeMilliseconds) {
Log.w(TAG, "Message expires too soon! Must skip.")
return null
}
if (builder.expireStartDate > 0 && builder.expiresInMs == 0L) {
builder.expireStartDate = 0
if (builder.expireStartDate != null && builder.expiresInMs == null) {
builder.expireStartDate = null
}
return builder
@@ -801,7 +801,7 @@ private fun BackupMessageRecord.toRemoteQuote(mediaArchiveEnabled: Boolean, atta
attachments = attachments?.toRemoteQuoteAttachments(mediaArchiveEnabled) ?: emptyList(),
type = when (type) {
QuoteModel.Type.NORMAL -> Quote.Type.NORMAL
QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE
QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFT_BADGE
}
)
}

View File

@@ -9,6 +9,7 @@ import android.database.Cursor
import okio.ByteString.Companion.toByteString
import org.signal.core.util.Base64
import org.signal.core.util.logging.Log
import org.signal.core.util.optionalInt
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
@@ -16,6 +17,7 @@ import org.signal.core.util.requireString
import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient
import org.thoughtcrime.securesms.backup.v2.proto.Contact
import org.thoughtcrime.securesms.backup.v2.proto.Self
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.RecipientTableCursorUtil
import org.thoughtcrime.securesms.recipients.Recipient
@@ -71,6 +73,8 @@ class ContactArchiveExporter(private val cursor: Cursor, private val selfId: Lon
.profileGivenName(cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME))
.profileFamilyName(cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME))
.hideStory(RecipientTableCursorUtil.getExtras(cursor)?.hideStory() ?: false)
.identityKey(cursor.requireString(IdentityTable.IDENTITY_KEY)?.let { Base64.decode(it).toByteString() })
.identityState(cursor.optionalInt(IdentityTable.VERIFIED).map { IdentityTable.VerifiedStatus.forState(it) }.orElse(IdentityTable.VerifiedStatus.DEFAULT).toRemote())
val registeredState = RecipientTable.RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED))
if (registeredState == RecipientTable.RegisteredState.REGISTERED) {
@@ -98,6 +102,14 @@ private fun Recipient.HiddenState.toRemote(): Contact.Visibility {
}
}
private fun IdentityTable.VerifiedStatus.toRemote(): Contact.IdentityState {
return when (this) {
IdentityTable.VerifiedStatus.DEFAULT -> Contact.IdentityState.DEFAULT
IdentityTable.VerifiedStatus.VERIFIED -> Contact.IdentityState.VERIFIED
IdentityTable.VerifiedStatus.UNVERIFIED -> Contact.IdentityState.UNVERIFIED
}
}
private fun String.e164ToLong(): Long? {
val fixed = if (this.startsWith("+")) {
this.substring(1)

View File

@@ -55,8 +55,8 @@ object ChatArchiveImporter {
RecipientTable.TABLE_NAME,
contentValuesOf(
RecipientTable.MENTION_SETTING to (if (chat.dontNotifyForMentionsIfMuted) RecipientTable.MentionSetting.DO_NOT_NOTIFY.id else RecipientTable.MentionSetting.ALWAYS_NOTIFY.id),
RecipientTable.MUTE_UNTIL to chat.muteUntilMs,
RecipientTable.MESSAGE_EXPIRATION_TIME to chat.expirationTimerMs.milliseconds.inWholeSeconds,
RecipientTable.MUTE_UNTIL to (chat.muteUntilMs ?: 0),
RecipientTable.MESSAGE_EXPIRATION_TIME to (chat.expirationTimerMs?.milliseconds?.inWholeSeconds ?: 0),
RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION to chat.expireTimerVersion,
RecipientTable.CHAT_COLORS to chatColor?.serialize()?.encode(),
RecipientTable.CUSTOM_CHAT_COLORS_ID to (chatColor?.id ?: ChatColors.Id.NotSet).longValue,

View File

@@ -456,8 +456,8 @@ class ChatItemArchiveImporter(
contentValues.putNull(MessageTable.LATEST_REVISION_ID)
contentValues.putNull(MessageTable.ORIGINAL_MESSAGE_ID)
contentValues.put(MessageTable.REVISION_NUMBER, 0)
contentValues.put(MessageTable.EXPIRES_IN, this.expiresInMs)
contentValues.put(MessageTable.EXPIRE_STARTED, this.expireStartDate)
contentValues.put(MessageTable.EXPIRES_IN, this.expiresInMs ?: 0)
contentValues.put(MessageTable.EXPIRE_STARTED, this.expireStartDate ?: 0)
when {
this.outgoing != null -> {
@@ -669,7 +669,7 @@ class ChatItemArchiveImporter(
}
updateMessage.expirationTimerChange != null -> {
typeFlags = getAsLong(MessageTable.TYPE) or MessageTypes.EXPIRATION_TIMER_UPDATE_BIT
put(MessageTable.EXPIRES_IN, updateMessage.expirationTimerChange.expiresInMs.toLong())
put(MessageTable.EXPIRES_IN, updateMessage.expirationTimerChange.expiresInMs)
}
updateMessage.profileChange != null -> {
typeFlags = MessageTypes.PROFILE_CHANGE_TYPE
@@ -895,7 +895,8 @@ class ChatItemArchiveImporter(
return when (this) {
Quote.Type.UNKNOWN -> QuoteModel.Type.NORMAL.code
Quote.Type.NORMAL -> QuoteModel.Type.NORMAL.code
Quote.Type.GIFTBADGE -> QuoteModel.Type.GIFT_BADGE.code
Quote.Type.GIFT_BADGE -> QuoteModel.Type.GIFT_BADGE.code
Quote.Type.VIEW_ONCE -> QuoteModel.Type.NORMAL.code
}
}

View File

@@ -7,10 +7,13 @@ package org.thoughtcrime.securesms.backup.v2.importer
import androidx.core.content.contentValuesOf
import org.signal.core.util.Base64
import org.signal.core.util.insertInto
import org.signal.core.util.toInt
import org.signal.core.util.update
import org.thoughtcrime.securesms.backup.v2.proto.Contact
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SQLiteDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras
import org.thoughtcrime.securesms.dependencies.AppDependencies
@@ -26,9 +29,12 @@ import org.whispersystems.signalservice.api.push.ServiceId.PNI
*/
object ContactArchiveImporter {
fun import(contact: Contact): RecipientId {
val aci = ACI.parseOrNull(contact.aci?.toByteArray())
val pni = PNI.parseOrNull(contact.pni?.toByteArray())
val id = SignalDatabase.recipients.getAndPossiblyMergePnpVerified(
aci = ACI.parseOrNull(contact.aci?.toByteArray()),
pni = PNI.parseOrNull(contact.pni?.toByteArray()),
aci = aci,
pni = pni,
e164 = contact.formattedE164
)
@@ -60,6 +66,17 @@ object ContactArchiveImporter {
.where("${RecipientTable.ID} = ?", id)
.run()
if (contact.identityKey != null && (aci != null || pni != null)) {
SignalDatabase.writableDatabase
.insertInto(IdentityTable.TABLE_NAME)
.values(
IdentityTable.ADDRESS to (aci ?: pni).toString(),
IdentityTable.IDENTITY_KEY to Base64.encodeWithPadding(contact.identityKey.toByteArray()),
IdentityTable.VERIFIED to contact.identityState.toLocal().toInt()
)
.run(SQLiteDatabase.CONFLICT_REPLACE)
}
return id
}
}
@@ -72,6 +89,14 @@ private fun Contact.Visibility.toLocal(): Recipient.HiddenState {
}
}
private fun Contact.IdentityState.toLocal(): IdentityTable.VerifiedStatus {
return when (this) {
Contact.IdentityState.DEFAULT -> IdentityTable.VerifiedStatus.DEFAULT
Contact.IdentityState.VERIFIED -> IdentityTable.VerifiedStatus.VERIFIED
Contact.IdentityState.UNVERIFIED -> IdentityTable.VerifiedStatus.UNVERIFIED
}
}
private fun Contact.toLocalExtras(): RecipientExtras {
return RecipientExtras(
hideStory = this.hideStory

View File

@@ -61,7 +61,7 @@ fun FilePointer?.toLocalAttachment(
isGif = gif,
caption = Optional.ofNullable(this.caption),
blurHash = Optional.ofNullable(this.blurHash),
uploadTimestamp = this.attachmentLocator.uploadTimestamp,
uploadTimestamp = this.attachmentLocator.uploadTimestamp ?: 0,
uuid = UuidUtil.fromByteStringOrNull(uuid)
)
return PointerAttachment.forPointer(
@@ -164,7 +164,7 @@ fun DatabaseAttachment.toRemoteFilePointer(mediaArchiveEnabled: Boolean, content
builder.attachmentLocator = FilePointer.AttachmentLocator(
cdnKey = this.remoteLocation,
cdnNumber = this.cdn.cdnNumber,
uploadTimestamp = this.uploadTimestamp,
uploadTimestamp = this.uploadTimestamp.takeIf { it > 0 },
key = Base64.decode(remoteKey).toByteString(),
size = this.size.toInt(),
digest = this.remoteDigest.toByteString()