mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
parent
299671480f
commit
2e01a05e71
11 changed files with 346 additions and 5 deletions
|
@ -42,6 +42,7 @@ import org.asamk.signal.manager.helper.IncomingMessageHandler;
|
||||||
import org.asamk.signal.manager.helper.PinHelper;
|
import org.asamk.signal.manager.helper.PinHelper;
|
||||||
import org.asamk.signal.manager.helper.ProfileHelper;
|
import org.asamk.signal.manager.helper.ProfileHelper;
|
||||||
import org.asamk.signal.manager.helper.SendHelper;
|
import org.asamk.signal.manager.helper.SendHelper;
|
||||||
|
import org.asamk.signal.manager.helper.StorageHelper;
|
||||||
import org.asamk.signal.manager.helper.SyncHelper;
|
import org.asamk.signal.manager.helper.SyncHelper;
|
||||||
import org.asamk.signal.manager.helper.UnidentifiedAccessHelper;
|
import org.asamk.signal.manager.helper.UnidentifiedAccessHelper;
|
||||||
import org.asamk.signal.manager.jobs.Context;
|
import org.asamk.signal.manager.jobs.Context;
|
||||||
|
@ -133,6 +134,7 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
private final ProfileHelper profileHelper;
|
private final ProfileHelper profileHelper;
|
||||||
private final PinHelper pinHelper;
|
private final PinHelper pinHelper;
|
||||||
|
private final StorageHelper storageHelper;
|
||||||
private final SendHelper sendHelper;
|
private final SendHelper sendHelper;
|
||||||
private final SyncHelper syncHelper;
|
private final SyncHelper syncHelper;
|
||||||
private final AttachmentHelper attachmentHelper;
|
private final AttachmentHelper attachmentHelper;
|
||||||
|
@ -209,6 +211,7 @@ public class Manager implements Closeable {
|
||||||
avatarStore,
|
avatarStore,
|
||||||
this::resolveSignalServiceAddress,
|
this::resolveSignalServiceAddress,
|
||||||
account.getRecipientStore());
|
account.getRecipientStore());
|
||||||
|
this.storageHelper = new StorageHelper(account, dependencies, groupHelper);
|
||||||
this.contactHelper = new ContactHelper(account);
|
this.contactHelper = new ContactHelper(account);
|
||||||
this.syncHelper = new SyncHelper(account,
|
this.syncHelper = new SyncHelper(account,
|
||||||
attachmentHelper,
|
attachmentHelper,
|
||||||
|
@ -223,7 +226,8 @@ public class Manager implements Closeable {
|
||||||
sendHelper,
|
sendHelper,
|
||||||
groupHelper,
|
groupHelper,
|
||||||
syncHelper,
|
syncHelper,
|
||||||
profileHelper);
|
profileHelper,
|
||||||
|
storageHelper);
|
||||||
var jobExecutor = new JobExecutor(context);
|
var jobExecutor = new JobExecutor(context);
|
||||||
|
|
||||||
this.incomingMessageHandler = new IncomingMessageHandler(account,
|
this.incomingMessageHandler = new IncomingMessageHandler(account,
|
||||||
|
@ -747,6 +751,13 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
public void requestAllSyncData() throws IOException {
|
public void requestAllSyncData() throws IOException {
|
||||||
syncHelper.requestAllSyncData();
|
syncHelper.requestAllSyncData();
|
||||||
|
retrieveRemoteStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void retrieveRemoteStorage() throws IOException {
|
||||||
|
if (account.getStorageKey() != null) {
|
||||||
|
storageHelper.readDataFromStorage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getSenderCertificate() {
|
private byte[] getSenderCertificate() {
|
||||||
|
|
|
@ -103,6 +103,7 @@ public class ProvisioningManager {
|
||||||
? null
|
? null
|
||||||
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey());
|
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey());
|
||||||
|
|
||||||
|
logger.debug("Finishing new device registration");
|
||||||
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
|
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -129,6 +130,7 @@ public class ProvisioningManager {
|
||||||
try {
|
try {
|
||||||
m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
|
m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
|
||||||
|
|
||||||
|
logger.debug("Refreshing pre keys");
|
||||||
try {
|
try {
|
||||||
m.refreshPreKeys();
|
m.refreshPreKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -136,6 +138,7 @@ public class ProvisioningManager {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("Requesting sync data");
|
||||||
try {
|
try {
|
||||||
m.requestAllSyncData();
|
m.requestAllSyncData();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -174,7 +174,6 @@ public class RegistrationManager implements Closeable {
|
||||||
masterKey = registrationLockData.getMasterKey();
|
masterKey = registrationLockData.getMasterKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO response.isStorageCapable()
|
|
||||||
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
|
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
|
||||||
account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
|
account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
|
||||||
|
|
||||||
|
@ -186,6 +185,9 @@ public class RegistrationManager implements Closeable {
|
||||||
m.refreshPreKeys();
|
m.refreshPreKeys();
|
||||||
// Set an initial empty profile so user can be added to groups
|
// Set an initial empty profile so user can be added to groups
|
||||||
m.setProfile(null, null, null, null, null);
|
m.setProfile(null, null, null, null, null);
|
||||||
|
if (response.isStorageCapable()) {
|
||||||
|
m.retrieveRemoteStorage();
|
||||||
|
}
|
||||||
|
|
||||||
final var result = m;
|
final var result = m;
|
||||||
m = null;
|
m = null;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.asamk.signal.manager;
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
|
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord;
|
||||||
|
|
||||||
public enum TrustLevel {
|
public enum TrustLevel {
|
||||||
UNTRUSTED,
|
UNTRUSTED,
|
||||||
|
@ -16,6 +17,20 @@ public enum TrustLevel {
|
||||||
return TrustLevel.cachedValues[i];
|
return TrustLevel.cachedValues[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TrustLevel fromIdentityState(ContactRecord.IdentityState identityState) {
|
||||||
|
switch (identityState) {
|
||||||
|
case DEFAULT:
|
||||||
|
return TRUSTED_UNVERIFIED;
|
||||||
|
case UNVERIFIED:
|
||||||
|
return UNTRUSTED;
|
||||||
|
case VERIFIED:
|
||||||
|
return TRUSTED_VERIFIED;
|
||||||
|
case UNRECOGNIZED:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unknown identity state: " + identityState);
|
||||||
|
}
|
||||||
|
|
||||||
public static TrustLevel fromVerifiedState(VerifiedMessage.VerifiedState verifiedState) {
|
public static TrustLevel fromVerifiedState(VerifiedMessage.VerifiedState verifiedState) {
|
||||||
switch (verifiedState) {
|
switch (verifiedState) {
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.asamk.signal.manager.actions;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.jobs.Context;
|
||||||
|
|
||||||
|
public class RetrieveStorageDataAction implements HandleAction {
|
||||||
|
|
||||||
|
private static final RetrieveStorageDataAction INSTANCE = new RetrieveStorageDataAction();
|
||||||
|
|
||||||
|
private RetrieveStorageDataAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetrieveStorageDataAction create() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Context context) throws Throwable {
|
||||||
|
if (context.getAccount().getStorageKey() != null) {
|
||||||
|
context.getStorageHelper().readDataFromStorage();
|
||||||
|
} else {
|
||||||
|
if (!context.getAccount().isMasterDevice()) {
|
||||||
|
context.getSyncHelper().requestAllSyncData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.asamk.signal.manager.actions;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.jobs.Context;
|
||||||
|
|
||||||
|
public class SendSyncKeysAction implements HandleAction {
|
||||||
|
|
||||||
|
private static final SendSyncKeysAction INSTANCE = new SendSyncKeysAction();
|
||||||
|
|
||||||
|
private SendSyncKeysAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SendSyncKeysAction create() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Context context) throws Throwable {
|
||||||
|
context.getSyncHelper().sendKeysMessage();
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,14 @@ import org.asamk.signal.manager.UntrustedIdentityException;
|
||||||
import org.asamk.signal.manager.actions.HandleAction;
|
import org.asamk.signal.manager.actions.HandleAction;
|
||||||
import org.asamk.signal.manager.actions.RenewSessionAction;
|
import org.asamk.signal.manager.actions.RenewSessionAction;
|
||||||
import org.asamk.signal.manager.actions.RetrieveProfileAction;
|
import org.asamk.signal.manager.actions.RetrieveProfileAction;
|
||||||
|
import org.asamk.signal.manager.actions.RetrieveStorageDataAction;
|
||||||
import org.asamk.signal.manager.actions.SendGroupInfoAction;
|
import org.asamk.signal.manager.actions.SendGroupInfoAction;
|
||||||
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
|
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
|
||||||
import org.asamk.signal.manager.actions.SendReceiptAction;
|
import org.asamk.signal.manager.actions.SendReceiptAction;
|
||||||
import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
|
import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
|
||||||
import org.asamk.signal.manager.actions.SendSyncContactsAction;
|
import org.asamk.signal.manager.actions.SendSyncContactsAction;
|
||||||
import org.asamk.signal.manager.actions.SendSyncGroupsAction;
|
import org.asamk.signal.manager.actions.SendSyncGroupsAction;
|
||||||
|
import org.asamk.signal.manager.actions.SendSyncKeysAction;
|
||||||
import org.asamk.signal.manager.groups.GroupId;
|
import org.asamk.signal.manager.groups.GroupId;
|
||||||
import org.asamk.signal.manager.groups.GroupNotFoundException;
|
import org.asamk.signal.manager.groups.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.groups.GroupUtils;
|
import org.asamk.signal.manager.groups.GroupUtils;
|
||||||
|
@ -237,7 +239,10 @@ public final class IncomingMessageHandler {
|
||||||
if (rm.isBlockedListRequest()) {
|
if (rm.isBlockedListRequest()) {
|
||||||
actions.add(SendSyncBlockedListAction.create());
|
actions.add(SendSyncBlockedListAction.create());
|
||||||
}
|
}
|
||||||
// TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest();
|
if (rm.isKeysRequest()) {
|
||||||
|
actions.add(SendSyncKeysAction.create());
|
||||||
|
}
|
||||||
|
// TODO Handle rm.isConfigurationRequest();
|
||||||
}
|
}
|
||||||
if (syncMessage.getGroups().isPresent()) {
|
if (syncMessage.getGroups().isPresent()) {
|
||||||
logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
|
logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
|
||||||
|
@ -307,7 +312,7 @@ public final class IncomingMessageHandler {
|
||||||
case LOCAL_PROFILE:
|
case LOCAL_PROFILE:
|
||||||
actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
|
actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
|
||||||
case STORAGE_MANIFEST:
|
case STORAGE_MANIFEST:
|
||||||
// TODO
|
actions.add(RetrieveStorageDataAction.create());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (syncMessage.getKeys().isPresent()) {
|
if (syncMessage.getKeys().isPresent()) {
|
||||||
|
@ -315,6 +320,7 @@ public final class IncomingMessageHandler {
|
||||||
if (keysMessage.getStorageService().isPresent()) {
|
if (keysMessage.getStorageService().isPresent()) {
|
||||||
final var storageKey = keysMessage.getStorageService().get();
|
final var storageKey = keysMessage.getStorageService().get();
|
||||||
account.setStorageKey(storageKey);
|
account.setStorageKey(storageKey);
|
||||||
|
actions.add(RetrieveStorageDataAction.create());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (syncMessage.getConfiguration().isPresent()) {
|
if (syncMessage.getConfiguration().isPresent()) {
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.SignalDependencies;
|
||||||
|
import org.asamk.signal.manager.TrustLevel;
|
||||||
|
import org.asamk.signal.manager.groups.GroupId;
|
||||||
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
|
import org.asamk.signal.manager.storage.recipients.Contact;
|
||||||
|
import org.signal.zkgroup.InvalidInputException;
|
||||||
|
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
|
import org.signal.zkgroup.profiles.ProfileKey;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
|
||||||
|
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.ManifestRecord;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class StorageHelper {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(StorageHelper.class);
|
||||||
|
|
||||||
|
private final SignalAccount account;
|
||||||
|
private final SignalDependencies dependencies;
|
||||||
|
private final GroupHelper groupHelper;
|
||||||
|
|
||||||
|
public StorageHelper(
|
||||||
|
final SignalAccount account, final SignalDependencies dependencies, final GroupHelper groupHelper
|
||||||
|
) {
|
||||||
|
this.account = account;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
this.groupHelper = groupHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readDataFromStorage() throws IOException {
|
||||||
|
logger.debug("Reading data from remote storage");
|
||||||
|
Optional<SignalStorageManifest> manifest;
|
||||||
|
try {
|
||||||
|
manifest = dependencies.getAccountManager()
|
||||||
|
.getStorageManifestIfDifferentVersion(account.getStorageKey(), account.getStorageManifestVersion());
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
logger.warn("Manifest couldn't be decrypted, ignoring.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manifest.isPresent()) {
|
||||||
|
logger.debug("Manifest is up to date, does not exist or couldn't be decrypted, ignoring.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
account.setStorageManifestVersion(manifest.get().getVersion());
|
||||||
|
|
||||||
|
readAccountRecord(manifest.get());
|
||||||
|
|
||||||
|
final var storageIds = manifest.get()
|
||||||
|
.getStorageIds()
|
||||||
|
.stream()
|
||||||
|
.filter(id -> !id.isUnknown() && id.getType() != ManifestRecord.Identifier.Type.ACCOUNT_VALUE)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (final var record : getSignalStorageRecords(storageIds)) {
|
||||||
|
if (record.getType() == ManifestRecord.Identifier.Type.GROUPV2_VALUE) {
|
||||||
|
readGroupV2Record(record);
|
||||||
|
} else if (record.getType() == ManifestRecord.Identifier.Type.GROUPV1_VALUE) {
|
||||||
|
readGroupV1Record(record);
|
||||||
|
} else if (record.getType() == ManifestRecord.Identifier.Type.CONTACT_VALUE) {
|
||||||
|
readContactRecord(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readContactRecord(final SignalStorageRecord record) {
|
||||||
|
if (record == null || !record.getContact().isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var contactRecord = record.getContact().get();
|
||||||
|
final var address = contactRecord.getAddress();
|
||||||
|
|
||||||
|
final var recipientId = account.getRecipientStore().resolveRecipient(address);
|
||||||
|
final var contact = account.getContactStore().getContact(recipientId);
|
||||||
|
if (contactRecord.getGivenName().isPresent() || contactRecord.getFamilyName().isPresent() || (
|
||||||
|
(contact == null || !contact.isBlocked()) && contactRecord.isBlocked()
|
||||||
|
)) {
|
||||||
|
final var newContact = (contact == null ? Contact.newBuilder() : Contact.newBuilder(contact)).withBlocked(
|
||||||
|
contactRecord.isBlocked())
|
||||||
|
.withName((contactRecord.getGivenName().or("") + " " + contactRecord.getFamilyName().or("")).trim())
|
||||||
|
.build();
|
||||||
|
account.getContactStore().storeContact(recipientId, newContact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contactRecord.getProfileKey().isPresent()) {
|
||||||
|
try {
|
||||||
|
final var profileKey = new ProfileKey(contactRecord.getProfileKey().get());
|
||||||
|
account.getProfileStore().storeProfileKey(recipientId, profileKey);
|
||||||
|
} catch (InvalidInputException e) {
|
||||||
|
logger.warn("Received invalid contact profile key from storage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contactRecord.getIdentityKey().isPresent()) {
|
||||||
|
try {
|
||||||
|
final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get());
|
||||||
|
account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date());
|
||||||
|
|
||||||
|
final var trustLevel = TrustLevel.fromIdentityState(contactRecord.getIdentityState());
|
||||||
|
if (trustLevel != null) {
|
||||||
|
account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identityKey, trustLevel);
|
||||||
|
}
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
logger.warn("Received invalid contact identity key from storage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readGroupV1Record(final SignalStorageRecord record) {
|
||||||
|
if (record == null || !record.getGroupV1().isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var groupV1Record = record.getGroupV1().get();
|
||||||
|
final var groupIdV1 = GroupId.v1(groupV1Record.getGroupId());
|
||||||
|
|
||||||
|
final var group = account.getGroupStore().getGroup(groupIdV1);
|
||||||
|
if (group == null) {
|
||||||
|
try {
|
||||||
|
groupHelper.sendGroupInfoRequest(groupIdV1, account.getSelfRecipientId());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.warn("Failed to send group request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final var groupV1 = account.getGroupStore().getOrCreateGroupV1(groupIdV1);
|
||||||
|
if (groupV1.isBlocked() != groupV1Record.isBlocked()) {
|
||||||
|
groupV1.setBlocked(groupV1Record.isBlocked());
|
||||||
|
account.getGroupStore().updateGroup(groupV1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readGroupV2Record(final SignalStorageRecord record) {
|
||||||
|
if (record == null || !record.getGroupV2().isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var groupV2Record = record.getGroupV2().get();
|
||||||
|
if (groupV2Record.isArchived()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final GroupMasterKey groupMasterKey;
|
||||||
|
try {
|
||||||
|
groupMasterKey = new GroupMasterKey(groupV2Record.getMasterKeyBytes());
|
||||||
|
} catch (InvalidInputException e) {
|
||||||
|
logger.warn("Received invalid group master key from storage");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var group = groupHelper.getOrMigrateGroup(groupMasterKey, 0, null);
|
||||||
|
if (group.isBlocked() != groupV2Record.isBlocked()) {
|
||||||
|
group.setBlocked(groupV2Record.isBlocked());
|
||||||
|
account.getGroupStore().updateGroup(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readAccountRecord(final SignalStorageManifest manifest) throws IOException {
|
||||||
|
Optional<StorageId> accountId = manifest.getAccountStorageId();
|
||||||
|
if (!accountId.isPresent()) {
|
||||||
|
logger.warn("Manifest has no account record, ignoring.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalStorageRecord record = getSignalStorageRecord(accountId.get());
|
||||||
|
if (record == null) {
|
||||||
|
logger.warn("Could not find account record, even though we had an ID, ignoring.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalAccountRecord accountRecord = record.getAccount().orNull();
|
||||||
|
if (accountRecord == null) {
|
||||||
|
logger.warn("The storage record didn't actually have an account, ignoring.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountRecord.getProfileKey().isPresent()) {
|
||||||
|
try {
|
||||||
|
account.setProfileKey(new ProfileKey(accountRecord.getProfileKey().get()));
|
||||||
|
} catch (InvalidInputException e) {
|
||||||
|
logger.warn("Received invalid profile key from storage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SignalStorageRecord getSignalStorageRecord(final StorageId accountId) throws IOException {
|
||||||
|
List<SignalStorageRecord> records;
|
||||||
|
try {
|
||||||
|
records = dependencies.getAccountManager()
|
||||||
|
.readStorageRecords(account.getStorageKey(), Collections.singletonList(accountId));
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
logger.warn("Failed to read storage records, ignoring.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return records.size() > 0 ? records.get(0) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SignalStorageRecord> getSignalStorageRecords(final List<StorageId> storageIds) throws IOException {
|
||||||
|
List<SignalStorageRecord> records;
|
||||||
|
try {
|
||||||
|
records = dependencies.getAccountManager().readStorageRecords(account.getStorageKey(), storageIds);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
logger.warn("Failed to read storage records, ignoring.");
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsI
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
||||||
|
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
|
@ -215,6 +216,11 @@ public class SyncHelper {
|
||||||
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendKeysMessage() throws IOException {
|
||||||
|
var keysMessage = new KeysMessage(Optional.fromNullable(account.getStorageKey()));
|
||||||
|
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage));
|
||||||
|
}
|
||||||
|
|
||||||
public void handleSyncDeviceContacts(final InputStream input) throws IOException {
|
public void handleSyncDeviceContacts(final InputStream input) throws IOException {
|
||||||
final var s = new DeviceContactsInputStream(input);
|
final var s = new DeviceContactsInputStream(input);
|
||||||
DeviceContact c;
|
DeviceContact c;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.asamk.signal.manager.StickerPackStore;
|
||||||
import org.asamk.signal.manager.helper.GroupHelper;
|
import org.asamk.signal.manager.helper.GroupHelper;
|
||||||
import org.asamk.signal.manager.helper.ProfileHelper;
|
import org.asamk.signal.manager.helper.ProfileHelper;
|
||||||
import org.asamk.signal.manager.helper.SendHelper;
|
import org.asamk.signal.manager.helper.SendHelper;
|
||||||
|
import org.asamk.signal.manager.helper.StorageHelper;
|
||||||
import org.asamk.signal.manager.helper.SyncHelper;
|
import org.asamk.signal.manager.helper.SyncHelper;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ public class Context {
|
||||||
private final GroupHelper groupHelper;
|
private final GroupHelper groupHelper;
|
||||||
private final SyncHelper syncHelper;
|
private final SyncHelper syncHelper;
|
||||||
private final ProfileHelper profileHelper;
|
private final ProfileHelper profileHelper;
|
||||||
|
private final StorageHelper storageHelper;
|
||||||
|
|
||||||
public Context(
|
public Context(
|
||||||
final SignalAccount account,
|
final SignalAccount account,
|
||||||
|
@ -25,7 +27,8 @@ public class Context {
|
||||||
final SendHelper sendHelper,
|
final SendHelper sendHelper,
|
||||||
final GroupHelper groupHelper,
|
final GroupHelper groupHelper,
|
||||||
final SyncHelper syncHelper,
|
final SyncHelper syncHelper,
|
||||||
final ProfileHelper profileHelper
|
final ProfileHelper profileHelper,
|
||||||
|
final StorageHelper storageHelper
|
||||||
) {
|
) {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.dependencies = dependencies;
|
this.dependencies = dependencies;
|
||||||
|
@ -34,6 +37,7 @@ public class Context {
|
||||||
this.groupHelper = groupHelper;
|
this.groupHelper = groupHelper;
|
||||||
this.syncHelper = syncHelper;
|
this.syncHelper = syncHelper;
|
||||||
this.profileHelper = profileHelper;
|
this.profileHelper = profileHelper;
|
||||||
|
this.storageHelper = storageHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalAccount getAccount() {
|
public SignalAccount getAccount() {
|
||||||
|
@ -63,4 +67,8 @@ public class Context {
|
||||||
public ProfileHelper getProfileHelper() {
|
public ProfileHelper getProfileHelper() {
|
||||||
return profileHelper;
|
return profileHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StorageHelper getStorageHelper() {
|
||||||
|
return storageHelper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ public class SignalAccount implements Closeable {
|
||||||
private String registrationLockPin;
|
private String registrationLockPin;
|
||||||
private MasterKey pinMasterKey;
|
private MasterKey pinMasterKey;
|
||||||
private StorageKey storageKey;
|
private StorageKey storageKey;
|
||||||
|
private long storageManifestVersion = -1;
|
||||||
private ProfileKey profileKey;
|
private ProfileKey profileKey;
|
||||||
private int preKeyIdOffset;
|
private int preKeyIdOffset;
|
||||||
private int nextSignedPreKeyId;
|
private int nextSignedPreKeyId;
|
||||||
|
@ -291,6 +292,9 @@ public class SignalAccount implements Closeable {
|
||||||
this.registered = true;
|
this.registered = true;
|
||||||
this.isMultiDevice = true;
|
this.isMultiDevice = true;
|
||||||
this.lastReceiveTimestamp = 0;
|
this.lastReceiveTimestamp = 0;
|
||||||
|
this.pinMasterKey = null;
|
||||||
|
this.storageManifestVersion = -1;
|
||||||
|
this.storageKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void migrateLegacyConfigs() {
|
private void migrateLegacyConfigs() {
|
||||||
|
@ -432,6 +436,9 @@ public class SignalAccount implements Closeable {
|
||||||
if (rootNode.hasNonNull("storageKey")) {
|
if (rootNode.hasNonNull("storageKey")) {
|
||||||
storageKey = new StorageKey(Base64.getDecoder().decode(rootNode.get("storageKey").asText()));
|
storageKey = new StorageKey(Base64.getDecoder().decode(rootNode.get("storageKey").asText()));
|
||||||
}
|
}
|
||||||
|
if (rootNode.hasNonNull("storageManifestVersion")) {
|
||||||
|
storageManifestVersion = rootNode.get("storageManifestVersion").asLong();
|
||||||
|
}
|
||||||
if (rootNode.hasNonNull("preKeyIdOffset")) {
|
if (rootNode.hasNonNull("preKeyIdOffset")) {
|
||||||
preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(0);
|
preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -693,6 +700,7 @@ public class SignalAccount implements Closeable {
|
||||||
pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
|
pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
|
||||||
.put("storageKey",
|
.put("storageKey",
|
||||||
storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize()))
|
storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize()))
|
||||||
|
.put("storageManifestVersion", storageManifestVersion == -1 ? null : storageManifestVersion)
|
||||||
.put("preKeyIdOffset", preKeyIdOffset)
|
.put("preKeyIdOffset", preKeyIdOffset)
|
||||||
.put("nextSignedPreKeyId", nextSignedPreKeyId)
|
.put("nextSignedPreKeyId", nextSignedPreKeyId)
|
||||||
.put("profileKey",
|
.put("profileKey",
|
||||||
|
@ -877,6 +885,18 @@ public class SignalAccount implements Closeable {
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getStorageManifestVersion() {
|
||||||
|
return this.storageManifestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorageManifestVersion(final long storageManifestVersion) {
|
||||||
|
if (storageManifestVersion == this.storageManifestVersion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.storageManifestVersion = storageManifestVersion;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
public ProfileKey getProfileKey() {
|
public ProfileKey getProfileKey() {
|
||||||
return profileKey;
|
return profileKey;
|
||||||
}
|
}
|
||||||
|
@ -948,6 +968,8 @@ public class SignalAccount implements Closeable {
|
||||||
|
|
||||||
public void finishRegistration(final UUID uuid, final MasterKey masterKey, final String pin) {
|
public void finishRegistration(final UUID uuid, final MasterKey masterKey, final String pin) {
|
||||||
this.pinMasterKey = masterKey;
|
this.pinMasterKey = masterKey;
|
||||||
|
this.storageManifestVersion = -1;
|
||||||
|
this.storageKey = null;
|
||||||
this.encryptedDeviceName = null;
|
this.encryptedDeviceName = null;
|
||||||
this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
||||||
this.isMultiDevice = false;
|
this.isMultiDevice = false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue