import org.asamk.signal.manager.helper.GroupV2Helper;
import org.asamk.signal.manager.helper.IncomingMessageHandler;
import org.asamk.signal.manager.helper.PinHelper;
+import org.asamk.signal.manager.helper.PreKeyHelper;
import org.asamk.signal.manager.helper.ProfileHelper;
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.UnidentifiedAccessHelper;
import org.asamk.signal.manager.jobs.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
-import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.fingerprint.Fingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
-import org.whispersystems.libsignal.state.PreKeyRecord;
-import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalSessionLock;
private final ProfileHelper profileHelper;
private final PinHelper pinHelper;
+ private final StorageHelper storageHelper;
private final SendHelper sendHelper;
private final SyncHelper syncHelper;
private final AttachmentHelper attachmentHelper;
private final GroupHelper groupHelper;
private final ContactHelper contactHelper;
private final IncomingMessageHandler incomingMessageHandler;
+ private final PreKeyHelper preKeyHelper;
private final Context context;
+ private boolean hasCaughtUpWithOldMessages = false;
Manager(
SignalAccount account,
return LEGACY_LOCK::unlock;
}
};
- this.dependencies = new SignalDependencies(account.getSelfAddress(),
- serviceEnvironmentConfig,
+ this.dependencies = new SignalDependencies(serviceEnvironmentConfig,
userAgent,
credentialsProvider,
account.getSignalProtocolStore(),
avatarStore,
account.getProfileStore()::getProfileKey,
unidentifiedAccessHelper::getAccessFor,
- dependencies::getProfileService,
- dependencies::getMessageReceiver,
this::resolveSignalServiceAddress);
final GroupV2Helper groupV2Helper = new GroupV2Helper(profileHelper::getRecipientProfileKeyCredential,
this::getRecipientProfile,
avatarStore,
this::resolveSignalServiceAddress,
account.getRecipientStore());
+ this.storageHelper = new StorageHelper(account, dependencies, groupHelper);
this.contactHelper = new ContactHelper(account);
this.syncHelper = new SyncHelper(account,
attachmentHelper,
groupHelper,
avatarStore,
this::resolveSignalServiceAddress);
+ preKeyHelper = new PreKeyHelper(account, dependencies);
this.context = new Context(account,
- dependencies.getAccountManager(),
- dependencies.getMessageReceiver(),
+ dependencies,
stickerPackStore,
sendHelper,
groupHelper,
syncHelper,
- profileHelper);
+ profileHelper,
+ storageHelper,
+ preKeyHelper);
var jobExecutor = new JobExecutor(context);
this.incomingMessageHandler = new IncomingMessageHandler(account,
contactHelper,
attachmentHelper,
syncHelper,
+ this::getRecipientProfile,
jobExecutor);
}
return account.getSelfRecipientId();
}
- private IdentityKeyPair getIdentityKeyPair() {
- return account.getIdentityKeyPair();
- }
-
public int getDeviceId() {
return account.getDeviceId();
}
days);
}
}
- if (dependencies.getAccountManager().getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
- refreshPreKeys();
- }
+ preKeyHelper.refreshPreKeysIfNecessary();
if (account.getUuid() == null) {
account.setUuid(dependencies.getAccountManager().getOwnUuid());
}
- updateAccountAttributes();
+ updateAccountAttributes(null);
}
/**
}));
}
- public void updateAccountAttributes() throws IOException {
+ public void updateAccountAttributes(String deviceName) throws IOException {
+ final String encryptedDeviceName;
+ if (deviceName == null) {
+ encryptedDeviceName = account.getEncryptedDeviceName();
+ } else {
+ final var privateKey = account.getIdentityKeyPair().getPrivateKey();
+ encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey);
+ account.setEncryptedDeviceName(encryptedDeviceName);
+ }
dependencies.getAccountManager()
- .setAccountAttributes(account.getEncryptedDeviceName(),
+ .setAccountAttributes(encryptedDeviceName,
null,
account.getLocalRegistrationId(),
true,
- // set legacy pin only if no KBS master key is set
- account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null,
+ null,
account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(),
account.getSelfUnidentifiedAccessKey(),
account.isUnrestrictedUnidentifiedAccess(),
}
public void deleteAccount() throws IOException {
+ try {
+ pinHelper.removeRegistrationLockPin();
+ } catch (UnauthenticatedResponseException e) {
+ logger.warn("Failed to remove registration lock pin");
+ }
+ account.setRegistrationLockPin(null, null);
+
dependencies.getAccountManager().deleteAccount();
account.setRegistered(false);
}
+ public void submitRateLimitRecaptchaChallenge(String challenge, String captcha) throws IOException {
+ dependencies.getAccountManager().submitRateLimitRecaptchaChallenge(challenge, captcha);
+ }
+
public List<Device> getLinkedDevices() throws IOException {
var devices = dependencies.getAccountManager().getDevices();
account.setMultiDevice(devices.size() > 1);
}
private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException {
- var identityKeyPair = getIdentityKeyPair();
+ var identityKeyPair = account.getIdentityKeyPair();
var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode();
dependencies.getAccountManager()
}
void refreshPreKeys() throws IOException {
- var oneTimePreKeys = generatePreKeys();
- final var identityKeyPair = getIdentityKeyPair();
- var signedPreKeyRecord = generateSignedPreKey(identityKeyPair);
-
- dependencies.getAccountManager().setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys);
- }
-
- private List<PreKeyRecord> generatePreKeys() {
- final var offset = account.getPreKeyIdOffset();
-
- var records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE);
- account.addPreKeys(records);
-
- return records;
- }
-
- private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) {
- final var signedPreKeyId = account.getNextSignedPreKeyId();
-
- var record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId);
- account.addSignedPreKey(record);
-
- return record;
+ preKeyHelper.refreshPreKeys();
}
public Profile getRecipientProfile(RecipientId recipientId) {
public void requestAllSyncData() throws IOException {
syncHelper.requestAllSyncData();
+ retrieveRemoteStorage();
+ }
+
+ void retrieveRemoteStorage() throws IOException {
+ if (account.getStorageKey() != null) {
+ storageHelper.readDataFromStorage();
+ }
}
private byte[] getSenderCertificate() {
final var signalWebSocket = dependencies.getSignalWebSocket();
signalWebSocket.connect();
- var hasCaughtUpWithOldMessages = false;
+ hasCaughtUpWithOldMessages = false;
while (!Thread.interrupted()) {
SignalServiceEnvelope envelope;
// store message on disk, before acknowledging receipt to the server
cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId);
});
- logger.debug("New message received from server");
if (result.isPresent()) {
envelope = result.get();
+ logger.debug("New message received from server");
} else {
- // Received indicator that server queue is empty
- hasCaughtUpWithOldMessages = true;
-
+ logger.debug("Received indicator that server queue is empty");
handleQueuedActions(queuedActions);
queuedActions.clear();
+ hasCaughtUpWithOldMessages = true;
+ synchronized (this) {
+ this.notifyAll();
+ }
+
// Continue to wait another timeout for new messages
continue;
}
handleQueuedActions(queuedActions);
}
+ public boolean hasCaughtUpWithOldMessages() {
+ return hasCaughtUpWithOldMessages;
+ }
+
private void handleQueuedActions(final Collection<HandleAction> queuedActions) {
+ var interrupted = false;
for (var action : queuedActions) {
try {
action.execute(context);
} catch (Throwable e) {
- if (e instanceof AssertionError && e.getCause() instanceof InterruptedException) {
- Thread.currentThread().interrupt();
+ if ((e instanceof AssertionError || e instanceof RuntimeException)
+ && e.getCause() instanceof InterruptedException) {
+ interrupted = true;
+ continue;
}
logger.warn("Message action failed.", e);
}
}
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
}
public boolean isContactBlocked(final RecipientIdentifier.Single recipient) {
) {
return Utils.computeSafetyNumber(capabilities.isUuid(),
account.getSelfAddress(),
- getIdentityKeyPair().getPublicKey(),
+ account.getIdentityKeyPair().getPublicKey(),
theirAddress,
theirIdentityKey);
}
public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) {
- if (address.matches(account.getSelfAddress())) {
- return account.getSelfAddress();
- }
-
return resolveSignalServiceAddress(resolveRecipient(address));
}