From: AsamK Date: Tue, 24 May 2022 19:07:40 +0000 (+0200) Subject: Store retrieved storage manifest locally X-Git-Tag: v0.10.7~10 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/b292de8ae6036208b4a780aae5ba1b105db57119?ds=inline Store retrieved storage manifest locally --- diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 426806ff..281901c1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -21,10 +21,13 @@ import org.whispersystems.signalservice.internal.storage.protos.AccountRecord; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; public class StorageHelper { @@ -56,11 +59,19 @@ public class StorageHelper { return; } - account.setStorageManifestVersion(manifest.get().getVersion()); + logger.trace("Remote storage manifest has {} records", manifest.get().getStorageIds().size()); + final var storageIds = manifest.get() + .getStorageIds() + .stream() + .filter(id -> !id.isUnknown()) + .collect(Collectors.toSet()); - final var storageIds = manifest.get().getStorageIds().stream().filter(id -> !id.isUnknown()).toList(); + Optional localManifest = account.getStorageManifest(); + localManifest.ifPresent(m -> m.getStorageIds().forEach(storageIds::remove)); + logger.trace("Reading {} new records", manifest.get().getStorageIds().size()); for (final var record : getSignalStorageRecords(storageIds)) { + logger.debug("Reading record of type {}", record.getType()); if (record.getType() == ManifestRecord.Identifier.Type.ACCOUNT_VALUE) { readAccountRecord(record); } else if (record.getType() == ManifestRecord.Identifier.Type.GROUPV2_VALUE) { @@ -71,6 +82,8 @@ public class StorageHelper { readContactRecord(record); } } + account.setStorageManifestVersion(manifest.get().getVersion()); + account.setStorageManifest(manifest.get()); logger.debug("Done reading data from remote storage"); } @@ -86,10 +99,12 @@ public class StorageHelper { final var contact = account.getContactStore().getContact(recipientId); final var blocked = contact != null && contact.isBlocked(); final var profileShared = contact != null && contact.isProfileSharingEnabled(); - if (contactRecord.getGivenName().isPresent() - || contactRecord.getFamilyName().isPresent() - || blocked != contactRecord.isBlocked() - || profileShared != contactRecord.isProfileSharingEnabled()) { + final var givenName = contact == null ? null : contact.getGivenName(); + final var familyName = contact == null ? null : contact.getFamilyName(); + if ((contactRecord.getGivenName().isPresent() && !contactRecord.getGivenName().get().equals(givenName)) || ( + contactRecord.getFamilyName().isPresent() && !contactRecord.getFamilyName().get().equals(familyName) + ) || blocked != contactRecord.isBlocked() || profileShared != contactRecord.isProfileSharingEnabled()) { + logger.debug("Storing new or updated contact {}", recipientId); final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked()) .withGivenName(contactRecord.getGivenName().orElse(null)) @@ -101,6 +116,7 @@ public class StorageHelper { if (contactRecord.getProfileKey().isPresent()) { try { + logger.trace("Storing profile key {}", recipientId); final var profileKey = new ProfileKey(contactRecord.getProfileKey().get()); account.getProfileStore().storeProfileKey(recipientId, profileKey); } catch (InvalidInputException e) { @@ -109,6 +125,7 @@ public class StorageHelper { } if (contactRecord.getIdentityKey().isPresent()) { try { + logger.trace("Storing identity key {}", recipientId); final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get()); account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); @@ -239,10 +256,11 @@ public class StorageHelper { return records.size() > 0 ? records.get(0) : null; } - private List getSignalStorageRecords(final List storageIds) throws IOException { + private List getSignalStorageRecords(final Collection storageIds) throws IOException { List records; try { - records = dependencies.getAccountManager().readStorageRecords(account.getStorageKey(), storageIds); + records = dependencies.getAccountManager() + .readStorageRecords(account.getStorageKey(), new ArrayList<>(storageIds)); } catch (InvalidKeyException e) { logger.warn("Failed to read storage records, ignoring."); return List.of(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 6d6bb12f..f66d3e4a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -59,6 +59,7 @@ import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.UuidUtil; @@ -67,6 +68,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.Channels; @@ -81,6 +84,7 @@ import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.function.Supplier; public class SignalAccount implements Closeable { @@ -467,6 +471,10 @@ public class SignalAccount implements Closeable { return new File(getUserPath(dataPath, account), "recipients-store"); } + private static File getStorageManifestFile(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "storage-manifest"); + } + private static File getDatabaseFile(File dataPath, String account) { return new File(getUserPath(dataPath, account), "account.db"); } @@ -1274,6 +1282,30 @@ public class SignalAccount implements Closeable { save(); } + public Optional getStorageManifest() { + final var storageManifestFile = getStorageManifestFile(dataPath, accountPath); + if (!storageManifestFile.exists()) { + return Optional.empty(); + } + try (var inputStream = new FileInputStream(storageManifestFile)) { + return Optional.of(SignalStorageManifest.deserialize(inputStream.readAllBytes())); + } catch (IOException e) { + logger.warn("Failed to read local storage manifest.", e); + return Optional.empty(); + } + } + + public void setStorageManifest(SignalStorageManifest manifest) { + final var manifestBytes = manifest.serialize(); + + final var storageManifestFile = getStorageManifestFile(dataPath, accountPath); + try (var outputStream = new FileOutputStream(storageManifestFile)) { + outputStream.write(manifestBytes); + } catch (IOException e) { + logger.error("Failed to store local storage manifest.", e); + } + } + public ProfileKey getProfileKey() { return profileKey; }