mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-03-01 14:16:49 +00:00
Update most of the backup integration tests.
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user