Store retrieved storage manifest locally

This commit is contained in:
AsamK 2022-05-24 21:07:40 +02:00
parent 87016f6ecf
commit b292de8ae6
2 changed files with 58 additions and 8 deletions

View file

@ -21,10 +21,13 @@ import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
public class StorageHelper { public class StorageHelper {
@ -56,11 +59,19 @@ public class StorageHelper {
return; 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<SignalStorageManifest> 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)) { for (final var record : getSignalStorageRecords(storageIds)) {
logger.debug("Reading record of type {}", record.getType());
if (record.getType() == ManifestRecord.Identifier.Type.ACCOUNT_VALUE) { if (record.getType() == ManifestRecord.Identifier.Type.ACCOUNT_VALUE) {
readAccountRecord(record); readAccountRecord(record);
} else if (record.getType() == ManifestRecord.Identifier.Type.GROUPV2_VALUE) { } else if (record.getType() == ManifestRecord.Identifier.Type.GROUPV2_VALUE) {
@ -71,6 +82,8 @@ public class StorageHelper {
readContactRecord(record); readContactRecord(record);
} }
} }
account.setStorageManifestVersion(manifest.get().getVersion());
account.setStorageManifest(manifest.get());
logger.debug("Done reading data from remote storage"); logger.debug("Done reading data from remote storage");
} }
@ -86,10 +99,12 @@ public class StorageHelper {
final var contact = account.getContactStore().getContact(recipientId); final var contact = account.getContactStore().getContact(recipientId);
final var blocked = contact != null && contact.isBlocked(); final var blocked = contact != null && contact.isBlocked();
final var profileShared = contact != null && contact.isProfileSharingEnabled(); final var profileShared = contact != null && contact.isProfileSharingEnabled();
if (contactRecord.getGivenName().isPresent() final var givenName = contact == null ? null : contact.getGivenName();
|| contactRecord.getFamilyName().isPresent() final var familyName = contact == null ? null : contact.getFamilyName();
|| blocked != contactRecord.isBlocked() if ((contactRecord.getGivenName().isPresent() && !contactRecord.getGivenName().get().equals(givenName)) || (
|| profileShared != contactRecord.isProfileSharingEnabled()) { 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 contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked()) final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked())
.withGivenName(contactRecord.getGivenName().orElse(null)) .withGivenName(contactRecord.getGivenName().orElse(null))
@ -101,6 +116,7 @@ public class StorageHelper {
if (contactRecord.getProfileKey().isPresent()) { if (contactRecord.getProfileKey().isPresent()) {
try { try {
logger.trace("Storing profile key {}", recipientId);
final var profileKey = new ProfileKey(contactRecord.getProfileKey().get()); final var profileKey = new ProfileKey(contactRecord.getProfileKey().get());
account.getProfileStore().storeProfileKey(recipientId, profileKey); account.getProfileStore().storeProfileKey(recipientId, profileKey);
} catch (InvalidInputException e) { } catch (InvalidInputException e) {
@ -109,6 +125,7 @@ public class StorageHelper {
} }
if (contactRecord.getIdentityKey().isPresent()) { if (contactRecord.getIdentityKey().isPresent()) {
try { try {
logger.trace("Storing identity key {}", recipientId);
final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get()); final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get());
account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date());
@ -239,10 +256,11 @@ public class StorageHelper {
return records.size() > 0 ? records.get(0) : null; return records.size() > 0 ? records.get(0) : null;
} }
private List<SignalStorageRecord> getSignalStorageRecords(final List<StorageId> storageIds) throws IOException { private List<SignalStorageRecord> getSignalStorageRecords(final Collection<StorageId> storageIds) throws IOException {
List<SignalStorageRecord> records; List<SignalStorageRecord> records;
try { try {
records = dependencies.getAccountManager().readStorageRecords(account.getStorageKey(), storageIds); records = dependencies.getAccountManager()
.readStorageRecords(account.getStorageKey(), new ArrayList<>(storageIds));
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
logger.warn("Failed to read storage records, ignoring."); logger.warn("Failed to read storage records, ignoring.");
return List.of(); return List.of();

View file

@ -59,6 +59,7 @@ import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.ServiceIdType;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; 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.storage.StorageKey;
import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
@ -67,6 +68,8 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.channels.Channels; import java.nio.channels.Channels;
@ -81,6 +84,7 @@ import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
public class SignalAccount implements Closeable { public class SignalAccount implements Closeable {
@ -467,6 +471,10 @@ public class SignalAccount implements Closeable {
return new File(getUserPath(dataPath, account), "recipients-store"); 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) { private static File getDatabaseFile(File dataPath, String account) {
return new File(getUserPath(dataPath, account), "account.db"); return new File(getUserPath(dataPath, account), "account.db");
} }
@ -1274,6 +1282,30 @@ public class SignalAccount implements Closeable {
save(); save();
} }
public Optional<SignalStorageManifest> 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() { public ProfileKey getProfileKey() {
return profileKey; return profileKey;
} }