Create stores in SignalAccount lazily

This commit is contained in:
AsamK 2022-01-26 21:03:04 +01:00
parent e5537dc4db
commit 67146f9cc7
4 changed files with 134 additions and 94 deletions

View file

@ -66,6 +66,7 @@ import java.util.Base64;
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.function.Supplier;
public class SignalAccount implements Closeable { public class SignalAccount implements Closeable {
@ -74,13 +75,16 @@ public class SignalAccount implements Closeable {
private static final int MINIMUM_STORAGE_VERSION = 1; private static final int MINIMUM_STORAGE_VERSION = 1;
private static final int CURRENT_STORAGE_VERSION = 3; private static final int CURRENT_STORAGE_VERSION = 3;
private int previousStorageVersion; private final Object LOCK = new Object();
private final ObjectMapper jsonProcessor = Utils.createStorageObjectMapper(); private final ObjectMapper jsonProcessor = Utils.createStorageObjectMapper();
private final FileChannel fileChannel; private final FileChannel fileChannel;
private final FileLock lock; private final FileLock lock;
private int previousStorageVersion;
private File dataPath;
private String account; private String account;
private ACI aci; private ACI aci;
private String encryptedDeviceName; private String encryptedDeviceName;
@ -94,6 +98,9 @@ public class SignalAccount implements Closeable {
private ProfileKey profileKey; private ProfileKey profileKey;
private int preKeyIdOffset; private int preKeyIdOffset;
private int nextSignedPreKeyId; private int nextSignedPreKeyId;
private IdentityKeyPair identityKeyPair;
private int localRegistrationId;
private TrustNewIdentity trustNewIdentity;
private long lastReceiveTimestamp = 0; private long lastReceiveTimestamp = 0;
private boolean registered = false; private boolean registered = false;
@ -165,9 +172,12 @@ public class SignalAccount implements Closeable {
signalAccount.account = account; signalAccount.account = account;
signalAccount.profileKey = profileKey; signalAccount.profileKey = profileKey;
signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity); signalAccount.dataPath = dataPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account),
signalAccount.recipientStore, signalAccount.getRecipientStore(),
signalAccount::saveGroupStore); signalAccount::saveGroupStore);
signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore);
signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore);
@ -181,36 +191,6 @@ public class SignalAccount implements Closeable {
return signalAccount; return signalAccount;
} }
private void initStores(
final File dataPath,
final IdentityKeyPair identityKey,
final int registrationId,
final TrustNewIdentity trustNewIdentity
) throws IOException {
recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account), this::mergeRecipients);
preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account));
signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account));
sessionStore = new SessionStore(getSessionsPath(dataPath, account), recipientStore);
identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account),
recipientStore,
identityKey,
registrationId,
trustNewIdentity);
senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account),
getSenderKeysPath(dataPath, account),
recipientStore::resolveRecipientAddress,
recipientStore);
signalProtocolStore = new SignalProtocolStore(preKeyStore,
signedPreKeyStore,
sessionStore,
identityKeyStore,
senderKeyStore,
this::isMultiDevice);
messageCache = new MessageCache(getMessageCachePath(dataPath, account));
}
public static SignalAccount createOrUpdateLinkedAccount( public static SignalAccount createOrUpdateLinkedAccount(
File dataPath, File dataPath,
String account, String account,
@ -240,9 +220,9 @@ public class SignalAccount implements Closeable {
final var signalAccount = load(dataPath, account, true, trustNewIdentity); final var signalAccount = load(dataPath, account, true, trustNewIdentity);
signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress()); signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress());
signalAccount.sessionStore.archiveAllSessions(); signalAccount.getSessionStore().archiveAllSessions();
signalAccount.senderKeyStore.deleteAll(); signalAccount.getSenderKeyStore().deleteAll();
signalAccount.clearAllPreKeys(); signalAccount.clearAllPreKeys();
return signalAccount; return signalAccount;
} }
@ -250,8 +230,8 @@ public class SignalAccount implements Closeable {
private void clearAllPreKeys() { private void clearAllPreKeys() {
this.preKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE); this.preKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE);
this.nextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE); this.nextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE);
this.preKeyStore.removeAllPreKeys(); this.getPreKeyStore().removeAllPreKeys();
this.signedPreKeyStore.removeAllSignedPreKeys(); this.getSignedPreKeyStore().removeAllSignedPreKeys();
save(); save();
} }
@ -275,14 +255,17 @@ public class SignalAccount implements Closeable {
signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey);
signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity); signalAccount.dataPath = dataPath;
signalAccount.identityKeyPair = identityKey;
signalAccount.localRegistrationId = registrationId;
signalAccount.trustNewIdentity = trustNewIdentity;
signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account),
signalAccount.recipientStore, signalAccount.getRecipientStore(),
signalAccount::saveGroupStore); signalAccount::saveGroupStore);
signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore);
signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore);
signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress()); signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress());
signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
signalAccount.migrateLegacyConfigs(); signalAccount.migrateLegacyConfigs();
signalAccount.save(); signalAccount.save();
@ -335,19 +318,19 @@ public class SignalAccount implements Closeable {
} }
private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) { private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
sessionStore.mergeRecipients(recipientId, toBeMergedRecipientId); getSessionStore().mergeRecipients(recipientId, toBeMergedRecipientId);
identityKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId); getIdentityKeyStore().mergeRecipients(recipientId, toBeMergedRecipientId);
messageCache.mergeRecipients(recipientId, toBeMergedRecipientId); getMessageCache().mergeRecipients(recipientId, toBeMergedRecipientId);
groupStore.mergeRecipients(recipientId, toBeMergedRecipientId); getGroupStore().mergeRecipients(recipientId, toBeMergedRecipientId);
senderKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId); getSenderKeyStore().mergeRecipients(recipientId, toBeMergedRecipientId);
} }
public void removeRecipient(final RecipientId recipientId) { public void removeRecipient(final RecipientId recipientId) {
sessionStore.deleteAllSessions(recipientId); getSessionStore().deleteAllSessions(recipientId);
identityKeyStore.deleteIdentity(recipientId); getIdentityKeyStore().deleteIdentity(recipientId);
messageCache.deleteMessages(recipientId); getMessageCache().deleteMessages(recipientId);
senderKeyStore.deleteAll(recipientId); getSenderKeyStore().deleteAll(recipientId);
recipientStore.deleteRecipientData(recipientId); getRecipientStore().deleteRecipientData(recipientId);
} }
public static File getFileName(File dataPath, String account) { public static File getFileName(File dataPath, String account) {
@ -505,7 +488,10 @@ public class SignalAccount implements Closeable {
migratedLegacyConfig = true; migratedLegacyConfig = true;
} }
initStores(dataPath, identityKeyPair, registrationId, trustNewIdentity); this.dataPath = dataPath;
this.identityKeyPair = identityKeyPair;
this.localRegistrationId = registrationId;
this.trustNewIdentity = trustNewIdentity;
migratedLegacyConfig = loadLegacyStores(rootNode, legacySignalProtocolStore) || migratedLegacyConfig; migratedLegacyConfig = loadLegacyStores(rootNode, legacySignalProtocolStore) || migratedLegacyConfig;
@ -513,10 +499,12 @@ public class SignalAccount implements Closeable {
groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class);
groupStore = GroupStore.fromStorage(groupStoreStorage, groupStore = GroupStore.fromStorage(groupStoreStorage,
getGroupCachePath(dataPath, account), getGroupCachePath(dataPath, account),
recipientStore, getRecipientStore(),
this::saveGroupStore); this::saveGroupStore);
} else { } else {
groupStore = new GroupStore(getGroupCachePath(dataPath, account), recipientStore, this::saveGroupStore); groupStore = new GroupStore(getGroupCachePath(dataPath, account),
getRecipientStore(),
this::saveGroupStore);
} }
if (rootNode.hasNonNull("stickerStore")) { if (rootNode.hasNonNull("stickerStore")) {
@ -551,7 +539,7 @@ public class SignalAccount implements Closeable {
logger.debug("Migrating legacy recipient store."); logger.debug("Migrating legacy recipient store.");
var legacyRecipientStore = jsonProcessor.convertValue(legacyRecipientStoreNode, LegacyRecipientStore.class); var legacyRecipientStore = jsonProcessor.convertValue(legacyRecipientStoreNode, LegacyRecipientStore.class);
if (legacyRecipientStore != null) { if (legacyRecipientStore != null) {
recipientStore.resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); getRecipientStore().resolveRecipientsTrusted(legacyRecipientStore.getAddresses());
} }
getSelfRecipientId(); getSelfRecipientId();
migrated = true; migrated = true;
@ -561,7 +549,7 @@ public class SignalAccount implements Closeable {
logger.debug("Migrating legacy pre key store."); logger.debug("Migrating legacy pre key store.");
for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) { for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) {
try { try {
preKeyStore.storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue())); getPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue()));
} catch (IOException e) { } catch (IOException e) {
logger.warn("Failed to migrate pre key, ignoring", e); logger.warn("Failed to migrate pre key, ignoring", e);
} }
@ -573,7 +561,7 @@ public class SignalAccount implements Closeable {
logger.debug("Migrating legacy signed pre key store."); logger.debug("Migrating legacy signed pre key store.");
for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) { for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) {
try { try {
signedPreKeyStore.storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue())); getSignedPreKeyStore().storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue()));
} catch (IOException e) { } catch (IOException e) {
logger.warn("Failed to migrate signed pre key, ignoring", e); logger.warn("Failed to migrate signed pre key, ignoring", e);
} }
@ -585,7 +573,7 @@ public class SignalAccount implements Closeable {
logger.debug("Migrating legacy session store."); logger.debug("Migrating legacy session store.");
for (var session : legacySignalProtocolStore.getLegacySessionStore().getSessions()) { for (var session : legacySignalProtocolStore.getLegacySessionStore().getSessions()) {
try { try {
sessionStore.storeSession(new SignalProtocolAddress(session.address.getIdentifier(), getSessionStore().storeSession(new SignalProtocolAddress(session.address.getIdentifier(),
session.deviceId), new SessionRecord(session.sessionRecord)); session.deviceId), new SessionRecord(session.sessionRecord));
} catch (Exception e) { } catch (Exception e) {
logger.warn("Failed to migrate session, ignoring", e); logger.warn("Failed to migrate session, ignoring", e);
@ -597,9 +585,9 @@ public class SignalAccount implements Closeable {
if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) { if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
logger.debug("Migrating legacy identity session store."); logger.debug("Migrating legacy identity session store.");
for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) { for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) {
RecipientId recipientId = recipientStore.resolveRecipientTrusted(identity.getAddress()); RecipientId recipientId = getRecipientStore().resolveRecipientTrusted(identity.getAddress());
identityKeyStore.saveIdentity(recipientId, identity.getIdentityKey(), identity.getDateAdded()); getIdentityKeyStore().saveIdentity(recipientId, identity.getIdentityKey(), identity.getDateAdded());
identityKeyStore.setIdentityTrustLevel(recipientId, getIdentityKeyStore().setIdentityTrustLevel(recipientId,
identity.getIdentityKey(), identity.getIdentityKey(),
identity.getTrustLevel()); identity.getTrustLevel());
} }
@ -611,8 +599,8 @@ public class SignalAccount implements Closeable {
final var contactStoreNode = rootNode.get("contactStore"); final var contactStoreNode = rootNode.get("contactStore");
final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class); final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class);
for (var contact : contactStore.getContacts()) { for (var contact : contactStore.getContacts()) {
final var recipientId = recipientStore.resolveRecipientTrusted(contact.getAddress()); final var recipientId = getRecipientStore().resolveRecipientTrusted(contact.getAddress());
recipientStore.storeContact(recipientId, getRecipientStore().storeContact(recipientId,
new Contact(contact.name, new Contact(contact.name,
contact.color, contact.color,
contact.messageExpirationTime, contact.messageExpirationTime,
@ -639,9 +627,9 @@ public class SignalAccount implements Closeable {
var profileStoreNode = rootNode.get("profileStore"); var profileStoreNode = rootNode.get("profileStore");
final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class);
for (var profileEntry : legacyProfileStore.getProfileEntries()) { for (var profileEntry : legacyProfileStore.getProfileEntries()) {
var recipientId = recipientStore.resolveRecipient(profileEntry.getAddress()); var recipientId = getRecipientStore().resolveRecipient(profileEntry.getAddress());
recipientStore.storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); getRecipientStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential());
recipientStore.storeProfileKey(recipientId, profileEntry.getProfileKey()); getRecipientStore().storeProfileKey(recipientId, profileEntry.getProfileKey());
final var profile = profileEntry.getProfile(); final var profile = profileEntry.getProfile();
if (profile != null) { if (profile != null) {
final var capabilities = new HashSet<Profile.Capability>(); final var capabilities = new HashSet<Profile.Capability>();
@ -668,7 +656,7 @@ public class SignalAccount implements Closeable {
? Profile.UnidentifiedAccessMode.ENABLED ? Profile.UnidentifiedAccessMode.ENABLED
: Profile.UnidentifiedAccessMode.DISABLED, : Profile.UnidentifiedAccessMode.DISABLED,
capabilities); capabilities);
recipientStore.storeProfile(recipientId, newProfile); getRecipientStore().storeProfile(recipientId, newProfile);
} }
} }
} }
@ -687,10 +675,10 @@ public class SignalAccount implements Closeable {
} }
try { try {
if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) { if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) {
final var recipientId = recipientStore.resolveRecipient(thread.id); final var recipientId = getRecipientStore().resolveRecipient(thread.id);
var contact = recipientStore.getContact(recipientId); var contact = getRecipientStore().getContact(recipientId);
if (contact != null) { if (contact != null) {
recipientStore.storeContact(recipientId, getRecipientStore().storeContact(recipientId,
Contact.newBuilder(contact) Contact.newBuilder(contact)
.withMessageExpirationTime(thread.messageExpirationTime) .withMessageExpirationTime(thread.messageExpirationTime)
.build()); .build());
@ -738,13 +726,10 @@ public class SignalAccount implements Closeable {
.put("isMultiDevice", isMultiDevice) .put("isMultiDevice", isMultiDevice)
.put("lastReceiveTimestamp", lastReceiveTimestamp) .put("lastReceiveTimestamp", lastReceiveTimestamp)
.put("password", password) .put("password", password)
.put("registrationId", identityKeyStore.getLocalRegistrationId()) .put("registrationId", localRegistrationId)
.put("identityPrivateKey", .put("identityPrivateKey",
Base64.getEncoder() Base64.getEncoder().encodeToString(identityKeyPair.getPrivateKey().serialize()))
.encodeToString(identityKeyStore.getIdentityKeyPair().getPrivateKey().serialize())) .put("identityKey", Base64.getEncoder().encodeToString(identityKeyPair.getPublicKey().serialize()))
.put("identityKey",
Base64.getEncoder()
.encodeToString(identityKeyStore.getIdentityKeyPair().getPublicKey().serialize()))
.put("registrationLockPin", registrationLockPin) .put("registrationLockPin", registrationLockPin)
.put("pinMasterKey", .put("pinMasterKey",
pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
@ -796,7 +781,7 @@ public class SignalAccount implements Closeable {
logger.error("Invalid pre key id {}, expected {}", record.getId(), preKeyIdOffset); logger.error("Invalid pre key id {}, expected {}", record.getId(), preKeyIdOffset);
throw new AssertionError("Invalid pre key id"); throw new AssertionError("Invalid pre key id");
} }
preKeyStore.storePreKey(record.getId(), record); getPreKeyStore().storePreKey(record.getId(), record);
preKeyIdOffset = (preKeyIdOffset + 1) % Medium.MAX_VALUE; preKeyIdOffset = (preKeyIdOffset + 1) % Medium.MAX_VALUE;
} }
save(); save();
@ -807,21 +792,42 @@ public class SignalAccount implements Closeable {
logger.error("Invalid signed pre key id {}, expected {}", record.getId(), nextSignedPreKeyId); logger.error("Invalid signed pre key id {}, expected {}", record.getId(), nextSignedPreKeyId);
throw new AssertionError("Invalid signed pre key id"); throw new AssertionError("Invalid signed pre key id");
} }
signalProtocolStore.storeSignedPreKey(record.getId(), record); getSignedPreKeyStore().storeSignedPreKey(record.getId(), record);
nextSignedPreKeyId = (nextSignedPreKeyId + 1) % Medium.MAX_VALUE; nextSignedPreKeyId = (nextSignedPreKeyId + 1) % Medium.MAX_VALUE;
save(); save();
} }
public SignalProtocolStore getSignalProtocolStore() { public SignalProtocolStore getSignalProtocolStore() {
return signalProtocolStore; return getOrCreate(() -> signalProtocolStore,
() -> signalProtocolStore = new SignalProtocolStore(getPreKeyStore(),
getSignedPreKeyStore(),
getSessionStore(),
getIdentityKeyStore(),
getSenderKeyStore(),
this::isMultiDevice));
}
private PreKeyStore getPreKeyStore() {
return getOrCreate(() -> preKeyStore, () -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account)));
}
private SignedPreKeyStore getSignedPreKeyStore() {
return getOrCreate(() -> signedPreKeyStore,
() -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account)));
} }
public SessionStore getSessionStore() { public SessionStore getSessionStore() {
return sessionStore; return getOrCreate(() -> sessionStore,
() -> sessionStore = new SessionStore(getSessionsPath(dataPath, account), getRecipientStore()));
} }
public IdentityKeyStore getIdentityKeyStore() { public IdentityKeyStore getIdentityKeyStore() {
return identityKeyStore; return getOrCreate(() -> identityKeyStore,
() -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account),
getRecipientStore(),
identityKeyPair,
localRegistrationId,
trustNewIdentity));
} }
public GroupStore getGroupStore() { public GroupStore getGroupStore() {
@ -829,15 +835,17 @@ public class SignalAccount implements Closeable {
} }
public ContactsStore getContactStore() { public ContactsStore getContactStore() {
return recipientStore; return getRecipientStore();
} }
public RecipientStore getRecipientStore() { public RecipientStore getRecipientStore() {
return recipientStore; return getOrCreate(() -> recipientStore,
() -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account),
this::mergeRecipients));
} }
public ProfileStore getProfileStore() { public ProfileStore getProfileStore() {
return recipientStore; return getRecipientStore();
} }
public StickerStore getStickerStore() { public StickerStore getStickerStore() {
@ -845,7 +853,11 @@ public class SignalAccount implements Closeable {
} }
public SenderKeyStore getSenderKeyStore() { public SenderKeyStore getSenderKeyStore() {
return senderKeyStore; return getOrCreate(() -> senderKeyStore,
() -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account),
getSenderKeysPath(dataPath, account),
getRecipientStore()::resolveRecipientAddress,
getRecipientStore()));
} }
public ConfigurationStore getConfigurationStore() { public ConfigurationStore getConfigurationStore() {
@ -853,7 +865,8 @@ public class SignalAccount implements Closeable {
} }
public MessageCache getMessageCache() { public MessageCache getMessageCache() {
return messageCache; return getOrCreate(() -> messageCache,
() -> messageCache = new MessageCache(getMessageCachePath(dataPath, account)));
} }
public String getAccount() { public String getAccount() {
@ -874,7 +887,8 @@ public class SignalAccount implements Closeable {
} }
public RecipientId getSelfRecipientId() { public RecipientId getSelfRecipientId() {
return recipientStore.resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(), account)); return getRecipientStore().resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(),
account));
} }
public String getEncryptedDeviceName() { public String getEncryptedDeviceName() {
@ -895,11 +909,11 @@ public class SignalAccount implements Closeable {
} }
public IdentityKeyPair getIdentityKeyPair() { public IdentityKeyPair getIdentityKeyPair() {
return signalProtocolStore.getIdentityKeyPair(); return identityKeyPair;
} }
public int getLocalRegistrationId() { public int getLocalRegistrationId() {
return signalProtocolStore.getLocalRegistrationId(); return localRegistrationId;
} }
public String getPassword() { public String getPassword() {
@ -1026,7 +1040,7 @@ public class SignalAccount implements Closeable {
clearAllPreKeys(); clearAllPreKeys();
getSessionStore().archiveAllSessions(); getSessionStore().archiveAllSessions();
senderKeyStore.deleteAll(); getSenderKeyStore().deleteAll();
final var recipientId = getRecipientStore().resolveRecipientTrusted(getSelfAddress()); final var recipientId = getRecipientStore().resolveRecipientTrusted(getSelfAddress());
final var publicKey = getIdentityKeyPair().getPublicKey(); final var publicKey = getIdentityKeyPair().getPublicKey();
getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date());
@ -1047,4 +1061,25 @@ public class SignalAccount implements Closeable {
} }
} }
} }
private <T> T getOrCreate(Supplier<T> supplier, Callable creator) {
var value = supplier.get();
if (value != null) {
return value;
}
synchronized (LOCK) {
value = supplier.get();
if (value != null) {
return value;
}
creator.call();
return supplier.get();
}
}
private interface Callable {
void call();
}
} }

View file

@ -49,7 +49,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
private long lastId; private long lastId;
private boolean isBulkUpdating; private boolean isBulkUpdating;
public static RecipientStore load(File file, RecipientMergeHandler recipientMergeHandler) throws IOException { public static RecipientStore load(File file, RecipientMergeHandler recipientMergeHandler) {
final var objectMapper = Utils.createStorageObjectMapper(); final var objectMapper = Utils.createStorageObjectMapper();
try (var inputStream = new FileInputStream(file)) { try (var inputStream = new FileInputStream(file)) {
final var storage = objectMapper.readValue(inputStream, Storage.class); final var storage = objectMapper.readValue(inputStream, Storage.class);
@ -114,6 +114,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
logger.trace("Creating new recipient store."); logger.trace("Creating new recipient store.");
return new RecipientStore(objectMapper, file, recipientMergeHandler, new HashMap<>(), 0); return new RecipientStore(objectMapper, file, recipientMergeHandler, new HashMap<>(), 0);
} catch (IOException e) {
logger.warn("Failed to load recipient store", e);
throw new RuntimeException(e);
} }
} }

View file

@ -42,7 +42,7 @@ public class SenderKeySharedStore {
public static SenderKeySharedStore load( public static SenderKeySharedStore load(
final File file, final RecipientAddressResolver addressResolver, final RecipientResolver resolver final File file, final RecipientAddressResolver addressResolver, final RecipientResolver resolver
) throws IOException { ) {
final var objectMapper = Utils.createStorageObjectMapper(); final var objectMapper = Utils.createStorageObjectMapper();
try (var inputStream = new FileInputStream(file)) { try (var inputStream = new FileInputStream(file)) {
final var storage = objectMapper.readValue(inputStream, Storage.class); final var storage = objectMapper.readValue(inputStream, Storage.class);
@ -70,6 +70,9 @@ public class SenderKeySharedStore {
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
logger.trace("Creating new shared sender key store."); logger.trace("Creating new shared sender key store.");
return new SenderKeySharedStore(new HashMap<>(), objectMapper, file, addressResolver, resolver); return new SenderKeySharedStore(new HashMap<>(), objectMapper, file, addressResolver, resolver);
} catch (IOException e) {
logger.warn("Failed to load shared sender key store", e);
throw new RuntimeException(e);
} }
} }

View file

@ -9,7 +9,6 @@ import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore;
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -24,7 +23,7 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore {
final File senderKeysPath, final File senderKeysPath,
final RecipientAddressResolver addressResolver, final RecipientAddressResolver addressResolver,
final RecipientResolver resolver final RecipientResolver resolver
) throws IOException { ) {
this.senderKeyRecordStore = new SenderKeyRecordStore(senderKeysPath, resolver); this.senderKeyRecordStore = new SenderKeyRecordStore(senderKeysPath, resolver);
this.senderKeySharedStore = SenderKeySharedStore.load(file, addressResolver, resolver); this.senderKeySharedStore = SenderKeySharedStore.load(file, addressResolver, resolver);
} }