Ensure that backup timestamps are within a certain range.

This commit is contained in:
Greyson Parrelli 2025-01-09 10:22:15 -05:00
parent 7042ce8c5c
commit b983a56dd2
6 changed files with 29 additions and 20 deletions

View file

@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.backup.v2.database
import android.database.Cursor
import org.signal.core.util.requireLong
import org.thoughtcrime.securesms.backup.v2.proto.AdHocCall
import org.thoughtcrime.securesms.backup.v2.util.clampToValidBackupRange
import org.thoughtcrime.securesms.database.CallTable
import java.io.Closeable
@ -31,7 +32,7 @@ class AdHocCallArchiveExporter(private val cursor: Cursor) : Iterator<AdHocCall>
callId = callId,
recipientId = cursor.requireLong(CallTable.PEER),
state = AdHocCall.State.GENERIC,
callTimestamp = cursor.requireLong(CallTable.TIMESTAMP)
callTimestamp = cursor.requireLong(CallTable.TIMESTAMP).clampToValidBackupRange()
)
}

View file

@ -13,6 +13,7 @@ import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
import org.thoughtcrime.securesms.backup.v2.proto.Chat
import org.thoughtcrime.securesms.backup.v2.util.ChatStyleConverter
import org.thoughtcrime.securesms.backup.v2.util.clampToValidBackupRange
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
@ -57,7 +58,7 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData
pinnedOrder = cursor.requireInt(ThreadTable.PINNED),
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).takeIf { it > 0 },
muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL).takeIf { it > 0 }?.clampToValidBackupRange(),
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

@ -53,6 +53,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.StickerMessage
import org.thoughtcrime.securesms.backup.v2.proto.Text
import org.thoughtcrime.securesms.backup.v2.proto.ThreadMergeChatUpdate
import org.thoughtcrime.securesms.backup.v2.proto.ViewOnceMessage
import org.thoughtcrime.securesms.backup.v2.util.clampToValidBackupRange
import org.thoughtcrime.securesms.backup.v2.util.toRemoteFilePointer
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.database.AttachmentTable
@ -431,7 +432,7 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien
val builder = ChatItem.Builder().apply {
chatId = record.threadId
authorId = fromRecipientId
dateSent = record.dateSent
dateSent = record.dateSent.clampToValidBackupRange()
expireStartDate = record.expireStarted.takeIf { it > 0 }
expiresInMs = record.expiresIn.takeIf { it > 0 }
revisions = emptyList()
@ -603,8 +604,8 @@ private fun CallTable.Call.toRemoteCallUpdate(db: SignalDatabase, messageRecord:
},
ringerRecipientId = this.ringerRecipient?.toLong(),
startedCallRecipientId = ACI.parseOrNull(groupCallUpdateDetails.startedCallUuid)?.let { db.recipientTable.getByAci(it).getOrNull()?.toLong() },
startedCallTimestamp = this.timestamp,
endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp,
startedCallTimestamp = this.timestamp.clampToValidBackupRange(),
endedCallTimestamp = groupCallUpdateDetails.endedCallTimestamp.clampToValidBackupRange(),
read = messageRecord.read
)
)
@ -634,7 +635,7 @@ private fun CallTable.Call.toRemoteCallUpdate(db: SignalDatabase, messageRecord:
return null
}
},
startedCallTimestamp = this.timestamp,
startedCallTimestamp = this.timestamp.clampToValidBackupRange(),
read = messageRecord.read
)
)
@ -720,7 +721,7 @@ private fun BackupMessageRecord.toRemoteLinkPreviews(attachments: List<DatabaseA
val attachment = attachmentIdMap[preview.attachmentId]
if (attachment != null) {
previews += LinkPreview(preview.url, preview.title, preview.description, preview.date, attachment)
previews += LinkPreview(preview.url, preview.title, preview.description, preview.date.clampToValidBackupRange(), attachment)
} else {
previews += preview
}
@ -745,7 +746,7 @@ private fun LinkPreview.toRemoteLinkPreview(mediaArchiveEnabled: Boolean): org.t
title = title.nullIfEmpty(),
image = (thumbnail.orNull() as? DatabaseAttachment)?.toRemoteMessageAttachment(mediaArchiveEnabled)?.pointer,
description = description.nullIfEmpty(),
date = date
date = date.clampToValidBackupRange()
)
}
@ -884,7 +885,7 @@ private fun BackupMessageRecord.toRemoteQuote(mediaArchiveEnabled: Boolean, atta
}
return Quote(
targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID },
targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }?.clampToValidBackupRange(),
authorId = this.quoteAuthor,
text = this.quoteBody?.let { body ->
Text(
@ -981,9 +982,9 @@ private fun PaymentTable.PaymentTransaction.toRemoteTransactionDetails(): Paymen
return PaymentNotification.TransactionDetails(
transaction = PaymentNotification.TransactionDetails.Transaction(
status = this.state.toRemote(),
timestamp = this.timestamp,
timestamp = this.timestamp.clampToValidBackupRange(),
blockIndex = this.blockIndex,
blockTimestamp = this.blockTimestamp,
blockTimestamp = this.blockTimestamp.clampToValidBackupRange(),
mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.let {
PaymentNotification.TransactionDetails.MobileCoinTxoIdentification(
publicKey = it.publicKey.takeIf { this.direction.isReceived } ?: emptyList(),
@ -1065,7 +1066,7 @@ private fun List<ReactionRecord>?.toRemote(): List<Reaction> {
Reaction(
emoji = it.emoji,
authorId = it.author.toLong(),
sentTimestamp = it.dateSent,
sentTimestamp = it.dateSent.clampToValidBackupRange(),
sortOrder = it.dateReceived
)
} ?: emptyList()
@ -1298,9 +1299,9 @@ private fun Cursor.toBackupMessageRecord(pastIds: Set<Long>, backupStartTime: Lo
return BackupMessageRecord(
id = id,
dateSent = this.requireLong(MessageTable.DATE_SENT),
dateReceived = this.requireLong(MessageTable.DATE_RECEIVED),
dateServer = this.requireLong(MessageTable.DATE_SERVER),
dateSent = this.requireLong(MessageTable.DATE_SENT).clampToValidBackupRange(),
dateReceived = this.requireLong(MessageTable.DATE_RECEIVED).clampToValidBackupRange(),
dateServer = this.requireLong(MessageTable.DATE_SERVER).clampToValidBackupRange(),
type = this.requireLong(MessageTable.TYPE),
threadId = this.requireLong(MessageTable.THREAD_ID),
body = this.requireString(MessageTable.BODY),
@ -1313,7 +1314,7 @@ private fun Cursor.toBackupMessageRecord(pastIds: Set<Long>, backupStartTime: Lo
sealedSender = this.requireBoolean(MessageTable.UNIDENTIFIED),
linkPreview = this.requireString(MessageTable.LINK_PREVIEWS),
sharedContacts = this.requireString(MessageTable.SHARED_CONTACTS),
quoteTargetSentTimestamp = this.requireLong(MessageTable.QUOTE_ID),
quoteTargetSentTimestamp = this.requireLong(MessageTable.QUOTE_ID).clampToValidBackupRange(),
quoteAuthor = this.requireLong(MessageTable.QUOTE_AUTHOR),
quoteBody = this.requireString(MessageTable.QUOTE_BODY),
quoteMissing = this.requireBoolean(MessageTable.QUOTE_MISSING),

View file

@ -17,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.backup.v2.util.clampToValidBackupRange
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.RecipientTableCursorUtil
@ -80,7 +81,7 @@ class ContactArchiveExporter(private val cursor: Cursor, private val selfId: Lon
if (registeredState == RecipientTable.RegisteredState.REGISTERED) {
contactBuilder.registered = Contact.Registered()
} else {
contactBuilder.notRegistered = Contact.NotRegistered(unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP))
contactBuilder.notRegistered = Contact.NotRegistered(unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP).clampToValidBackupRange())
}
return ArchiveRecipient(

View file

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient
import org.thoughtcrime.securesms.backup.v2.database.getMembersForBackup
import org.thoughtcrime.securesms.backup.v2.proto.DistributionList
import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem
import org.thoughtcrime.securesms.backup.v2.util.clampToValidBackupRange
import org.thoughtcrime.securesms.database.DistributionListTables
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
@ -49,7 +50,7 @@ class DistributionListArchiveExporter(
allowsReplies = cursor.requireBoolean(DistributionListTables.ListTable.ALLOWS_REPLIES),
rawMembers = distributionListTables.getRawMembers(id, privacyMode),
members = distributionListTables.getMembersForBackup(id),
deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP),
deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP).clampToValidBackupRange(),
isUnknown = cursor.requireBoolean(DistributionListTables.ListTable.IS_UNKNOWN),
privacyMode = privacyMode
)

View file

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