From 5743cf4455d98ac16231866c43c877c10c016a89 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 30 Aug 2021 13:33:54 +0200 Subject: [PATCH 1/5] Improve dbus register error message if called with invalid number --- .../java/org/asamk/signal/manager/storage/SignalAccount.java | 3 ++- src/main/java/org/asamk/signal/BaseConfig.java | 2 +- .../java/org/asamk/signal/dbus/DbusSignalControlImpl.java | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c972c2c7..4e240887 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -21,6 +21,7 @@ import org.asamk.signal.manager.storage.protocol.SignalProtocolStore; import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.LegacyRecipientStore; import org.asamk.signal.manager.storage.recipients.Profile; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientStore; import org.asamk.signal.manager.storage.sessions.SessionStore; @@ -789,7 +790,7 @@ public class SignalAccount implements Closeable { } public RecipientId getSelfRecipientId() { - return recipientStore.resolveRecipientTrusted(getSelfAddress()); + return recipientStore.resolveRecipientTrusted(new RecipientAddress(uuid, username)); } public String getEncryptedDeviceName() { diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index bb8db7d2..04c1ac8a 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -5,7 +5,7 @@ public class BaseConfig { public final static String PROJECT_NAME = BaseConfig.class.getPackage().getImplementationTitle(); public final static String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); - final static String USER_AGENT_SIGNAL_ANDROID = "Signal-Android/5.12.4"; + final static String USER_AGENT_SIGNAL_ANDROID = "Signal-Android/5.22.3"; final static String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + "/" + PROJECT_VERSION; diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index 35f530b0..6ec8d964 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -13,6 +13,7 @@ import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.IOException; import java.net.URI; @@ -99,6 +100,10 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { public void registerWithCaptcha( final String number, final boolean voiceVerification, final String captcha ) throws Error.Failure, Error.InvalidNumber { + if (!PhoneNumberFormatter.isValidNumber(number, null)) { + throw new SignalControl.Error.InvalidNumber( + "Invalid username (phone number), make sure you include the country code."); + } try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) { registrationManager.register(voiceVerification, captcha); } catch (CaptchaRequiredException e) { From 32150b1aaa32888c5179d664a9a497a13d3f4bfa Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 30 Aug 2021 13:39:27 +0200 Subject: [PATCH 2/5] Move all message decryption to IncomingMessageHandler --- .../org/asamk/signal/manager/Manager.java | 47 +++++++-------- .../helper/IncomingMessageHandler.java | 59 +++++++++++++++---- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 9e38853b..87b89913 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -58,7 +58,6 @@ import org.asamk.signal.manager.storage.stickers.StickerPackId; import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.StickerUtils; import org.asamk.signal.manager.util.Utils; -import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; @@ -818,37 +817,33 @@ public class Manager implements Closeable { ) { var envelope = cachedMessage.loadEnvelope(); if (envelope == null) { + cachedMessage.delete(); return null; } - SignalServiceContent content = null; - List actions = null; - if (!envelope.isReceipt()) { - try { - content = dependencies.getCipher().decrypt(envelope); - } catch (ProtocolUntrustedIdentityException e) { - if (System.currentTimeMillis() - envelope.getServerDeliveredTimestamp() > 1000L * 60 * 60 * 24 * 30) { - // Envelope is more than a month old, cleaning up. - cachedMessage.delete(); - return null; - } - if (!envelope.hasSourceUuid()) { - final var identifier = e.getSender(); - final var recipientId = account.getRecipientStore().resolveRecipient(identifier); - try { - account.getMessageCache().replaceSender(cachedMessage, recipientId); - } catch (IOException ioException) { - logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage()); - } - } - return null; - } catch (Exception er) { - // All other errors are not recoverable, so delete the cached message + + final var result = incomingMessageHandler.handleRetryEnvelope(envelope, ignoreAttachments, handler); + final var actions = result.first(); + final var exception = result.second(); + + if (exception instanceof UntrustedIdentityException) { + if (System.currentTimeMillis() - envelope.getServerDeliveredTimestamp() > 1000L * 60 * 60 * 24 * 30) { + // Envelope is more than a month old, cleaning up. cachedMessage.delete(); return null; } - actions = incomingMessageHandler.handleMessage(envelope, content, ignoreAttachments); + if (!envelope.hasSourceUuid()) { + final var identifier = ((UntrustedIdentityException) exception).getSender(); + final var recipientId = account.getRecipientStore().resolveRecipient(identifier); + try { + account.getMessageCache().replaceSender(cachedMessage, recipientId); + } catch (IOException ioException) { + logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage()); + } + } + return null; } - handler.handleMessage(envelope, content, null); + + // If successful and for all other errors that are not recoverable, delete the cached message cachedMessage.delete(); return actions; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index f28b3638..57b71ee1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -79,6 +79,28 @@ public final class IncomingMessageHandler { this.jobExecutor = jobExecutor; } + public Pair, Exception> handleRetryEnvelope( + final SignalServiceEnvelope envelope, + final boolean ignoreAttachments, + final Manager.ReceiveMessageHandler handler + ) { + SignalServiceContent content = null; + if (!envelope.isReceipt()) { + try { + content = dependencies.getCipher().decrypt(envelope); + } catch (ProtocolUntrustedIdentityException e) { + final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); + final var exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress( + recipientId), e.getSenderDevice()); + return new Pair<>(List.of(), exception); + } catch (Exception e) { + return new Pair<>(List.of(), e); + } + } + final var actions = checkAndHandleMessage(envelope, content, ignoreAttachments, handler, null); + return new Pair<>(actions, null); + } + public Pair, Exception> handleEnvelope( final SignalServiceEnvelope envelope, final boolean ignoreAttachments, @@ -108,35 +130,48 @@ public final class IncomingMessageHandler { } catch (Exception e) { exception = e; } - - if (!envelope.hasSourceUuid() && content != null) { - // Store uuid if we don't have it already - // address/uuid is validated by unidentified sender certificate - account.getRecipientStore().resolveRecipientTrusted(content.getSender()); - } } + actions.addAll(checkAndHandleMessage(envelope, content, ignoreAttachments, handler, exception)); + return new Pair<>(actions, exception); + } + + private List checkAndHandleMessage( + final SignalServiceEnvelope envelope, + final SignalServiceContent content, + final boolean ignoreAttachments, + final Manager.ReceiveMessageHandler handler, + final Exception exception + ) { + if (!envelope.hasSourceUuid() && content != null) { + // Store uuid if we don't have it already + // address/uuid is validated by unidentified sender certificate + account.getRecipientStore().resolveRecipientTrusted(content.getSender()); + } if (isMessageBlocked(envelope, content)) { logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp()); + return List.of(); } else if (isNotAllowedToSendToGroup(envelope, content)) { logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}", (envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(), envelope.getTimestamp()); + return List.of(); } else { - actions.addAll(handleMessage(envelope, content, ignoreAttachments)); + List actions; + if (content != null) { + actions = handleMessage(envelope, content, ignoreAttachments); + } else { + actions = List.of(); + } handler.handleMessage(envelope, content, exception); + return actions; } - return new Pair<>(actions, exception); } public List handleMessage( SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments ) { var actions = new ArrayList(); - if (content == null) { - return actions; - } - final RecipientId sender; if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { sender = recipientResolver.resolveRecipient(envelope.getSourceAddress()); From 7a3522dc010fd15601b040547807c617276ec6c2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 30 Aug 2021 13:55:25 +0200 Subject: [PATCH 3/5] Prevent endless loop when receiving contact sync message --- .../java/org/asamk/signal/manager/helper/SyncHelper.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index 461706f0..3cc76b28 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -222,8 +222,13 @@ public class SyncHelper { try { c = s.read(); } catch (IOException e) { - logger.warn("Sync contacts contained invalid contact, ignoring: {}", e.getMessage()); - continue; + if (e.getMessage() != null && e.getMessage().contains("Missing contact address!")) { + logger.warn("Sync contacts contained invalid contact, ignoring: {}", e.getMessage()); + continue; + } else { + logger.warn("Failed to read sync contacts", e); + break; + } } if (c == null) { break; From 1f0c2d5c782d2d2663dc8b4147b1fbb8df53bc97 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 30 Aug 2021 14:12:39 +0200 Subject: [PATCH 4/5] Remove registration lock pin before deleting account --- lib/src/main/java/org/asamk/signal/manager/Manager.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 87b89913..6d91ba11 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -385,6 +385,13 @@ public class Manager implements Closeable { } 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); From 626406a43c169105c3485749249ffbfd715e3441 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 30 Aug 2021 15:07:12 +0200 Subject: [PATCH 5/5] Create libsignal dependencies only when required --- .../org/asamk/signal/manager/Manager.java | 12 +- .../signal/manager/SignalDependencies.java | 202 +++++++++++------- .../helper/MessageReceiverProvider.java | 8 - .../signal/manager/helper/ProfileHelper.java | 10 +- .../helper/ProfileServiceProvider.java | 8 - .../asamk/signal/manager/jobs/Context.java | 20 +- .../manager/jobs/RetrieveStickerPackJob.java | 8 +- 7 files changed, 138 insertions(+), 130 deletions(-) delete mode 100644 lib/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java delete mode 100644 lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 6d91ba11..c40fa7cd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -164,8 +164,7 @@ public class Manager implements Closeable { return LEGACY_LOCK::unlock; } }; - this.dependencies = new SignalDependencies(account.getSelfAddress(), - serviceEnvironmentConfig, + this.dependencies = new SignalDependencies(serviceEnvironmentConfig, userAgent, credentialsProvider, account.getSignalProtocolStore(), @@ -186,8 +185,6 @@ public class Manager implements Closeable { avatarStore, account.getProfileStore()::getProfileKey, unidentifiedAccessHelper::getAccessFor, - dependencies::getProfileService, - dependencies::getMessageReceiver, this::resolveSignalServiceAddress); final GroupV2Helper groupV2Helper = new GroupV2Helper(profileHelper::getRecipientProfileKeyCredential, this::getRecipientProfile, @@ -220,8 +217,7 @@ public class Manager implements Closeable { this::resolveSignalServiceAddress); this.context = new Context(account, - dependencies.getAccountManager(), - dependencies.getMessageReceiver(), + dependencies, stickerPackStore, sendHelper, groupHelper, @@ -1149,10 +1145,6 @@ public class Manager implements Closeable { } public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) { - if (address.matches(account.getSelfAddress())) { - return account.getSelfAddress(); - } - return resolveSignalServiceAddress(resolveRecipient(address)); } diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index fef8351f..970a6741 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -3,7 +3,6 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.signal.libsignal.metadata.certificate.CertificateValidator; -import org.signal.zkgroup.profiles.ClientZkProfileOperations; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.SignalServiceAccountManager; @@ -18,32 +17,41 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.services.ProfileService; -import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.websocket.WebSocketFactory; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; public class SignalDependencies { - private final SignalServiceAccountManager accountManager; - private final GroupsV2Api groupsV2Api; - private final GroupsV2Operations groupsV2Operations; + private final Object LOCK = new Object(); - private final SignalWebSocket signalWebSocket; - private final SignalServiceMessageReceiver messageReceiver; - private final SignalServiceMessageSender messageSender; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; + private final String userAgent; + private final DynamicCredentialsProvider credentialsProvider; + private final SignalServiceDataStore dataStore; + private final ExecutorService executor; + private final SignalSessionLock sessionLock; - private final KeyBackupService keyBackupService; - private final ProfileService profileService; - private final SignalServiceCipher cipher; + private SignalServiceAccountManager accountManager; + private GroupsV2Api groupsV2Api; + private GroupsV2Operations groupsV2Operations; + private ClientZkOperations clientZkOperations; + + private SignalWebSocket signalWebSocket; + private SignalServiceMessageReceiver messageReceiver; + private SignalServiceMessageSender messageSender; + + private KeyBackupService keyBackupService; + private ProfileService profileService; + private SignalServiceCipher cipher; public SignalDependencies( - final SignalServiceAddress selfAddress, final ServiceEnvironmentConfig serviceEnvironmentConfig, final String userAgent, final DynamicCredentialsProvider credentialsProvider, @@ -51,100 +59,134 @@ public class SignalDependencies { final ExecutorService executor, final SignalSessionLock sessionLock ) { - this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( - serviceEnvironmentConfig.getSignalServiceConfiguration())) : null; - final SleepTimer timer = new UptimeSleepTimer(); - this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), - credentialsProvider, - userAgent, - groupsV2Operations, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); - this.groupsV2Api = accountManager.getGroupsV2Api(); - this.keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), - serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), - serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), - serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), - 10); - final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create( - serviceEnvironmentConfig.getSignalServiceConfiguration()).getProfileOperations() : null; - this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(), - credentialsProvider, - userAgent, - clientZkProfileOperations, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); - - final var healthMonitor = new SignalWebSocketHealthMonitor(timer); - final WebSocketFactory webSocketFactory = new WebSocketFactory() { - @Override - public WebSocketConnection createWebSocket() { - return new WebSocketConnection("normal", - serviceEnvironmentConfig.getSignalServiceConfiguration(), - Optional.of(credentialsProvider), - userAgent, - healthMonitor); - } - - @Override - public WebSocketConnection createUnidentifiedWebSocket() { - return new WebSocketConnection("unidentified", - serviceEnvironmentConfig.getSignalServiceConfiguration(), - Optional.absent(), - userAgent, - healthMonitor); - } - }; - this.signalWebSocket = new SignalWebSocket(webSocketFactory); - healthMonitor.monitor(signalWebSocket); - this.profileService = new ProfileService(clientZkProfileOperations, messageReceiver, signalWebSocket); - - final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); - this.cipher = new SignalServiceCipher(selfAddress, dataStore, sessionLock, certificateValidator); - this.messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), - credentialsProvider, - dataStore, - sessionLock, - userAgent, - signalWebSocket, - Optional.absent(), - clientZkProfileOperations, - executor, - ServiceConfig.MAX_ENVELOPE_SIZE, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); + this.serviceEnvironmentConfig = serviceEnvironmentConfig; + this.userAgent = userAgent; + this.credentialsProvider = credentialsProvider; + this.dataStore = dataStore; + this.executor = executor; + this.sessionLock = sessionLock; } public SignalServiceAccountManager getAccountManager() { - return accountManager; + return getOrCreate(() -> accountManager, + () -> accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), + credentialsProvider, + userAgent, + getGroupsV2Operations(), + ServiceConfig.AUTOMATIC_NETWORK_RETRY)); } public GroupsV2Api getGroupsV2Api() { - return groupsV2Api; + return getOrCreate(() -> groupsV2Api, () -> groupsV2Api = getAccountManager().getGroupsV2Api()); } public GroupsV2Operations getGroupsV2Operations() { - return groupsV2Operations; + return getOrCreate(() -> groupsV2Operations, + () -> groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( + serviceEnvironmentConfig.getSignalServiceConfiguration())) : null); + } + + private ClientZkOperations getClientZkOperations() { + return getOrCreate(() -> clientZkOperations, + () -> clientZkOperations = capabilities.isGv2() + ? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()) + : null); } public SignalWebSocket getSignalWebSocket() { - return signalWebSocket; + return getOrCreate(() -> signalWebSocket, () -> { + final var timer = new UptimeSleepTimer(); + final var healthMonitor = new SignalWebSocketHealthMonitor(timer); + final var webSocketFactory = new WebSocketFactory() { + @Override + public WebSocketConnection createWebSocket() { + return new WebSocketConnection("normal", + serviceEnvironmentConfig.getSignalServiceConfiguration(), + Optional.of(credentialsProvider), + userAgent, + healthMonitor); + } + + @Override + public WebSocketConnection createUnidentifiedWebSocket() { + return new WebSocketConnection("unidentified", + serviceEnvironmentConfig.getSignalServiceConfiguration(), + Optional.absent(), + userAgent, + healthMonitor); + } + }; + signalWebSocket = new SignalWebSocket(webSocketFactory); + healthMonitor.monitor(signalWebSocket); + }); } public SignalServiceMessageReceiver getMessageReceiver() { - return messageReceiver; + return getOrCreate(() -> messageReceiver, + () -> messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(), + credentialsProvider, + userAgent, + getClientZkOperations().getProfileOperations(), + ServiceConfig.AUTOMATIC_NETWORK_RETRY)); } public SignalServiceMessageSender getMessageSender() { - return messageSender; + return getOrCreate(() -> messageSender, + () -> messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), + credentialsProvider, + dataStore, + sessionLock, + userAgent, + getSignalWebSocket(), + Optional.absent(), + getClientZkOperations().getProfileOperations(), + executor, + ServiceConfig.MAX_ENVELOPE_SIZE, + ServiceConfig.AUTOMATIC_NETWORK_RETRY)); } public KeyBackupService getKeyBackupService() { - return keyBackupService; + return getOrCreate(() -> keyBackupService, + () -> keyBackupService = getAccountManager().getKeyBackupService(ServiceConfig.getIasKeyStore(), + serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), + serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), + serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), + 10)); } public ProfileService getProfileService() { - return profileService; + return getOrCreate(() -> profileService, + () -> profileService = new ProfileService(getClientZkOperations().getProfileOperations(), + getMessageReceiver(), + getSignalWebSocket())); } public SignalServiceCipher getCipher() { - return cipher; + return getOrCreate(() -> cipher, () -> { + final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); + final var address = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164()); + cipher = new SignalServiceCipher(address, dataStore, sessionLock, certificateValidator); + }); + } + + private T getOrCreate(Supplier 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(); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java deleted file mode 100644 index 9a18a5e4..00000000 --- a/lib/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.asamk.signal.manager.helper; - -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; - -public interface MessageReceiverProvider { - - SignalServiceMessageReceiver getMessageReceiver(); -} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 52154798..d4f8ae5d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -45,8 +45,6 @@ public final class ProfileHelper { private final AvatarStore avatarStore; private final ProfileKeyProvider profileKeyProvider; private final UnidentifiedAccessProvider unidentifiedAccessProvider; - private final ProfileServiceProvider profileServiceProvider; - private final MessageReceiverProvider messageReceiverProvider; private final SignalServiceAddressResolver addressResolver; public ProfileHelper( @@ -55,8 +53,6 @@ public final class ProfileHelper { final AvatarStore avatarStore, final ProfileKeyProvider profileKeyProvider, final UnidentifiedAccessProvider unidentifiedAccessProvider, - final ProfileServiceProvider profileServiceProvider, - final MessageReceiverProvider messageReceiverProvider, final SignalServiceAddressResolver addressResolver ) { this.account = account; @@ -64,8 +60,6 @@ public final class ProfileHelper { this.avatarStore = avatarStore; this.profileKeyProvider = profileKeyProvider; this.unidentifiedAccessProvider = unidentifiedAccessProvider; - this.profileServiceProvider = profileServiceProvider; - this.messageReceiverProvider = messageReceiverProvider; this.addressResolver = addressResolver; } @@ -218,7 +212,7 @@ public final class ProfileHelper { } private SignalServiceProfile retrieveProfileSync(String username) throws IOException { - return messageReceiverProvider.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent()); + return dependencies.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent()); } private ProfileAndCredential retrieveProfileAndCredential( @@ -287,7 +281,7 @@ public final class ProfileHelper { Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType ) { - var profileService = profileServiceProvider.getProfileService(); + var profileService = dependencies.getProfileService(); Single> responseSingle; try { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java deleted file mode 100644 index 4fffb15c..00000000 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.asamk.signal.manager.helper; - -import org.whispersystems.signalservice.api.services.ProfileService; - -public interface ProfileServiceProvider { - - ProfileService getProfileService(); -} diff --git a/lib/src/main/java/org/asamk/signal/manager/jobs/Context.java b/lib/src/main/java/org/asamk/signal/manager/jobs/Context.java index 82c3bf16..142c148a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/jobs/Context.java +++ b/lib/src/main/java/org/asamk/signal/manager/jobs/Context.java @@ -1,19 +1,17 @@ package org.asamk.signal.manager.jobs; +import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.StickerPackStore; import org.asamk.signal.manager.helper.GroupHelper; import org.asamk.signal.manager.helper.ProfileHelper; import org.asamk.signal.manager.helper.SendHelper; import org.asamk.signal.manager.helper.SyncHelper; import org.asamk.signal.manager.storage.SignalAccount; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; public class Context { private final SignalAccount account; - private final SignalServiceAccountManager accountManager; - private final SignalServiceMessageReceiver messageReceiver; + private final SignalDependencies dependencies; private final StickerPackStore stickerPackStore; private final SendHelper sendHelper; private final GroupHelper groupHelper; @@ -22,8 +20,7 @@ public class Context { public Context( final SignalAccount account, - final SignalServiceAccountManager accountManager, - final SignalServiceMessageReceiver messageReceiver, + final SignalDependencies dependencies, final StickerPackStore stickerPackStore, final SendHelper sendHelper, final GroupHelper groupHelper, @@ -31,8 +28,7 @@ public class Context { final ProfileHelper profileHelper ) { this.account = account; - this.accountManager = accountManager; - this.messageReceiver = messageReceiver; + this.dependencies = dependencies; this.stickerPackStore = stickerPackStore; this.sendHelper = sendHelper; this.groupHelper = groupHelper; @@ -44,12 +40,8 @@ public class Context { return account; } - public SignalServiceAccountManager getAccountManager() { - return accountManager; - } - - public SignalServiceMessageReceiver getMessageReceiver() { - return messageReceiver; + public SignalDependencies getDependencies() { + return dependencies; } public StickerPackStore getStickerPackStore() { diff --git a/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java b/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java index 20042451..c27bcafc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java +++ b/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java @@ -32,7 +32,9 @@ public class RetrieveStickerPackJob implements Job { } logger.debug("Retrieving sticker pack {}.", Hex.toStringCondensed(packId.serialize())); try { - final var manifest = context.getMessageReceiver().retrieveStickerManifest(packId.serialize(), packKey); + final var manifest = context.getDependencies() + .getMessageReceiver() + .retrieveStickerManifest(packId.serialize(), packKey); final var stickerIds = new HashSet(); if (manifest.getCover().isPresent()) { @@ -43,7 +45,9 @@ public class RetrieveStickerPackJob implements Job { } for (var id : stickerIds) { - final var inputStream = context.getMessageReceiver().retrieveSticker(packId.serialize(), packKey, id); + final var inputStream = context.getDependencies() + .getMessageReceiver() + .retrieveSticker(packId.serialize(), packKey, id); context.getStickerPackStore().storeSticker(packId, id, o -> IOUtils.copyStream(inputStream, o)); }