mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-24 02:39:55 +01:00
Do most of the proto and database groundwork for the new mediaName.
This commit is contained in:
committed by
Cody Henthorne
parent
e705495638
commit
38c8f852bf
@@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.ArchiveCommitAttachmentDeletesJob
|
||||
import org.thoughtcrime.securesms.jobs.ArchiveThumbnailUploadJob
|
||||
import org.thoughtcrime.securesms.jobs.BackfillDigestJob
|
||||
import org.thoughtcrime.securesms.jobs.UploadAttachmentToArchiveJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
|
||||
@@ -109,7 +108,6 @@ object ArchiveUploadProgress {
|
||||
)
|
||||
}
|
||||
|
||||
AppDependencies.jobManager.cancelAllInQueue(BackfillDigestJob.QUEUE)
|
||||
AppDependencies.jobManager.cancelAllInQueue(ArchiveCommitAttachmentDeletesJob.ARCHIVE_ATTACHMENT_QUEUE)
|
||||
UploadAttachmentToArchiveJob.getAllQueueKeys().forEach {
|
||||
AppDependencies.jobManager.cancelAllInQueue(it)
|
||||
@@ -126,7 +124,7 @@ object ArchiveUploadProgress {
|
||||
Log.d(TAG, "Flushing job manager queue...")
|
||||
AppDependencies.jobManager.flush()
|
||||
|
||||
val queues = setOf(BackfillDigestJob.QUEUE, ArchiveThumbnailUploadJob.KEY, ArchiveCommitAttachmentDeletesJob.ARCHIVE_ATTACHMENT_QUEUE) + UploadAttachmentToArchiveJob.getAllQueueKeys()
|
||||
val queues = setOf(ArchiveThumbnailUploadJob.KEY, ArchiveCommitAttachmentDeletesJob.ARCHIVE_ATTACHMENT_QUEUE) + UploadAttachmentToArchiveJob.getAllQueueKeys()
|
||||
Log.d(TAG, "Waiting for cancelations to occur...")
|
||||
while (!AppDependencies.jobManager.areQueuesEmpty(queues)) {
|
||||
delay(1.seconds)
|
||||
|
||||
@@ -691,7 +691,7 @@ object BackupRepository {
|
||||
val localArchivableAttachments = dbSnapshot
|
||||
.attachmentTable
|
||||
.getLocalArchivableAttachments()
|
||||
.associateBy { MediaName.fromDigest(it.remoteDigest) }
|
||||
.associateBy { MediaName.fromPlaintextHashAndRemoteKey(it.plaintextHash, it.remoteKey) }
|
||||
|
||||
localBackupProgressEmitter.onAttachment(0, localArchivableAttachments.size.toLong())
|
||||
|
||||
@@ -1965,13 +1965,14 @@ class ArchiveMediaItemIterator(private val cursor: Cursor) : Iterator<ArchiveMed
|
||||
override fun hasNext(): Boolean = !cursor.isAfterLast
|
||||
|
||||
override fun next(): ArchiveMediaItem {
|
||||
val digest = cursor.requireNonNullBlob(AttachmentTable.REMOTE_DIGEST)
|
||||
val plaintextHash = cursor.requireNonNullBlob(AttachmentTable.DATA_HASH_END)
|
||||
val remoteKey = cursor.requireNonNullBlob(AttachmentTable.REMOTE_KEY)
|
||||
val cdn = cursor.requireIntOrNull(AttachmentTable.ARCHIVE_CDN)
|
||||
|
||||
val mediaId = MediaName.fromDigest(digest).toMediaId(SignalStore.backup.mediaRootBackupKey).encode()
|
||||
val thumbnailMediaId = MediaName.fromDigestForThumbnail(digest).toMediaId(SignalStore.backup.mediaRootBackupKey).encode()
|
||||
val mediaId = MediaName.fromPlaintextHashAndRemoteKey(plaintextHash, remoteKey).toMediaId(SignalStore.backup.mediaRootBackupKey).encode()
|
||||
val thumbnailMediaId = MediaName.fromPlaintextHashAndRemoteKeyForThumbnail(plaintextHash, remoteKey).toMediaId(SignalStore.backup.mediaRootBackupKey).encode()
|
||||
|
||||
cursor.moveToNext()
|
||||
return ArchiveMediaItem(mediaId, thumbnailMediaId, cdn, digest)
|
||||
return ArchiveMediaItem(mediaId, thumbnailMediaId, cdn, plaintextHash, remoteKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ package org.thoughtcrime.securesms.backup.v2
|
||||
|
||||
import android.text.TextUtils
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.Base64.decodeBase64
|
||||
import org.signal.core.util.Base64.decodeBase64OrThrow
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
|
||||
import org.thoughtcrime.securesms.attachments.InvalidAttachmentException
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
@@ -22,8 +24,8 @@ import java.util.Optional
|
||||
object DatabaseAttachmentArchiveUtil {
|
||||
@JvmStatic
|
||||
fun requireMediaName(attachment: DatabaseAttachment): MediaName {
|
||||
require(isDigestValidated(attachment))
|
||||
return MediaName.fromDigest(attachment.remoteDigest!!)
|
||||
require(hadIntegrityCheckPerformed(attachment))
|
||||
return MediaName.fromPlaintextHashAndRemoteKey(attachment.dataHash!!.decodeBase64OrThrow(), attachment.remoteKey!!.decodeBase64OrThrow())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,14 +33,21 @@ object DatabaseAttachmentArchiveUtil {
|
||||
*/
|
||||
@JvmStatic
|
||||
fun requireMediaNameAsString(attachment: DatabaseAttachment): String {
|
||||
require(isDigestValidated(attachment))
|
||||
return MediaName.fromDigest(attachment.remoteDigest!!).name
|
||||
require(hadIntegrityCheckPerformed(attachment))
|
||||
return MediaName.fromPlaintextHashAndRemoteKey(attachment.dataHash!!.decodeBase64OrThrow(), attachment.remoteKey!!.decodeBase64OrThrow()).name
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getMediaName(attachment: DatabaseAttachment): MediaName? {
|
||||
return if (isDigestValidated(attachment)) {
|
||||
attachment.remoteDigest?.let { MediaName.fromDigest(it) }
|
||||
return if (hadIntegrityCheckPerformed(attachment)) {
|
||||
val plaintextHash = attachment.dataHash.decodeBase64()
|
||||
val remoteKey = attachment.remoteKey?.decodeBase64()
|
||||
|
||||
if (plaintextHash != null && remoteKey != null) {
|
||||
MediaName.fromPlaintextHashAndRemoteKey(plaintextHash, remoteKey)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -46,11 +55,11 @@ object DatabaseAttachmentArchiveUtil {
|
||||
|
||||
@JvmStatic
|
||||
fun requireThumbnailMediaName(attachment: DatabaseAttachment): MediaName {
|
||||
require(isDigestValidated(attachment))
|
||||
return MediaName.fromDigestForThumbnail(attachment.remoteDigest!!)
|
||||
require(hadIntegrityCheckPerformed(attachment))
|
||||
return MediaName.fromPlaintextHashAndRemoteKeyForThumbnail(attachment.dataHash!!.decodeBase64OrThrow(), attachment.remoteKey!!.decodeBase64OrThrow())
|
||||
}
|
||||
|
||||
private fun isDigestValidated(attachment: DatabaseAttachment): Boolean {
|
||||
private fun hadIntegrityCheckPerformed(attachment: DatabaseAttachment): Boolean {
|
||||
return when (attachment.transferState) {
|
||||
AttachmentTable.TRANSFER_PROGRESS_DONE,
|
||||
AttachmentTable.TRANSFER_NEEDS_RESTORE,
|
||||
|
||||
@@ -63,7 +63,7 @@ object LocalArchiver {
|
||||
return@localExport
|
||||
}
|
||||
|
||||
val mediaName = MediaName.fromDigest(attachment.remoteDigest)
|
||||
val mediaName = MediaName.fromPlaintextHashAndRemoteKey(attachment.plaintextHash, attachment.remoteKey)
|
||||
|
||||
mediaNames.add(mediaName)
|
||||
|
||||
@@ -73,7 +73,6 @@ object LocalArchiver {
|
||||
}
|
||||
|
||||
source()?.use { sourceStream ->
|
||||
val iv = attachment.remoteIv
|
||||
val combinedKey = Base64.decode(attachment.remoteKey)
|
||||
val destination: OutputStream? = filesFileSystem.fileOutputStream(mediaName)
|
||||
|
||||
@@ -84,7 +83,7 @@ object LocalArchiver {
|
||||
// todo [local-backup] but deal with attachment disappearing/deleted by normal app use
|
||||
try {
|
||||
PaddingInputStream(sourceStream, attachment.size).use { input ->
|
||||
AttachmentCipherOutputStream(combinedKey, iv, destination).use { output ->
|
||||
AttachmentCipherOutputStream(combinedKey, null, destination).use { output ->
|
||||
StreamUtil.copy(input, output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.thoughtcrime.securesms.backup.v2.proto.FilePointer
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.whispersystems.signalservice.api.backup.MediaName
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
@@ -48,81 +47,84 @@ fun FilePointer?.toLocalAttachment(
|
||||
): Attachment? {
|
||||
if (this == null || this.locatorInfo == null) return null
|
||||
|
||||
val hasMediaName = this.locatorInfo.mediaName.isNotEmpty()
|
||||
val hasTransitInfo = this.locatorInfo.transitCdnKey != null
|
||||
val attachmentType = when {
|
||||
this.locatorInfo.plaintextHash != null -> AttachmentType.ARCHIVE
|
||||
this.locatorInfo.encryptedDigest != null && this.locatorInfo.transitCdnKey != null -> AttachmentType.TRANSIT
|
||||
else -> AttachmentType.INVALID
|
||||
}
|
||||
|
||||
if (hasTransitInfo && !hasMediaName) {
|
||||
val signalAttachmentPointer = SignalServiceAttachmentPointer(
|
||||
cdnNumber = this.locatorInfo.transitCdnNumber ?: Cdn.CDN_0.cdnNumber,
|
||||
remoteId = SignalServiceAttachmentRemoteId.from(locatorInfo.transitCdnKey),
|
||||
contentType = contentType,
|
||||
key = this.locatorInfo.key.toByteArray(),
|
||||
size = Optional.ofNullable(locatorInfo.size),
|
||||
preview = Optional.empty(),
|
||||
width = this.width ?: 0,
|
||||
height = this.height ?: 0,
|
||||
digest = Optional.ofNullable(this.locatorInfo.digest.toByteArray()),
|
||||
incrementalDigest = Optional.ofNullable(this.incrementalMac?.toByteArray()),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize ?: 0,
|
||||
fileName = Optional.ofNullable(fileName),
|
||||
voiceNote = voiceNote,
|
||||
isBorderless = borderless,
|
||||
isGif = gif,
|
||||
caption = Optional.ofNullable(this.caption),
|
||||
blurHash = Optional.ofNullable(this.blurHash),
|
||||
uploadTimestamp = this.locatorInfo.transitTierUploadTimestamp?.clampToValidBackupRange() ?: 0,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
return PointerAttachment.forPointer(
|
||||
pointer = Optional.of(signalAttachmentPointer),
|
||||
stickerLocator = stickerLocator,
|
||||
transferState = if (wasDownloaded) AttachmentTable.TRANSFER_NEEDS_RESTORE else AttachmentTable.TRANSFER_PROGRESS_PENDING
|
||||
).orNull()
|
||||
} else if (!hasMediaName) {
|
||||
return TombstoneAttachment(
|
||||
contentType = contentType,
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
fileName = this.fileName,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = quote,
|
||||
stickerLocator = stickerLocator,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
} else {
|
||||
return ArchivedAttachment(
|
||||
contentType = contentType,
|
||||
size = this.locatorInfo.size.toLong(),
|
||||
cdn = this.locatorInfo.transitCdnNumber ?: Cdn.CDN_0.cdnNumber,
|
||||
uploadTimestamp = this.locatorInfo.transitTierUploadTimestamp ?: 0,
|
||||
key = this.locatorInfo.key.toByteArray(),
|
||||
iv = null,
|
||||
cdnKey = this.locatorInfo.transitCdnKey?.nullIfBlank(),
|
||||
archiveCdn = this.locatorInfo.mediaTierCdnNumber,
|
||||
archiveMediaName = this.locatorInfo.mediaName,
|
||||
archiveMediaId = importState.mediaRootBackupKey.deriveMediaId(MediaName(this.locatorInfo.mediaName)).encode(),
|
||||
archiveThumbnailMediaId = importState.mediaRootBackupKey.deriveMediaId(MediaName.forThumbnailFromMediaName(this.locatorInfo.mediaName)).encode(),
|
||||
digest = this.locatorInfo.digest.toByteArray(),
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = quote,
|
||||
stickerLocator = stickerLocator,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid),
|
||||
fileName = fileName
|
||||
)
|
||||
return when (attachmentType) {
|
||||
AttachmentType.ARCHIVE -> {
|
||||
ArchivedAttachment(
|
||||
contentType = contentType,
|
||||
size = this.locatorInfo.size.toLong(),
|
||||
cdn = this.locatorInfo.transitCdnNumber ?: Cdn.CDN_0.cdnNumber,
|
||||
uploadTimestamp = this.locatorInfo.transitTierUploadTimestamp ?: 0,
|
||||
key = this.locatorInfo.key.toByteArray(),
|
||||
cdnKey = this.locatorInfo.transitCdnKey?.nullIfBlank(),
|
||||
archiveCdn = this.locatorInfo.mediaTierCdnNumber,
|
||||
plaintextHash = this.locatorInfo.plaintextHash!!.toByteArray(),
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
stickerLocator = stickerLocator,
|
||||
gif = gif,
|
||||
quote = quote,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid),
|
||||
fileName = fileName
|
||||
)
|
||||
}
|
||||
AttachmentType.TRANSIT -> {
|
||||
val signalAttachmentPointer = SignalServiceAttachmentPointer(
|
||||
cdnNumber = this.locatorInfo.transitCdnNumber ?: Cdn.CDN_0.cdnNumber,
|
||||
remoteId = SignalServiceAttachmentRemoteId.from(locatorInfo.transitCdnKey!!),
|
||||
contentType = contentType,
|
||||
key = this.locatorInfo.key.toByteArray(),
|
||||
size = Optional.ofNullable(locatorInfo.size),
|
||||
preview = Optional.empty(),
|
||||
width = this.width ?: 0,
|
||||
height = this.height ?: 0,
|
||||
digest = Optional.ofNullable(this.locatorInfo.encryptedDigest!!.toByteArray()),
|
||||
incrementalDigest = Optional.ofNullable(this.incrementalMac?.toByteArray()),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize ?: 0,
|
||||
fileName = Optional.ofNullable(fileName),
|
||||
voiceNote = voiceNote,
|
||||
isBorderless = borderless,
|
||||
isGif = gif,
|
||||
caption = Optional.ofNullable(this.caption),
|
||||
blurHash = Optional.ofNullable(this.blurHash),
|
||||
uploadTimestamp = this.locatorInfo.transitTierUploadTimestamp?.clampToValidBackupRange() ?: 0,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
PointerAttachment.forPointer(
|
||||
pointer = Optional.of(signalAttachmentPointer),
|
||||
stickerLocator = stickerLocator,
|
||||
transferState = if (wasDownloaded) AttachmentTable.TRANSFER_NEEDS_RESTORE else AttachmentTable.TRANSFER_PROGRESS_PENDING
|
||||
).orNull()
|
||||
}
|
||||
AttachmentType.INVALID -> {
|
||||
TombstoneAttachment(
|
||||
contentType = contentType,
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
fileName = this.fileName,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = quote,
|
||||
stickerLocator = stickerLocator,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,21 +194,17 @@ fun FilePointer.Builder.setLegacyLocators(attachment: DatabaseAttachment, mediaA
|
||||
}
|
||||
|
||||
fun DatabaseAttachment.toLocatorInfo(): FilePointer.LocatorInfo {
|
||||
if (this.remoteKey.isNullOrBlank() || this.remoteDigest == null || this.size == 0L) {
|
||||
return FilePointer.LocatorInfo()
|
||||
}
|
||||
val attachmentType = this.toRemoteAttachmentType()
|
||||
|
||||
if (this.transferState == AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE && this.archiveTransferState != AttachmentTable.ArchiveTransferState.FINISHED) {
|
||||
if (attachmentType == AttachmentType.INVALID) {
|
||||
return FilePointer.LocatorInfo()
|
||||
}
|
||||
|
||||
val locatorBuilder = FilePointer.LocatorInfo.Builder()
|
||||
|
||||
val remoteKey = Base64.decode(this.remoteKey).toByteString()
|
||||
val archiveMediaName = this.getMediaName()?.toString()
|
||||
val remoteKey = Base64.decode(this.remoteKey!!).toByteString()
|
||||
|
||||
locatorBuilder.key = remoteKey
|
||||
locatorBuilder.digest = this.remoteDigest.toByteString()
|
||||
locatorBuilder.size = this.size.toInt()
|
||||
|
||||
if (this.remoteLocation.isNotNullOrBlank()) {
|
||||
@@ -215,8 +213,17 @@ fun DatabaseAttachment.toLocatorInfo(): FilePointer.LocatorInfo {
|
||||
locatorBuilder.transitTierUploadTimestamp = this.uploadTimestamp.takeIf { it > 0 }?.clampToValidBackupRange()
|
||||
}
|
||||
|
||||
locatorBuilder.mediaTierCdnNumber = this.archiveCdn?.takeIf { archiveMediaName != null }
|
||||
locatorBuilder.mediaName = archiveMediaName.emptyIfNull()
|
||||
@Suppress("KotlinConstantConditions")
|
||||
when (attachmentType) {
|
||||
AttachmentType.ARCHIVE -> {
|
||||
locatorBuilder.plaintextHash = Base64.decode(this.dataHash!!).toByteString()
|
||||
locatorBuilder.mediaTierCdnNumber = this.archiveCdn
|
||||
}
|
||||
AttachmentType.TRANSIT -> {
|
||||
locatorBuilder.encryptedDigest = this.remoteDigest!!.toByteString()
|
||||
}
|
||||
AttachmentType.INVALID -> Unit
|
||||
}
|
||||
|
||||
return locatorBuilder.build()
|
||||
}
|
||||
@@ -260,3 +267,30 @@ fun RemoteAvatarColor.toLocal(): AvatarColor {
|
||||
RemoteAvatarColor.A210 -> AvatarColor.A210
|
||||
}
|
||||
}
|
||||
|
||||
private fun DatabaseAttachment.toRemoteAttachmentType(): AttachmentType {
|
||||
if (this.remoteKey.isNullOrBlank()) {
|
||||
return AttachmentType.INVALID
|
||||
}
|
||||
|
||||
if (this.transferState == AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE && this.archiveTransferState != AttachmentTable.ArchiveTransferState.FINISHED) {
|
||||
return AttachmentType.INVALID
|
||||
}
|
||||
|
||||
val activelyOnArchiveCdn = this.archiveTransferState == AttachmentTable.ArchiveTransferState.FINISHED
|
||||
val couldBeOnArchiveCdn = this.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE && this.archiveTransferState != AttachmentTable.ArchiveTransferState.PERMANENT_FAILURE
|
||||
|
||||
if (this.dataHash != null && (activelyOnArchiveCdn || couldBeOnArchiveCdn)) {
|
||||
return AttachmentType.ARCHIVE
|
||||
}
|
||||
|
||||
if (this.remoteDigest != null && this.remoteLocation.isNotNullOrBlank()) {
|
||||
return AttachmentType.TRANSIT
|
||||
}
|
||||
|
||||
return AttachmentType.INVALID
|
||||
}
|
||||
|
||||
private enum class AttachmentType {
|
||||
TRANSIT, ARCHIVE, INVALID
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user