mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Create stores in SignalAccount lazily
This commit is contained in:
parent
e5537dc4db
commit
67146f9cc7
4 changed files with 134 additions and 94 deletions
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue