mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-26 14:09:58 +00:00
Add incremental digests to attachment sending.
This commit is contained in:
@@ -35,6 +35,9 @@ public abstract class Attachment {
|
||||
@Nullable
|
||||
private final byte[] digest;
|
||||
|
||||
@Nullable
|
||||
private final byte[] incrementalDigest;
|
||||
|
||||
@Nullable
|
||||
private final String fastPreflightId;
|
||||
|
||||
@@ -70,6 +73,7 @@ public abstract class Attachment {
|
||||
@Nullable String key,
|
||||
@Nullable String relay,
|
||||
@Nullable byte[] digest,
|
||||
@Nullable byte[] incrementalDigest,
|
||||
@Nullable String fastPreflightId,
|
||||
boolean voiceNote,
|
||||
boolean borderless,
|
||||
@@ -93,6 +97,7 @@ public abstract class Attachment {
|
||||
this.key = key;
|
||||
this.relay = relay;
|
||||
this.digest = digest;
|
||||
this.incrementalDigest = incrementalDigest;
|
||||
this.fastPreflightId = fastPreflightId;
|
||||
this.voiceNote = voiceNote;
|
||||
this.borderless = borderless;
|
||||
@@ -165,6 +170,11 @@ public abstract class Attachment {
|
||||
return digest;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public byte[] getIncrementalDigest() {
|
||||
return incrementalDigest;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFastPreflightId() {
|
||||
return fastPreflightId;
|
||||
|
||||
@@ -33,6 +33,7 @@ public class DatabaseAttachment extends Attachment {
|
||||
String key,
|
||||
String relay,
|
||||
byte[] digest,
|
||||
byte[] incrementalDigest,
|
||||
String fastPreflightId,
|
||||
boolean voiceNote,
|
||||
boolean borderless,
|
||||
@@ -48,7 +49,7 @@ public class DatabaseAttachment extends Attachment {
|
||||
int displayOrder,
|
||||
long uploadTimestamp)
|
||||
{
|
||||
super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, videoGif, width, height, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties);
|
||||
super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, incrementalDigest, fastPreflightId, voiceNote, borderless, videoGif, width, height, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties);
|
||||
this.attachmentId = attachmentId;
|
||||
this.hasData = hasData;
|
||||
this.hasThumbnail = hasThumbnail;
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.database.MessageTable;
|
||||
public class MmsNotificationAttachment extends Attachment {
|
||||
|
||||
public MmsNotificationAttachment(int status, long size) {
|
||||
super("application/mms", getTransferStateFromStatus(status), size, null, 0, null, null, null, null, null, false, false, false, 0, 0, false, 0, null, null, null, null, null);
|
||||
super("application/mms", getTransferStateFromStatus(status), size, null, 0, null, null, null, null, null, null, false, false, false, 0, 0, false, 0, null, null, null, null, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -30,6 +30,7 @@ public class PointerAttachment extends Attachment {
|
||||
@Nullable String key,
|
||||
@Nullable String relay,
|
||||
@Nullable byte[] digest,
|
||||
@Nullable byte[] incrementalDigest,
|
||||
@Nullable String fastPreflightId,
|
||||
boolean voiceNote,
|
||||
boolean borderless,
|
||||
@@ -41,7 +42,7 @@ public class PointerAttachment extends Attachment {
|
||||
@Nullable StickerLocator stickerLocator,
|
||||
@Nullable BlurHash blurHash)
|
||||
{
|
||||
super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, videoGif, width, height, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null);
|
||||
super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, incrementalDigest, fastPreflightId, voiceNote, borderless, videoGif, width, height, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -112,6 +113,7 @@ public class PointerAttachment extends Attachment {
|
||||
pointer.get().asPointer().getRemoteId().toString(),
|
||||
encodedKey, null,
|
||||
pointer.get().asPointer().getDigest().orElse(null),
|
||||
pointer.get().asPointer().getincrementalDigest().orElse(null),
|
||||
fastPreflightId,
|
||||
pointer.get().asPointer().getVoiceNote(),
|
||||
pointer.get().asPointer().isBorderless(),
|
||||
@@ -137,6 +139,7 @@ public class PointerAttachment extends Attachment {
|
||||
thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null,
|
||||
null,
|
||||
thumbnail != null ? thumbnail.asPointer().getDigest().orElse(null) : null,
|
||||
thumbnail != null ? thumbnail.asPointer().getincrementalDigest().orElse(null) : null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
@@ -166,6 +169,7 @@ public class PointerAttachment extends Attachment {
|
||||
thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null,
|
||||
null,
|
||||
thumbnail != null ? thumbnail.asPointer().getDigest().orElse(null) : null,
|
||||
thumbnail != null ? thumbnail.asPointer().getincrementalDigest().orElse(null) : null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.database.AttachmentTable;
|
||||
public class TombstoneAttachment extends Attachment {
|
||||
|
||||
public TombstoneAttachment(@NonNull String contentType, boolean quote) {
|
||||
super(contentType, AttachmentTable.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, false, false, false, 0, 0, quote, 0, null, null, null, null, null);
|
||||
super(contentType, AttachmentTable.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, null, false, false, false, 0, 0, quote, 0, null, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,7 +52,7 @@ public class UriAttachment extends Attachment {
|
||||
@Nullable AudioHash audioHash,
|
||||
@Nullable TransformProperties transformProperties)
|
||||
{
|
||||
super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, borderless, videoGif, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
|
||||
super(contentType, transferState, size, fileName, 0, null, null, null, null, null, fastPreflightId, voiceNote, borderless, videoGif, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
|
||||
this.dataUri = Objects.requireNonNull(dataUri);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
static final String DISPLAY_ORDER = "display_order";
|
||||
static final String UPLOAD_TIMESTAMP = "upload_timestamp";
|
||||
static final String CDN_NUMBER = "cdn_number";
|
||||
static final String MAC_DIGEST = "incremental_mac_digest";
|
||||
|
||||
private static final String DIRECTORY = "parts";
|
||||
|
||||
@@ -143,7 +144,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
private static final String[] PROJECTION = new String[] {ROW_ID,
|
||||
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
|
||||
CDN_NUMBER, CONTENT_LOCATION, DATA,
|
||||
TRANSFER_STATE, SIZE, FILE_NAME, UNIQUE_ID, DIGEST,
|
||||
TRANSFER_STATE, SIZE, FILE_NAME, UNIQUE_ID, DIGEST, MAC_DIGEST,
|
||||
FAST_PREFLIGHT_ID, VOICE_NOTE, BORDERLESS, VIDEO_GIF, QUOTE, DATA_RANDOM,
|
||||
WIDTH, HEIGHT, CAPTION, STICKER_PACK_ID,
|
||||
STICKER_PACK_KEY, STICKER_ID, STICKER_EMOJI, DATA_HASH, VISUAL_HASH,
|
||||
@@ -188,7 +189,8 @@ public class AttachmentTable extends DatabaseTable {
|
||||
TRANSFER_FILE + " TEXT DEFAULT NULL, " +
|
||||
DISPLAY_ORDER + " INTEGER DEFAULT 0, " +
|
||||
UPLOAD_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||
CDN_NUMBER + " INTEGER DEFAULT 0);";
|
||||
CDN_NUMBER + " INTEGER DEFAULT 0, " +
|
||||
MAC_DIGEST + " BLOB);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
|
||||
@@ -698,6 +700,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
contentValues.put(CDN_NUMBER, sourceAttachment.getCdnNumber());
|
||||
contentValues.put(CONTENT_LOCATION, sourceAttachment.getLocation());
|
||||
contentValues.put(DIGEST, sourceAttachment.getDigest());
|
||||
contentValues.put(MAC_DIGEST, sourceAttachment.getIncrementalDigest());
|
||||
contentValues.put(CONTENT_DISPOSITION, sourceAttachment.getKey());
|
||||
contentValues.put(NAME, sourceAttachment.getRelay());
|
||||
contentValues.put(SIZE, sourceAttachment.getSize());
|
||||
@@ -746,6 +749,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
values.put(CDN_NUMBER, attachment.getCdnNumber());
|
||||
values.put(CONTENT_LOCATION, attachment.getLocation());
|
||||
values.put(DIGEST, attachment.getDigest());
|
||||
values.put(MAC_DIGEST, attachment.getIncrementalDigest());
|
||||
values.put(CONTENT_DISPOSITION, attachment.getKey());
|
||||
values.put(NAME, attachment.getRelay());
|
||||
values.put(SIZE, attachment.getSize());
|
||||
@@ -1272,6 +1276,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
object.getString(CONTENT_DISPOSITION),
|
||||
object.getString(NAME),
|
||||
null,
|
||||
null,
|
||||
object.getString(FAST_PREFLIGHT_ID),
|
||||
object.getInt(VOICE_NOTE) == 1,
|
||||
object.getInt(BORDERLESS) == 1,
|
||||
@@ -1319,6 +1324,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
|
||||
cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)),
|
||||
cursor.getBlob(cursor.getColumnIndexOrThrow(MAC_DIGEST)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)),
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(VOICE_NOTE)) == 1,
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(BORDERLESS)) == 1,
|
||||
@@ -1385,6 +1391,7 @@ public class AttachmentTable extends DatabaseTable {
|
||||
contentValues.put(CDN_NUMBER, useTemplateUpload ? template.getCdnNumber() : attachment.getCdnNumber());
|
||||
contentValues.put(CONTENT_LOCATION, useTemplateUpload ? template.getLocation() : attachment.getLocation());
|
||||
contentValues.put(DIGEST, useTemplateUpload ? template.getDigest() : attachment.getDigest());
|
||||
contentValues.put(MAC_DIGEST, useTemplateUpload ? template.getIncrementalDigest() : attachment.getIncrementalDigest());
|
||||
contentValues.put(CONTENT_DISPOSITION, useTemplateUpload ? template.getKey() : attachment.getKey());
|
||||
contentValues.put(NAME, useTemplateUpload ? template.getRelay() : attachment.getRelay());
|
||||
contentValues.put(FILE_NAME, StorageUtil.getCleanFileName(attachment.getFileName()));
|
||||
|
||||
@@ -48,6 +48,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.CAPTION},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.NAME},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.UPLOAD_TIMESTAMP},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.MAC_DIGEST},
|
||||
${MessageTable.TABLE_NAME}.${MessageTable.TYPE},
|
||||
${MessageTable.TABLE_NAME}.${MessageTable.DATE_SENT},
|
||||
${MessageTable.TABLE_NAME}.${MessageTable.DATE_RECEIVED},
|
||||
@@ -55,7 +56,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD
|
||||
${MessageTable.TABLE_NAME}.${MessageTable.THREAD_ID},
|
||||
${MessageTable.TABLE_NAME}.${MessageTable.FROM_RECIPIENT_ID},
|
||||
${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} as $THREAD_RECIPIENT_ID
|
||||
FROM
|
||||
FROM
|
||||
${AttachmentTable.TABLE_NAME}
|
||||
LEFT JOIN ${MessageTable.TABLE_NAME} ON ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} = ${MessageTable.TABLE_NAME}.${MessageTable.ID}
|
||||
LEFT JOIN ${ThreadTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.ID} = ${MessageTable.TABLE_NAME}.${MessageTable.THREAD_ID}
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V194_KyberPreKeyMig
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V195_GroupMemberForeignKeyMigration
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V196_BackCallLinksWithRecipientV2
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V197_DropAvatarColorFromCallLinks
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V198_AddMacDigestColumn
|
||||
|
||||
/**
|
||||
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
|
||||
@@ -61,7 +62,7 @@ object SignalDatabaseMigrations {
|
||||
|
||||
val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass)
|
||||
|
||||
const val DATABASE_VERSION = 197
|
||||
const val DATABASE_VERSION = 198
|
||||
|
||||
@JvmStatic
|
||||
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
@@ -260,6 +261,10 @@ object SignalDatabaseMigrations {
|
||||
if (oldVersion < 197) {
|
||||
V197_DropAvatarColorFromCallLinks.migrate(context, db, oldVersion, newVersion)
|
||||
}
|
||||
|
||||
if (oldVersion < 198) {
|
||||
V198_AddMacDigestColumn.migrate(context, db, oldVersion, newVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.database.helpers.migration
|
||||
|
||||
import android.app.Application
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
|
||||
/**
|
||||
* New field migration.
|
||||
*/
|
||||
@Suppress("ClassName")
|
||||
object V198_AddMacDigestColumn : SignalDatabaseMigration {
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("ALTER TABLE part ADD COLUMN incremental_mac_digest BLOB")
|
||||
}
|
||||
}
|
||||
@@ -247,6 +247,7 @@ public final class AttachmentDownloadJob extends BaseJob {
|
||||
Optional.empty(),
|
||||
0, 0,
|
||||
Optional.ofNullable(attachment.getDigest()),
|
||||
Optional.ofNullable(attachment.getIncrementalDigest()),
|
||||
Optional.ofNullable(attachment.getFileName()),
|
||||
attachment.isVoiceNote(),
|
||||
attachment.isBorderless(),
|
||||
|
||||
@@ -86,7 +86,7 @@ public final class AvatarGroupsV1DownloadJob extends BaseJob {
|
||||
attachment.deleteOnExit();
|
||||
|
||||
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
||||
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId(avatarId), contentType, key, Optional.of(0), Optional.empty(), 0, 0, digest, fileName, false, false, false, Optional.empty(), Optional.empty(), System.currentTimeMillis());
|
||||
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId(avatarId), contentType, key, Optional.of(0), Optional.empty(), 0, 0, digest, Optional.empty(), fileName, false, false, false, Optional.empty(), Optional.empty(), System.currentTimeMillis());
|
||||
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, AvatarHelper.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE);
|
||||
|
||||
AvatarHelper.setAvatar(context, record.get().getRecipientId(), inputStream);
|
||||
|
||||
@@ -283,6 +283,7 @@ public abstract class PushSendJob extends SendJob {
|
||||
width,
|
||||
height,
|
||||
Optional.ofNullable(attachment.getDigest()),
|
||||
Optional.ofNullable(attachment.getIncrementalDigest()),
|
||||
Optional.ofNullable(attachment.getFileName()),
|
||||
attachment.isVoiceNote(),
|
||||
attachment.isBorderless(),
|
||||
|
||||
@@ -22,22 +22,24 @@ class AttachmentStreamLocalUriFetcher implements DataFetcher<InputStream> {
|
||||
private final File attachment;
|
||||
private final byte[] key;
|
||||
private final Optional<byte[]> digest;
|
||||
private final Optional<byte[]> incrementalDigest;
|
||||
private final long plaintextLength;
|
||||
|
||||
private InputStream is;
|
||||
|
||||
AttachmentStreamLocalUriFetcher(File attachment, long plaintextLength, byte[] key, Optional<byte[]> digest) {
|
||||
this.attachment = attachment;
|
||||
this.plaintextLength = plaintextLength;
|
||||
this.digest = digest;
|
||||
this.key = key;
|
||||
AttachmentStreamLocalUriFetcher(File attachment, long plaintextLength, byte[] key, Optional<byte[]> digest, Optional<byte[]> incrementalDigest) {
|
||||
this.attachment = attachment;
|
||||
this.plaintextLength = plaintextLength;
|
||||
this.digest = digest;
|
||||
this.incrementalDigest = incrementalDigest;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
|
||||
try {
|
||||
if (!digest.isPresent()) throw new InvalidMessageException("No attachment digest!");
|
||||
is = AttachmentCipherInputStream.createForAttachment(attachment, plaintextLength, key, digest.get());
|
||||
is = AttachmentCipherInputStream.createForAttachment(attachment, plaintextLength, key, digest.get(), incrementalDigest.get());
|
||||
callback.onDataReady(is);
|
||||
} catch (IOException | InvalidMessageException e) {
|
||||
callback.onLoadFailed(e);
|
||||
|
||||
@@ -20,7 +20,7 @@ public class AttachmentStreamUriLoader implements ModelLoader<AttachmentModel, I
|
||||
|
||||
@Override
|
||||
public @Nullable LoadData<InputStream> buildLoadData(@NonNull AttachmentModel attachmentModel, int width, int height, @NonNull Options options) {
|
||||
return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.plaintextLength, attachmentModel.key, attachmentModel.digest));
|
||||
return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.plaintextLength, attachmentModel.key, attachmentModel.digest, attachmentModel.incrementalDigest));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,15 +45,20 @@ public class AttachmentStreamUriLoader implements ModelLoader<AttachmentModel, I
|
||||
public @NonNull File attachment;
|
||||
public @NonNull byte[] key;
|
||||
public @NonNull Optional<byte[]> digest;
|
||||
public @NonNull Optional<byte[]> incrementalDigest;
|
||||
public long plaintextLength;
|
||||
|
||||
public AttachmentModel(@NonNull File attachment, @NonNull byte[] key,
|
||||
long plaintextLength, @NonNull Optional<byte[]> digest)
|
||||
public AttachmentModel(@NonNull File attachment,
|
||||
@NonNull byte[] key,
|
||||
long plaintextLength,
|
||||
@NonNull Optional<byte[]> digest,
|
||||
@NonNull Optional<byte[]> incrementalDigest)
|
||||
{
|
||||
this.attachment = attachment;
|
||||
this.key = key;
|
||||
this.digest = digest;
|
||||
this.plaintextLength = plaintextLength;
|
||||
this.attachment = attachment;
|
||||
this.key = key;
|
||||
this.digest = digest;
|
||||
this.incrementalDigest = incrementalDigest;
|
||||
this.plaintextLength = plaintextLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,6 +44,7 @@ object ReleaseChannel {
|
||||
mediaWidth,
|
||||
mediaHeight,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.of(media),
|
||||
false,
|
||||
false,
|
||||
|
||||
Reference in New Issue
Block a user