From dcdfe0b762d5adea7c975b35799503f826dd163c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 11 Nov 2024 11:38:41 -0500 Subject: [PATCH] Simplify SignalStorageRecord. --- ...TableTest_applyStorageSyncContactUpdate.kt | 3 +- .../database/UnknownStorageIdTable.java | 2 +- .../jobs/StorageAccountRestoreJob.java | 4 +- .../securesms/jobs/StorageSyncJob.kt | 30 ++-- .../storage/AccountRecordProcessor.kt | 7 +- .../storage/ContactRecordProcessor.java | 2 +- .../storage/GroupV1RecordProcessor.kt | 3 +- .../storage/GroupV2RecordProcessor.kt | 3 +- .../securesms/storage/StorageSyncHelper.kt | 5 +- .../securesms/storage/StorageSyncModels.kt | 13 +- .../storage/StorageSyncValidations.java | 14 +- .../StoryDistributionListRecordProcessor.kt | 11 +- .../securesms/StorageServicePlugin.kt | 24 +-- .../storage/StorageSyncHelperTest.java | 14 -- .../api/storage/AccountRecordExtensions.kt | 4 - .../api/storage/SignalAccountRecord.kt | 4 - .../api/storage/SignalCallLinkRecord.kt | 4 - .../api/storage/SignalContactRecord.java | 5 - .../api/storage/SignalGroupV1Record.java | 5 - .../api/storage/SignalGroupV2Record.java | 5 - .../signalservice/api/storage/SignalRecord.kt | 1 - .../api/storage/SignalStorageModels.java | 94 ------------ .../api/storage/SignalStorageModels.kt | 96 ++++++++++++ .../api/storage/SignalStorageRecord.java | 145 ------------------ .../api/storage/SignalStorageRecord.kt | 21 +++ .../SignalStoryDistributionListRecord.java | 5 - .../api/storage/StorageRecordConverters.kt | 66 ++++++++ 27 files changed, 249 insertions(+), 341 deletions(-) delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageRecordConverters.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest_applyStorageSyncContactUpdate.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest_applyStorageSyncContactUpdate.kt index e7ef1645e4..b21be1a9ca 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest_applyStorageSyncContactUpdate.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientTableTest_applyStorageSyncContactUpdate.kt @@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.testing.SignalActivityRule import org.thoughtcrime.securesms.testing.assertIs import org.thoughtcrime.securesms.util.MessageTableTestUtils import org.whispersystems.signalservice.api.storage.SignalContactRecord +import org.whispersystems.signalservice.api.storage.toSignalContactRecord import org.whispersystems.signalservice.internal.storage.protos.ContactRecord @Suppress("ClassName") @@ -34,7 +35,7 @@ class RecipientTableTest_applyStorageSyncContactUpdate { MmsHelper.insert(recipient = other) identities.setVerified(other.id, harness.othersKeys[0].publicKey, IdentityTable.VerifiedStatus.VERIFIED) - val oldRecord: SignalContactRecord = StorageSyncModels.localToRemoteRecord(SignalDatabase.recipients.getRecordForSync(harness.others[0])!!).contact.get() + val oldRecord: SignalContactRecord = StorageSyncModels.localToRemoteRecord(SignalDatabase.recipients.getRecordForSync(harness.others[0])!!).let { it.proto.contact!!.toSignalContactRecord(it.id) } val newProto = oldRecord .proto diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/UnknownStorageIdTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/UnknownStorageIdTable.java index 5cc50a28a4..4059b85d82 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/UnknownStorageIdTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/UnknownStorageIdTable.java @@ -116,7 +116,7 @@ public class UnknownStorageIdTable extends DatabaseTable { for (SignalStorageRecord insert : inserts) { ContentValues values = new ContentValues(); - values.put(TYPE, insert.getType()); + values.put(TYPE, insert.getId().getType()); values.put(STORAGE_ID, Base64.encodeWithPadding(insert.getId().getRaw())); db.insert(TABLE_NAME, null, values); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java index f83d0e27dd..904932acaf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java @@ -96,12 +96,12 @@ public class StorageAccountRestoreJob extends BaseJob { return; } - SignalAccountRecord accountRecord = record.getAccount().orElse(null); - if (accountRecord == null) { + if (record.getProto().account == null) { Log.w(TAG, "The storage record didn't actually have an account on it! Not restoring."); return; } + SignalAccountRecord accountRecord = new SignalAccountRecord(record.getId(), record.getProto().account); Log.i(TAG, "Applying changes locally..."); SignalDatabase.getRawDatabase().beginTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt index 7ec7c5b0f9..e63759d737 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt @@ -37,6 +37,12 @@ import org.whispersystems.signalservice.api.storage.SignalStorageManifest import org.whispersystems.signalservice.api.storage.SignalStorageRecord import org.whispersystems.signalservice.api.storage.SignalStoryDistributionListRecord import org.whispersystems.signalservice.api.storage.StorageId +import org.whispersystems.signalservice.api.storage.toSignalAccountRecord +import org.whispersystems.signalservice.api.storage.toSignalCallLinkRecord +import org.whispersystems.signalservice.api.storage.toSignalContactRecord +import org.whispersystems.signalservice.api.storage.toSignalGroupV1Record +import org.whispersystems.signalservice.api.storage.toSignalGroupV2Record +import org.whispersystems.signalservice.api.storage.toSignalStoryDistributionListRecord import org.whispersystems.signalservice.internal.push.SyncMessage import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord import java.io.IOException @@ -475,18 +481,18 @@ class StorageSyncJob private constructor(parameters: Parameters) : BaseJob(param init { for (record in records) { - if (record.contact.isPresent) { - contacts += record.contact.get() - } else if (record.groupV1.isPresent) { - gv1 += record.groupV1.get() - } else if (record.groupV2.isPresent) { - gv2 += record.groupV2.get() - } else if (record.account.isPresent) { - account += record.account.get() - } else if (record.storyDistributionList.isPresent) { - storyDistributionLists += record.storyDistributionList.get() - } else if (record.callLink.isPresent) { - callLinkRecords += record.callLink.get() + if (record.proto.contact != null) { + contacts += record.proto.contact!!.toSignalContactRecord(record.id) + } else if (record.proto.groupV1 != null) { + gv1 += record.proto.groupV1!!.toSignalGroupV1Record(record.id) + } else if (record.proto.groupV2 != null) { + gv2 += record.proto.groupV2!!.toSignalGroupV2Record(record.id) + } else if (record.proto.account != null) { + account += record.proto.account!!.toSignalAccountRecord(record.id) + } else if (record.proto.storyDistributionList != null) { + storyDistributionLists += record.proto.storyDistributionList!!.toSignalStoryDistributionListRecord(record.id) + } else if (record.proto.callLink != null) { + callLinkRecords += record.proto.callLink!!.toSignalCallLinkRecord(record.id) } else if (record.id.isUnknown) { unknown += record } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt index e38f0a12ef..f8558c174a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.kt @@ -8,7 +8,6 @@ import org.signal.core.util.nullIfEmpty import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.storage.StorageSyncHelper.applyAccountStorageSyncUpdates -import org.thoughtcrime.securesms.storage.StorageSyncHelper.buildAccountRecord import org.whispersystems.signalservice.api.storage.SignalAccountRecord import org.whispersystems.signalservice.api.storage.StorageId import org.whispersystems.signalservice.api.storage.safeSetBackupsSubscriber @@ -35,7 +34,11 @@ class AccountRecordProcessor( private var foundAccountRecord = false - constructor(context: Context, self: Recipient) : this(context, self, buildAccountRecord(context, self).account.get()) + constructor(context: Context, self: Recipient) : this( + context = context, + self = self, + localAccountRecord = StorageSyncHelper.buildAccountRecord(context, self).let { it.proto.account!!.toSignalAccountRecord(it.id) } + ) /** * We want to catch: diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java index 3161c8e0d4..2ab644067c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java @@ -138,7 +138,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor r.getContact().get()); + .map(r -> new SignalContactRecord(r.getId(), r.getProto().contact)); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1RecordProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1RecordProcessor.kt index 91a2d341c4..de73bb5ce0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1RecordProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1RecordProcessor.kt @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.groups.BadGroupIdException import org.thoughtcrime.securesms.groups.GroupId import org.whispersystems.signalservice.api.storage.SignalGroupV1Record import org.whispersystems.signalservice.api.storage.SignalStorageRecord +import org.whispersystems.signalservice.api.storage.toSignalGroupV1Record import java.util.Optional /** @@ -53,7 +54,7 @@ class GroupV1RecordProcessor(private val groupDatabase: GroupTable, private val return recipientId .map { recipientTable.getRecordForSync(it)!! } .map { settings: RecipientRecord -> StorageSyncModels.localToRemoteRecord(settings) } - .map { record: SignalStorageRecord -> record.groupV1.get() } + .map { record: SignalStorageRecord -> record.proto.groupV1!!.toSignalGroupV1Record(record.id) } } override fun merge(remote: SignalGroupV1Record, local: SignalGroupV1Record, keyGenerator: StorageKeyGenerator): SignalGroupV1Record { diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV2RecordProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV2RecordProcessor.kt index 0c1be8ec8c..4c552cf518 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV2RecordProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV2RecordProcessor.kt @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.database.model.RecipientRecord import org.thoughtcrime.securesms.groups.GroupId import org.whispersystems.signalservice.api.storage.SignalGroupV2Record import org.whispersystems.signalservice.api.storage.SignalStorageRecord +import org.whispersystems.signalservice.api.storage.toSignalGroupV2Record import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record import java.util.Optional @@ -39,7 +40,7 @@ class GroupV2RecordProcessor(private val recipientTable: RecipientTable, private StorageSyncModels.localToRemoteRecord(settings, remote.masterKeyOrThrow) } } - .map { record: SignalStorageRecord -> record.groupV2.get() } + .map { record: SignalStorageRecord -> record.proto.groupV2!!.toSignalGroupV2Record(record.id) } } override fun merge(remote: SignalGroupV2Record, local: SignalGroupV2Record, keyGenerator: StorageKeyGenerator): SignalGroupV2Record { diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt index 981e3a0257..bb0b035704 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt @@ -33,6 +33,7 @@ import org.whispersystems.signalservice.api.storage.safeSetBackupsSubscriber import org.whispersystems.signalservice.api.storage.safeSetPayments import org.whispersystems.signalservice.api.storage.safeSetSubscriber import org.whispersystems.signalservice.api.storage.toSignalAccountRecord +import org.whispersystems.signalservice.api.storage.toSignalStorageRecord import org.whispersystems.signalservice.api.util.OptionalUtil.byteArrayEquals import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray @@ -182,12 +183,12 @@ object StorageSyncHelper { safeSetPayments(SignalStore.payments.mobileCoinPaymentsEnabled(), Optional.ofNullable(SignalStore.payments.paymentsEntropy).map { obj: Entropy -> obj.bytes }.orElse(null)) } - return SignalStorageRecord.forAccount(accountRecord.toSignalAccountRecord(StorageId.forAccount(storageId))) + return accountRecord.toSignalAccountRecord(StorageId.forAccount(storageId)).toSignalStorageRecord() } @JvmStatic fun applyAccountStorageSyncUpdates(context: Context, self: Recipient, updatedRecord: SignalAccountRecord, fetchProfile: Boolean) { - val localRecord = buildAccountRecord(context, self).account.get() + val localRecord = buildAccountRecord(context, self).let { it.proto.account!!.toSignalAccountRecord(it.id) } applyAccountStorageSyncUpdates(context, self, StorageRecordUpdate(localRecord, updatedRecord), fetchProfile) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.kt index 5debbdadae..0d66f34778 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncModels.kt @@ -25,6 +25,7 @@ import org.whispersystems.signalservice.api.storage.SignalGroupV1Record import org.whispersystems.signalservice.api.storage.SignalGroupV2Record import org.whispersystems.signalservice.api.storage.SignalStorageRecord import org.whispersystems.signalservice.api.storage.SignalStoryDistributionListRecord +import org.whispersystems.signalservice.api.storage.toSignalStorageRecord import org.whispersystems.signalservice.api.subscriptions.SubscriberId import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.internal.storage.protos.AccountRecord @@ -50,17 +51,17 @@ object StorageSyncModels { throw AssertionError("Must have a storage key!") } - return SignalStorageRecord.forGroupV2(localToRemoteGroupV2(settings, settings.storageId, groupMasterKey)) + return localToRemoteGroupV2(settings, settings.storageId, groupMasterKey).toSignalStorageRecord() } @JvmStatic fun localToRemoteRecord(settings: RecipientRecord, rawStorageId: ByteArray): SignalStorageRecord { return when (settings.recipientType) { - RecipientType.INDIVIDUAL -> SignalStorageRecord.forContact(localToRemoteContact(settings, rawStorageId)) - RecipientType.GV1 -> SignalStorageRecord.forGroupV1(localToRemoteGroupV1(settings, rawStorageId)) - RecipientType.GV2 -> SignalStorageRecord.forGroupV2(localToRemoteGroupV2(settings, rawStorageId, settings.syncExtras.groupMasterKey!!)) - RecipientType.DISTRIBUTION_LIST -> SignalStorageRecord.forStoryDistributionList(localToRemoteStoryDistributionList(settings, rawStorageId)) - RecipientType.CALL_LINK -> SignalStorageRecord.forCallLink(localToRemoteCallLink(settings, rawStorageId)) + RecipientType.INDIVIDUAL -> localToRemoteContact(settings, rawStorageId).toSignalStorageRecord() + RecipientType.GV1 -> localToRemoteGroupV1(settings, rawStorageId).toSignalStorageRecord() + RecipientType.GV2 -> localToRemoteGroupV2(settings, rawStorageId, settings.syncExtras.groupMasterKey!!).toSignalStorageRecord() + RecipientType.DISTRIBUTION_LIST -> localToRemoteStoryDistributionList(settings, rawStorageId).toSignalStorageRecord() + RecipientType.CALL_LINK -> localToRemoteCallLink(settings, rawStorageId).toSignalStorageRecord() else -> throw AssertionError("Unsupported type!") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncValidations.java b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncValidations.java index 1845e657fb..581aeb1baf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncValidations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncValidations.java @@ -9,10 +9,12 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; import org.signal.core.util.Base64; import org.signal.core.util.SetUtil; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.storage.SignalContactRecord; import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.SignalStorageRecord; import org.whispersystems.signalservice.api.storage.StorageId; +import org.whispersystems.signalservice.internal.storage.protos.ContactRecord; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; import java.nio.ByteBuffer; @@ -166,18 +168,18 @@ public final class StorageSyncValidations { throw new UnknownInsertError(); } - if (insert.getContact().isPresent()) { - SignalContactRecord contact = insert.getContact().get(); + if (insert.getProto().contact != null) { + ContactRecord contact = insert.getProto().contact; - if (self.requireAci().equals(contact.getAci().orElse(null)) || - self.requirePni().equals(contact.getPni().orElse(null)) || - self.requireE164().equals(contact.getNumber().orElse(""))) + if (self.requireAci().equals(ServiceId.ACI.parseOrNull(contact.aci)) || + self.requirePni().equals(ServiceId.PNI.parseOrNull(contact.pni)) || + self.requireE164().equals(contact.e164)) { throw new SelfAddedAsContactError(); } } - if (insert.getAccount().isPresent() && insert.getAccount().get().getProto().profileKey.size() == 0) { + if (insert.getProto().account != null && insert.getProto().account.profileKey.size() == 0) { Log.w(TAG, "Uploading a null profile key in our AccountRecord!"); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StoryDistributionListRecordProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/StoryDistributionListRecordProcessor.kt index ebc38a369f..964ccc99f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StoryDistributionListRecordProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StoryDistributionListRecordProcessor.kt @@ -7,6 +7,8 @@ import org.thoughtcrime.securesms.database.SignalDatabase import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.storage.SignalStoryDistributionListRecord +import org.whispersystems.signalservice.api.storage.toSignalStoryDistributionListRecord +import org.whispersystems.signalservice.api.util.OptionalUtil.asOptional import org.whispersystems.signalservice.api.util.UuidUtil import java.io.IOException import java.util.Optional @@ -78,14 +80,7 @@ class StoryDistributionListRecordProcessor : DefaultStorageRecordProcessor() - if (record.account.isPresent) { + if (record.proto.account != null) { row += "Account" - row += record.account.get().toProto().toString() - } else if (record.contact.isPresent) { + row += record.proto.account.toString() + } else if (record.proto.contact != null) { row += "Contact" - row += record.contact.get().toProto().toString() - } else if (record.groupV1.isPresent) { + row += record.proto.toString() + } else if (record.proto.groupV1 != null) { row += "GV1" - row += record.groupV1.get().toProto().toString() - } else if (record.groupV2.isPresent) { + row += record.proto.toString() + } else if (record.proto.groupV2 != null) { row += "GV2" - row += record.groupV2.get().toProto().toString() - } else if (record.storyDistributionList.isPresent) { + row += record.proto.toString() + } else if (record.proto.storyDistributionList != null) { row += "Distribution List" - row += record.storyDistributionList.get().toProto().toString() - } else if (record.callLink.isPresent) { + row += record.proto.toString() + } else if (record.proto.callLink != null) { row += "Call Link" - row += record.callLink.get().toProto().toString() + row += record.proto.callLink.toString() } else { row += "Unknown" row += "" diff --git a/app/src/test/java/org/thoughtcrime/securesms/storage/StorageSyncHelperTest.java b/app/src/test/java/org/thoughtcrime/securesms/storage/StorageSyncHelperTest.java index ab7145e2a7..528eb6633d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/storage/StorageSyncHelperTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/storage/StorageSyncHelperTest.java @@ -156,20 +156,6 @@ public final class StorageSyncHelperTest { assertTrue(StorageSyncHelper.profileKeyChanged(update(a, b))); } - private static SignalStorageRecord record(SignalRecord record) { - if (record instanceof SignalContactRecord) { - return SignalStorageRecord.forContact(record.getId(), (SignalContactRecord) record); - } else if (record instanceof SignalGroupV1Record) { - return SignalStorageRecord.forGroupV1(record.getId(), (SignalGroupV1Record) record); - } else if (record instanceof SignalGroupV2Record) { - return SignalStorageRecord.forGroupV2(record.getId(), (SignalGroupV2Record) record); - } else if (record instanceof SignalAccountRecord) { - return SignalStorageRecord.forAccount(record.getId(), (SignalAccountRecord) record); - } else { - return SignalStorageRecord.forUnknown(record.getId()); - } - } - private static SignalContactRecord.Builder contactBuilder(int key, ACI aci, String e164, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/AccountRecordExtensions.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/AccountRecordExtensions.kt index 03b8824d2c..15fbc29af1 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/AccountRecordExtensions.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/AccountRecordExtensions.kt @@ -53,10 +53,6 @@ fun AccountRecord.Builder.safeSetBackupsSubscriber(subscriberId: ByteString, sub return this } -fun AccountRecord.Builder.toSignalAccountRecord(storageId: StorageId): SignalAccountRecord { - return SignalAccountRecord(storageId, this.build()) -} - fun AccountRecord.PinnedConversation.Contact.toSignalServiceAddress(): SignalServiceAddress { val serviceId = ServiceId.parseOrNull(this.serviceId) return SignalServiceAddress(serviceId, this.e164) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.kt index acf6a4963c..3d2a7f84ef 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.kt @@ -31,10 +31,6 @@ class SignalAccountRecord( } } - override fun asStorageRecord(): SignalStorageRecord { - return SignalStorageRecord.forAccount(this) - } - fun serializeUnknownFields(): ByteArray? { return if (proto.hasUnknownFields()) proto.encode() else null } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalCallLinkRecord.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalCallLinkRecord.kt index c9e8b4868b..c2eb05dc39 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalCallLinkRecord.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalCallLinkRecord.kt @@ -21,10 +21,6 @@ class SignalCallLinkRecord( val adminPassKey: ByteArray = proto.adminPasskey.toByteArray() val deletionTimestamp: Long = proto.deletedAtTimestampMs - override fun asStorageRecord(): SignalStorageRecord { - return SignalStorageRecord.forCallLink(this) - } - fun isDeleted(): Boolean { return deletionTimestamp > 0 } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java index bde89f4c70..c036c4f1cf 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java @@ -74,11 +74,6 @@ public final class SignalContactRecord implements SignalRecord { return proto; } - @Override - public SignalStorageRecord asStorageRecord() { - return SignalStorageRecord.forContact(this); - } - public boolean hasUnknownFields() { return hasUnknownFields; } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java index b43aa08830..4f614d510b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java @@ -37,11 +37,6 @@ public final class SignalGroupV1Record implements SignalRecord { return proto; } - @Override - public SignalStorageRecord asStorageRecord() { - return SignalStorageRecord.forGroupV1(this); - } - public boolean hasUnknownFields() { return hasUnknownFields; } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java index 6506cbb060..68bd801e27 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java @@ -40,11 +40,6 @@ public final class SignalGroupV2Record implements SignalRecord { return proto; } - @Override - public SignalStorageRecord asStorageRecord() { - return SignalStorageRecord.forGroupV2(this); - } - public boolean hasUnknownFields() { return hasUnknownFields; } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalRecord.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalRecord.kt index dc12d9985e..98f3830aa5 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalRecord.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalRecord.kt @@ -6,7 +6,6 @@ import kotlin.reflect.full.memberProperties interface SignalRecord { val id: StorageId val proto: E - fun asStorageRecord(): SignalStorageRecord fun describeDiff(other: SignalRecord<*>): String { if (this::class != other::class) { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java deleted file mode 100644 index 9cefaad638..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -import org.signal.core.util.ProtoUtil; -import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.logging.Log; -import org.signal.libsignal.zkgroup.groups.GroupMasterKey; -import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; -import org.whispersystems.signalservice.internal.storage.protos.StorageItem; -import org.whispersystems.signalservice.internal.storage.protos.StorageManifest; -import org.whispersystems.signalservice.internal.storage.protos.StorageRecord; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import okio.ByteString; - -public final class SignalStorageModels { - - private static final String TAG = SignalStorageModels.class.getSimpleName(); - - public static SignalStorageManifest remoteToLocalStorageManifest(StorageManifest manifest, StorageKey storageKey) throws IOException, InvalidKeyException { - byte[] rawRecord = SignalStorageCipher.decrypt(storageKey.deriveManifestKey(manifest.version), manifest.value_.toByteArray()); - ManifestRecord manifestRecord = ManifestRecord.ADAPTER.decode(rawRecord); - List ids = new ArrayList<>(manifestRecord.identifiers.size()); - - for (ManifestRecord.Identifier id : manifestRecord.identifiers) { - int typeValue = (id.type != ManifestRecord.Identifier.Type.UNKNOWN) ? id.type.getValue() - : ProtoUtil.getUnknownEnumValue(id, StorageRecordProtoUtil.STORAGE_ID_TYPE_TAG); - - ids.add(StorageId.forType(id.raw.toByteArray(), typeValue)); - } - - return new SignalStorageManifest(manifestRecord.version, manifestRecord.sourceDevice, ids); - } - - public static SignalStorageRecord remoteToLocalStorageRecord(StorageItem item, int type, StorageKey storageKey) throws IOException, InvalidKeyException { - byte[] key = item.key.toByteArray(); - byte[] rawRecord = SignalStorageCipher.decrypt(storageKey.deriveItemKey(key), item.value_.toByteArray()); - StorageRecord record = StorageRecord.ADAPTER.decode(rawRecord); - StorageId id = StorageId.forType(key, type); - - if (record.contact != null && type == ManifestRecord.Identifier.Type.CONTACT.getValue()) { - return SignalStorageRecord.forContact(id, new SignalContactRecord(id, record.contact)); - } else if (record.groupV1 != null && type == ManifestRecord.Identifier.Type.GROUPV1.getValue()) { - return SignalStorageRecord.forGroupV1(id, new SignalGroupV1Record(id, record.groupV1)); - } else if (record.groupV2 != null && type == ManifestRecord.Identifier.Type.GROUPV2.getValue() && record.groupV2.masterKey.size() == GroupMasterKey.SIZE) { - return SignalStorageRecord.forGroupV2(id, new SignalGroupV2Record(id, record.groupV2)); - } else if (record.account != null && type == ManifestRecord.Identifier.Type.ACCOUNT.getValue()) { - return SignalStorageRecord.forAccount(id, new SignalAccountRecord(id, record.account)); - } else if (record.storyDistributionList != null && type == ManifestRecord.Identifier.Type.STORY_DISTRIBUTION_LIST.getValue()) { - return SignalStorageRecord.forStoryDistributionList(id, new SignalStoryDistributionListRecord(id, record.storyDistributionList)); - } else if (record.callLink != null && type == ManifestRecord.Identifier.Type.CALL_LINK.getValue()) { - return SignalStorageRecord.forCallLink(id, new SignalCallLinkRecord(id, record.callLink)); - }else { - if (StorageId.isKnownType(type)) { - Log.w(TAG, "StorageId is of known type (" + type + "), but the data is bad! Falling back to unknown."); - } - return SignalStorageRecord.forUnknown(StorageId.forType(key, type)); - } - } - - public static StorageItem localToRemoteStorageRecord(SignalStorageRecord record, StorageKey storageKey) { - StorageRecord.Builder builder = new StorageRecord.Builder(); - - if (record.getContact().isPresent()) { - builder.contact(record.getContact().get().getProto()); - } else if (record.getGroupV1().isPresent()) { - builder.groupV1(record.getGroupV1().get().getProto()); - } else if (record.getGroupV2().isPresent()) { - builder.groupV2(record.getGroupV2().get().getProto()); - } else if (record.getAccount().isPresent()) { - builder.account(record.getAccount().get().getProto()); - } else if (record.getStoryDistributionList().isPresent()) { - builder.storyDistributionList(record.getStoryDistributionList().get().getProto()); - } else if (record.getCallLink().isPresent()) { - builder.callLink(record.getCallLink().get().getProto()); - } else { - throw new InvalidStorageWriteError(); - } - - StorageRecord remoteRecord = builder.build(); - StorageItemKey itemKey = storageKey.deriveItemKey(record.getId().getRaw()); - byte[] encryptedRecord = SignalStorageCipher.encrypt(itemKey, remoteRecord.encode()); - - return new StorageItem.Builder() - .key(ByteString.of(record.getId().getRaw())) - .value_(ByteString.of(encryptedRecord)) - .build(); - } - - private static class InvalidStorageWriteError extends Error { - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.kt new file mode 100644 index 0000000000..50d2f16b33 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.kt @@ -0,0 +1,96 @@ +package org.whispersystems.signalservice.api.storage + +import okio.ByteString.Companion.toByteString +import org.signal.core.util.getUnknownEnumValue +import org.signal.libsignal.protocol.InvalidKeyException +import org.signal.libsignal.protocol.logging.Log +import org.signal.libsignal.zkgroup.groups.GroupMasterKey +import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord +import org.whispersystems.signalservice.internal.storage.protos.StorageItem +import org.whispersystems.signalservice.internal.storage.protos.StorageManifest +import org.whispersystems.signalservice.internal.storage.protos.StorageRecord +import java.io.IOException + +object SignalStorageModels { + private val TAG: String = SignalStorageModels::class.java.simpleName + + @JvmStatic + @Throws(IOException::class, InvalidKeyException::class) + fun remoteToLocalStorageManifest(manifest: StorageManifest, storageKey: StorageKey): SignalStorageManifest { + val rawRecord = SignalStorageCipher.decrypt(storageKey.deriveManifestKey(manifest.version), manifest.value_.toByteArray()) + val manifestRecord = ManifestRecord.ADAPTER.decode(rawRecord) + val ids: MutableList = ArrayList(manifestRecord.identifiers.size) + + for (id in manifestRecord.identifiers) { + val typeValue = if ((id.type != ManifestRecord.Identifier.Type.UNKNOWN)) { + id.type.value + } else { + id.getUnknownEnumValue(StorageRecordProtoUtil.STORAGE_ID_TYPE_TAG) + } + + ids.add(StorageId.forType(id.raw.toByteArray(), typeValue)) + } + + return SignalStorageManifest(manifestRecord.version, manifestRecord.sourceDevice, ids) + } + + @JvmStatic + @Throws(IOException::class, InvalidKeyException::class) + fun remoteToLocalStorageRecord(item: StorageItem, type: Int, storageKey: StorageKey): SignalStorageRecord { + val key = item.key.toByteArray() + val rawRecord = SignalStorageCipher.decrypt(storageKey.deriveItemKey(key), item.value_.toByteArray()) + val record = StorageRecord.ADAPTER.decode(rawRecord) + val id = StorageId.forType(key, type) + + if (record.contact != null && type == ManifestRecord.Identifier.Type.CONTACT.value) { + return SignalContactRecord(id, record.contact).toSignalStorageRecord() + } else if (record.groupV1 != null && type == ManifestRecord.Identifier.Type.GROUPV1.value) { + return SignalGroupV1Record(id, record.groupV1).toSignalStorageRecord() + } else if (record.groupV2 != null && type == ManifestRecord.Identifier.Type.GROUPV2.value && record.groupV2.masterKey.size == GroupMasterKey.SIZE) { + return SignalGroupV2Record(id, record.groupV2).toSignalStorageRecord() + } else if (record.account != null && type == ManifestRecord.Identifier.Type.ACCOUNT.value) { + return SignalAccountRecord(id, record.account).toSignalStorageRecord() + } else if (record.storyDistributionList != null && type == ManifestRecord.Identifier.Type.STORY_DISTRIBUTION_LIST.value) { + return SignalStoryDistributionListRecord(id, record.storyDistributionList).toSignalStorageRecord() + } else if (record.callLink != null && type == ManifestRecord.Identifier.Type.CALL_LINK.value) { + return SignalCallLinkRecord(id, record.callLink).toSignalStorageRecord() + } else { + if (StorageId.isKnownType(type)) { + Log.w(TAG, "StorageId is of known type ($type), but the data is bad! Falling back to unknown.") + } + return SignalStorageRecord.forUnknown(StorageId.forType(key, type)) + } + } + + @JvmStatic + fun localToRemoteStorageRecord(record: SignalStorageRecord, storageKey: StorageKey): StorageItem { + val builder = StorageRecord.Builder() + + if (record.proto.contact != null) { + builder.contact(record.proto.contact) + } else if (record.proto.groupV1 != null) { + builder.groupV1(record.proto.groupV1) + } else if (record.proto.groupV2 != null) { + builder.groupV2(record.proto.groupV2) + } else if (record.proto.account != null) { + builder.account(record.proto.account) + } else if (record.proto.storyDistributionList != null) { + builder.storyDistributionList(record.proto.storyDistributionList) + } else if (record.proto.callLink != null) { + builder.callLink(record.proto.callLink) + } else { + throw InvalidStorageWriteError() + } + + val remoteRecord = builder.build() + val itemKey = storageKey.deriveItemKey(record.id.raw) + val encryptedRecord = SignalStorageCipher.encrypt(itemKey, remoteRecord.encode()) + + return StorageItem.Builder() + .key(record.id.raw.toByteString()) + .value_(encryptedRecord.toByteString()) + .build() + } + + private class InvalidStorageWriteError : Error() +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.java deleted file mode 100644 index 7ad71cbe8d..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - - -import org.jetbrains.annotations.NotNull; -import org.whispersystems.signalservice.internal.storage.protos.CallLinkRecord; - -import java.util.Objects; -import java.util.Optional; - -public class SignalStorageRecord { - - private final StorageId id; - private final Optional storyDistributionList; - private final Optional contact; - private final Optional groupV1; - private final Optional groupV2; - private final Optional account; - private final Optional callLink; - - public static SignalStorageRecord forStoryDistributionList(SignalStoryDistributionListRecord storyDistributionList) { - return forStoryDistributionList(storyDistributionList.getId(), storyDistributionList); - } - - public static SignalStorageRecord forStoryDistributionList(StorageId key, SignalStoryDistributionListRecord storyDistributionList) { - return new SignalStorageRecord(key, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(storyDistributionList), Optional.empty()); - } - - public static SignalStorageRecord forContact(SignalContactRecord contact) { - return forContact(contact.getId(), contact); - } - - public static SignalStorageRecord forContact(StorageId key, SignalContactRecord contact) { - return new SignalStorageRecord(key, Optional.of(contact), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); - } - - public static SignalStorageRecord forGroupV1(SignalGroupV1Record groupV1) { - return forGroupV1(groupV1.getId(), groupV1); - } - - public static SignalStorageRecord forGroupV1(StorageId key, SignalGroupV1Record groupV1) { - return new SignalStorageRecord(key, Optional.empty(), Optional.of(groupV1), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); - } - - public static SignalStorageRecord forGroupV2(SignalGroupV2Record groupV2) { - return forGroupV2(groupV2.getId(), groupV2); - } - - public static SignalStorageRecord forGroupV2(StorageId key, SignalGroupV2Record groupV2) { - return new SignalStorageRecord(key, Optional.empty(), Optional.empty(), Optional.of(groupV2), Optional.empty(), Optional.empty(), Optional.empty()); - } - - public static SignalStorageRecord forAccount(SignalAccountRecord account) { - return forAccount(account.getId(), account); - } - - public static SignalStorageRecord forAccount(StorageId key, SignalAccountRecord account) { - return new SignalStorageRecord(key, Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(account), Optional.empty(), Optional.empty()); - } - - @NotNull - public static SignalStorageRecord forCallLink(@NotNull SignalCallLinkRecord callLink) { - return forCallLink(callLink.getId(), callLink); - } - - @NotNull - public static SignalStorageRecord forCallLink(StorageId key, @NotNull SignalCallLinkRecord callLink) { - return new SignalStorageRecord(key, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(callLink)); - } - - public static SignalStorageRecord forUnknown(StorageId key) { - return new SignalStorageRecord(key, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); - } - - - private SignalStorageRecord(StorageId id, - Optional contact, - Optional groupV1, - Optional groupV2, - Optional account, - Optional storyDistributionList, - Optional callLink) - { - this.id = id; - this.contact = contact; - this.groupV1 = groupV1; - this.groupV2 = groupV2; - this.account = account; - this.storyDistributionList = storyDistributionList; - this.callLink = callLink; - } - - public StorageId getId() { - return id; - } - - public int getType() { - return id.getType(); - } - - public Optional getContact() { - return contact; - } - - public Optional getGroupV1() { - return groupV1; - } - - public Optional getGroupV2() { - return groupV2; - } - - public Optional getAccount() { - return account; - } - - public Optional getStoryDistributionList() { - return storyDistributionList; - } - - public Optional getCallLink() { - return callLink; - } - - public boolean isUnknown() { - return !contact.isPresent() && !groupV1.isPresent() && !groupV2.isPresent() && !account.isPresent() && !storyDistributionList.isPresent() && !callLink.isPresent(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SignalStorageRecord that = (SignalStorageRecord) o; - return Objects.equals(id, that.id) && - Objects.equals(contact, that.contact) && - Objects.equals(groupV1, that.groupV1) && - Objects.equals(groupV2, that.groupV2) && - Objects.equals(storyDistributionList, that.storyDistributionList) && - Objects.equals(callLink, that.callLink); - } - - @Override - public int hashCode() { - return Objects.hash(id, contact, groupV1, groupV2, storyDistributionList, callLink); - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.kt new file mode 100644 index 0000000000..48093a3a00 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageRecord.kt @@ -0,0 +1,21 @@ +package org.whispersystems.signalservice.api.storage + +import org.whispersystems.signalservice.internal.storage.protos.StorageRecord + +/** + * A wrapper around [StorageRecord] to pair it with a [StorageId]. + */ +data class SignalStorageRecord( + val id: StorageId, + val proto: StorageRecord +) { + val isUnknown: Boolean + get() = proto.contact == null && proto.groupV1 == null && proto.groupV2 == null && proto.account == null && proto.storyDistributionList == null && proto.callLink == null + + companion object { + @JvmStatic + fun forUnknown(key: StorageId): SignalStorageRecord { + return SignalStorageRecord(key, proto = StorageRecord()) + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java index 25c7904c2e..bbf073f265 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java @@ -47,11 +47,6 @@ public class SignalStoryDistributionListRecord implements SignalRecord