]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java
Ensure messages are created with a unique timestamp
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / internal / ManagerImpl.java
index 426429dac87e33a031ef6c1c0a4157cba2579c9a..0a67e0a0a3eeadc8b6402d6f5c6b83d5eb7b2d29 100644 (file)
@@ -23,6 +23,7 @@ import org.asamk.signal.manager.api.CaptchaRejectedException;
 import org.asamk.signal.manager.api.CaptchaRequiredException;
 import org.asamk.signal.manager.api.Configuration;
 import org.asamk.signal.manager.api.Device;
+import org.asamk.signal.manager.api.DeviceLimitExceededException;
 import org.asamk.signal.manager.api.DeviceLinkUrl;
 import org.asamk.signal.manager.api.Group;
 import org.asamk.signal.manager.api.GroupId;
@@ -34,6 +35,7 @@ import org.asamk.signal.manager.api.IdentityVerificationCode;
 import org.asamk.signal.manager.api.InactiveGroupLinkException;
 import org.asamk.signal.manager.api.IncorrectPinException;
 import org.asamk.signal.manager.api.InvalidDeviceLinkException;
+import org.asamk.signal.manager.api.InvalidNumberException;
 import org.asamk.signal.manager.api.InvalidStickerException;
 import org.asamk.signal.manager.api.InvalidUsernameException;
 import org.asamk.signal.manager.api.LastGroupAdminException;
@@ -86,12 +88,12 @@ import org.asamk.signal.manager.storage.stickers.StickerPack;
 import org.asamk.signal.manager.util.AttachmentUtils;
 import org.asamk.signal.manager.util.KeyUtils;
 import org.asamk.signal.manager.util.MimeUtils;
+import org.asamk.signal.manager.util.PhoneNumberFormatter;
 import org.asamk.signal.manager.util.StickerUtils;
 import org.signal.libsignal.protocol.InvalidMessageException;
 import org.signal.libsignal.usernames.BaseUsernameException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.whispersystems.signalservice.api.SignalSessionLock;
 import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
 import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
 import org.whispersystems.signalservice.api.messages.SignalServicePreview;
@@ -105,8 +107,6 @@ import org.whispersystems.signalservice.api.push.exceptions.CdsiResourceExhauste
 import org.whispersystems.signalservice.api.push.exceptions.UsernameMalformedException;
 import org.whispersystems.signalservice.api.push.exceptions.UsernameTakenException;
 import org.whispersystems.signalservice.api.util.DeviceNameUtil;
-import org.whispersystems.signalservice.api.util.InvalidNumberException;
-import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
 import org.whispersystems.signalservice.api.util.StreamDetails;
 import org.whispersystems.signalservice.internal.util.Hex;
 import org.whispersystems.signalservice.internal.util.Util;
@@ -131,13 +131,18 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import io.reactivex.rxjava3.disposables.CompositeDisposable;
 import io.reactivex.rxjava3.schedulers.Schedulers;
+import okio.Utf8;
+
+import static org.asamk.signal.manager.config.ServiceConfig.MAX_MESSAGE_SIZE_BYTES;
+import static org.asamk.signal.manager.util.Utils.handleResponseException;
+import static org.signal.core.util.StringExtensionsKt.splitByByteLength;
 
 public class ManagerImpl implements Manager {
 
@@ -156,6 +161,7 @@ public class ManagerImpl implements Manager {
     private final List<Runnable> closedListeners = new ArrayList<>();
     private final List<Runnable> addressChangedListeners = new ArrayList<>();
     private final CompositeDisposable disposable = new CompositeDisposable();
+    private final AtomicLong lastMessageTimestamp = new AtomicLong();
 
     public ManagerImpl(
             SignalAccount account,
@@ -166,15 +172,7 @@ public class ManagerImpl implements Manager {
     ) {
         this.account = account;
 
-        final var sessionLock = new SignalSessionLock() {
-            private final ReentrantLock LEGACY_LOCK = new ReentrantLock();
-
-            @Override
-            public Lock acquire() {
-                LEGACY_LOCK.lock();
-                return LEGACY_LOCK::unlock;
-            }
-        };
+        final var sessionLock = new ReentrantSignalSessionLock();
         this.dependencies = new SignalDependencies(serviceEnvironmentConfig,
                 userAgent,
                 account.getCredentialsProvider(),
@@ -415,7 +413,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void startChangeNumber(
-            String newNumber, boolean voiceVerification, String captcha
+            String newNumber,
+            boolean voiceVerification,
+            String captcha
     ) throws RateLimitException, IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, NotPrimaryDeviceException, VerificationMethodNotAvailableException {
         if (!account.isPrimaryDevice()) {
             throw new NotPrimaryDeviceException();
@@ -425,7 +425,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void finishChangeNumber(
-            String newNumber, String verificationCode, String pin
+            String newNumber,
+            String verificationCode,
+            String pin
     ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException {
         if (!account.isPrimaryDevice()) {
             throw new NotPrimaryDeviceException();
@@ -445,12 +447,13 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void submitRateLimitRecaptchaChallenge(
-            String challenge, String captcha
+            String challenge,
+            String captcha
     ) throws IOException, CaptchaRejectedException {
-        captcha = captcha == null ? null : captcha.replace("signalcaptcha://", "");
+        captcha = captcha == null ? "" : captcha.replace("signalcaptcha://", "");
 
         try {
-            dependencies.getAccountManager().submitRateLimitRecaptchaChallenge(challenge, captcha);
+            handleResponseException(dependencies.getRateLimitChallengeApi().submitCaptchaChallenge(challenge, captcha));
         } catch (org.whispersystems.signalservice.internal.push.exceptions.CaptchaRejectedException ignored) {
             throw new CaptchaRejectedException();
         }
@@ -458,7 +461,7 @@ public class ManagerImpl implements Manager {
 
     @Override
     public List<Device> getLinkedDevices() throws IOException {
-        var devices = dependencies.getAccountManager().getDevices();
+        var devices = handleResponseException(dependencies.getLinkDeviceApi().getDevices());
         account.setMultiDevice(devices.size() > 1);
         var identityKey = account.getAciIdentityKeyPair().getPrivateKey();
         return devices.stream().map(d -> {
@@ -487,7 +490,7 @@ public class ManagerImpl implements Manager {
     }
 
     @Override
-    public void addDeviceLink(DeviceLinkUrl linkUrl) throws IOException, InvalidDeviceLinkException, NotPrimaryDeviceException {
+    public void addDeviceLink(DeviceLinkUrl linkUrl) throws IOException, InvalidDeviceLinkException, NotPrimaryDeviceException, DeviceLimitExceededException {
         if (!account.isPrimaryDevice()) {
             throw new NotPrimaryDeviceException();
         }
@@ -525,7 +528,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendGroupMessageResults quitGroup(
-            GroupId groupId, Set<RecipientIdentifier.Single> groupAdmins
+            GroupId groupId,
+            Set<RecipientIdentifier.Single> groupAdmins
     ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException, UnregisteredRecipientException {
         final var newAdmins = context.getRecipientHelper().resolveRecipients(groupAdmins);
         return context.getGroupHelper().quitGroup(groupId, newAdmins);
@@ -543,7 +547,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public Pair<GroupId, SendGroupMessageResults> createGroup(
-            String name, Set<RecipientIdentifier.Single> members, String avatarFile
+            String name,
+            Set<RecipientIdentifier.Single> members,
+            String avatarFile
     ) throws IOException, AttachmentInvalidException, UnregisteredRecipientException {
         return context.getGroupHelper()
                 .createGroup(name,
@@ -553,7 +559,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendGroupMessageResults updateGroup(
-            final GroupId groupId, final UpdateGroup updateGroup
+            final GroupId groupId,
+            final UpdateGroup updateGroup
     ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException, UnregisteredRecipientException {
         return context.getGroupHelper()
                 .updateGroup(groupId,
@@ -593,8 +600,28 @@ public class ManagerImpl implements Manager {
         return context.getGroupHelper().joinGroup(inviteLinkUrl);
     }
 
+    private long getNextMessageTimestamp() {
+        while (true) {
+            final var last = lastMessageTimestamp.get();
+            final var timestamp = System.currentTimeMillis();
+            if (last == timestamp) {
+                try {
+                    Thread.sleep(1);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+                continue;
+            }
+            if (lastMessageTimestamp.compareAndSet(last, timestamp)) {
+                return timestamp;
+            }
+        }
+    }
+
     private SendMessageResults sendMessage(
-            SignalServiceDataMessage.Builder messageBuilder, Set<RecipientIdentifier> recipients, boolean notifySelf
+            SignalServiceDataMessage.Builder messageBuilder,
+            Set<RecipientIdentifier> recipients,
+            boolean notifySelf
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
         return sendMessage(messageBuilder, recipients, notifySelf, Optional.empty());
     }
@@ -606,7 +633,7 @@ public class ManagerImpl implements Manager {
             Optional<Long> editTargetTimestamp
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
         var results = new HashMap<RecipientIdentifier, List<SendMessageResult>>();
-        long timestamp = System.currentTimeMillis();
+        long timestamp = getNextMessageTimestamp();
         messageBuilder.withTimestamp(timestamp);
         for (final var recipient : recipients) {
             if (recipient instanceof RecipientIdentifier.NoteToSelf || (
@@ -642,10 +669,11 @@ public class ManagerImpl implements Manager {
     }
 
     private SendMessageResults sendTypingMessage(
-            SignalServiceTypingMessage.Action action, Set<RecipientIdentifier> recipients
+            SignalServiceTypingMessage.Action action,
+            Set<RecipientIdentifier> recipients
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
         var results = new HashMap<RecipientIdentifier, List<SendMessageResult>>();
-        final var timestamp = System.currentTimeMillis();
+        final var timestamp = getNextMessageTimestamp();
         for (var recipient : recipients) {
             if (recipient instanceof RecipientIdentifier.Single single) {
                 final var message = new SignalServiceTypingMessage(action, timestamp, Optional.empty());
@@ -669,16 +697,15 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendMessageResults sendTypingMessage(
-            TypingAction action, Set<RecipientIdentifier> recipients
+            TypingAction action,
+            Set<RecipientIdentifier> recipients
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
         return sendTypingMessage(action.toSignalService(), recipients);
     }
 
     @Override
-    public SendMessageResults sendReadReceipt(
-            RecipientIdentifier.Single sender, List<Long> messageIds
-    ) {
-        final var timestamp = System.currentTimeMillis();
+    public SendMessageResults sendReadReceipt(RecipientIdentifier.Single sender, List<Long> messageIds) {
+        final var timestamp = getNextMessageTimestamp();
         var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ,
                 messageIds,
                 timestamp);
@@ -687,10 +714,8 @@ public class ManagerImpl implements Manager {
     }
 
     @Override
-    public SendMessageResults sendViewedReceipt(
-            RecipientIdentifier.Single sender, List<Long> messageIds
-    ) {
-        final var timestamp = System.currentTimeMillis();
+    public SendMessageResults sendViewedReceipt(RecipientIdentifier.Single sender, List<Long> messageIds) {
+        final var timestamp = getNextMessageTimestamp();
         var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED,
                 messageIds,
                 timestamp);
@@ -704,8 +729,15 @@ public class ManagerImpl implements Manager {
             final SignalServiceReceiptMessage receiptMessage
     ) {
         try {
-            final var result = context.getSendHelper()
-                    .sendReceiptMessage(receiptMessage, context.getRecipientHelper().resolveRecipient(sender));
+            final var recipientId = context.getRecipientHelper().resolveRecipient(sender);
+            final var result = context.getSendHelper().sendReceiptMessage(receiptMessage, recipientId);
+
+            final var serviceId = account.getRecipientAddressResolver()
+                    .resolveRecipientAddress(recipientId)
+                    .serviceId();
+            if (serviceId.isPresent()) {
+                context.getSyncHelper().sendSyncReceiptMessage(serviceId.get(), receiptMessage);
+            }
             return new SendMessageResults(timestamp, Map.of(sender, List.of(toSendMessageResult(result))));
         } catch (UnregisteredRecipientException e) {
             return new SendMessageResults(timestamp,
@@ -715,7 +747,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendMessageResults sendMessage(
-            Message message, Set<RecipientIdentifier> recipients, boolean notifySelf
+            Message message,
+            Set<RecipientIdentifier> recipients,
+            boolean notifySelf
     ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException {
         final var selfProfile = context.getProfileHelper().getSelfProfile();
         if (selfProfile == null || selfProfile.getDisplayName().isEmpty()) {
@@ -729,7 +763,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendMessageResults sendEditMessage(
-            Message message, Set<RecipientIdentifier> recipients, long editTargetTimestamp
+            Message message,
+            Set<RecipientIdentifier> recipients,
+            long editTargetTimestamp
     ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException {
         final var messageBuilder = SignalServiceDataMessage.newBuilder();
         applyMessage(messageBuilder, message);
@@ -737,25 +773,41 @@ public class ManagerImpl implements Manager {
     }
 
     private void applyMessage(
-            final SignalServiceDataMessage.Builder messageBuilder, final Message message
+            final SignalServiceDataMessage.Builder messageBuilder,
+            final Message message
     ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException, InvalidStickerException {
         final var additionalAttachments = new ArrayList<SignalServiceAttachment>();
-        if (message.messageText().length() > 2000) {
-            final var messageBytes = message.messageText().getBytes(StandardCharsets.UTF_8);
-            final var textAttachment = AttachmentUtils.createAttachmentStream(new StreamDetails(new ByteArrayInputStream(
-                    messageBytes), MimeUtils.LONG_TEXT, messageBytes.length), Optional.empty());
-            messageBuilder.withBody(message.messageText().substring(0, 2000));
-            additionalAttachments.add(context.getAttachmentHelper().uploadAttachment(textAttachment));
+        if (Utf8.size(message.messageText()) > MAX_MESSAGE_SIZE_BYTES) {
+            final var result = splitByByteLength(message.messageText(), MAX_MESSAGE_SIZE_BYTES);
+            final var trimmed = result.getFirst();
+            final var remainder = result.getSecond();
+            if (remainder != null) {
+                final var messageBytes = message.messageText().getBytes(StandardCharsets.UTF_8);
+                final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec();
+                final var streamDetails = new StreamDetails(new ByteArrayInputStream(messageBytes),
+                        MimeUtils.LONG_TEXT,
+                        messageBytes.length);
+                final var textAttachment = AttachmentUtils.createAttachmentStream(streamDetails,
+                        Optional.empty(),
+                        uploadSpec);
+                messageBuilder.withBody(trimmed);
+                additionalAttachments.add(context.getAttachmentHelper().uploadAttachment(textAttachment));
+            } else {
+                messageBuilder.withBody(message.messageText());
+            }
         } else {
             messageBuilder.withBody(message.messageText());
         }
         if (!message.attachments().isEmpty()) {
+            final var uploadedAttachments = context.getAttachmentHelper().uploadAttachments(message.attachments());
             if (!additionalAttachments.isEmpty()) {
-                additionalAttachments.addAll(context.getAttachmentHelper().uploadAttachments(message.attachments()));
+                additionalAttachments.addAll(uploadedAttachments);
                 messageBuilder.withAttachments(additionalAttachments);
             } else {
-                messageBuilder.withAttachments(context.getAttachmentHelper().uploadAttachments(message.attachments()));
+                messageBuilder.withAttachments(uploadedAttachments);
             }
+        } else if (!additionalAttachments.isEmpty()) {
+            messageBuilder.withAttachments(additionalAttachments);
         }
         if (!message.mentions().isEmpty()) {
             messageBuilder.withMentions(resolveMentions(message.mentions()));
@@ -800,11 +852,15 @@ public class ManagerImpl implements Manager {
             if (streamDetails == null) {
                 throw new InvalidStickerException("Missing local sticker file");
             }
+            final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec();
+            final var stickerAttachment = AttachmentUtils.createAttachmentStream(streamDetails,
+                    Optional.empty(),
+                    uploadSpec);
             messageBuilder.withSticker(new SignalServiceDataMessage.Sticker(packId.serialize(),
                     stickerPack.packKey(),
                     stickerId,
                     manifestSticker.emoji(),
-                    AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty())));
+                    stickerAttachment));
         }
         if (!message.previews().isEmpty()) {
             final var previews = new ArrayList<SignalServicePreview>(message.previews().size());
@@ -842,7 +898,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendMessageResults sendRemoteDeleteMessage(
-            long targetSentTimestamp, Set<RecipientIdentifier> recipients
+            long targetSentTimestamp,
+            Set<RecipientIdentifier> recipients
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
         var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
         final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete);
@@ -852,7 +909,7 @@ public class ManagerImpl implements Manager {
                         .deleteEntryForRecipientNonGroup(targetSentTimestamp, ACI.from(u.uuid()));
             } else if (recipient instanceof RecipientIdentifier.Pni pni) {
                 account.getMessageSendLogStore()
-                        .deleteEntryForRecipientNonGroup(targetSentTimestamp, PNI.parseOrThrow(pni.pni()));
+                        .deleteEntryForRecipientNonGroup(targetSentTimestamp, PNI.from(pni.pni()));
             } else if (recipient instanceof RecipientIdentifier.Single r) {
                 try {
                     final var recipientId = context.getRecipientHelper().resolveRecipient(r);
@@ -894,7 +951,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendMessageResults sendPaymentNotificationMessage(
-            byte[] receipt, String note, RecipientIdentifier.Single recipient
+            byte[] receipt,
+            String note,
+            RecipientIdentifier.Single recipient
     ) throws IOException {
         final var paymentNotification = new SignalServiceDataMessage.PaymentNotification(receipt, note);
         final var payment = new SignalServiceDataMessage.Payment(paymentNotification, null);
@@ -937,7 +996,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public SendMessageResults sendMessageRequestResponse(
-            final MessageRequestResponse.Type type, final Set<RecipientIdentifier> recipients
+            final MessageRequestResponse.Type type,
+            final Set<RecipientIdentifier> recipients
     ) {
         var results = new HashMap<RecipientIdentifier, List<SendMessageResult>>();
         for (final var recipient : recipients) {
@@ -1000,19 +1060,30 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void setContactName(
-            RecipientIdentifier.Single recipient, String givenName, final String familyName
+            final RecipientIdentifier.Single recipient,
+            final String givenName,
+            final String familyName,
+            final String nickGivenName,
+            final String nickFamilyName,
+            final String note
     ) throws NotPrimaryDeviceException, UnregisteredRecipientException {
         if (!account.isPrimaryDevice()) {
             throw new NotPrimaryDeviceException();
         }
         context.getContactHelper()
-                .setContactName(context.getRecipientHelper().resolveRecipient(recipient), givenName, familyName);
+                .setContactName(context.getRecipientHelper().resolveRecipient(recipient),
+                        givenName,
+                        familyName,
+                        nickGivenName,
+                        nickFamilyName,
+                        note);
         syncRemoteStorage();
     }
 
     @Override
     public void setContactsBlocked(
-            Collection<RecipientIdentifier.Single> recipients, boolean blocked
+            Collection<RecipientIdentifier.Single> recipients,
+            boolean blocked
     ) throws IOException, UnregisteredRecipientException {
         if (recipients.isEmpty()) {
             return;
@@ -1046,7 +1117,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void setGroupsBlocked(
-            final Collection<GroupId> groupIds, final boolean blocked
+            final Collection<GroupId> groupIds,
+            final boolean blocked
     ) throws GroupNotFoundException, IOException {
         if (groupIds.isEmpty()) {
             return;
@@ -1072,7 +1144,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void setExpirationTimer(
-            RecipientIdentifier.Single recipient, int messageExpirationTimer
+            RecipientIdentifier.Single recipient,
+            int messageExpirationTimer
     ) throws IOException, UnregisteredRecipientException {
         var recipientId = context.getRecipientHelper().resolveRecipient(recipient);
         context.getContactHelper().setExpirationTimer(recipientId, messageExpirationTimer);
@@ -1234,7 +1307,9 @@ public class ManagerImpl implements Manager {
 
     @Override
     public void receiveMessages(
-            Optional<Duration> timeout, Optional<Integer> maxMessages, ReceiveMessageHandler handler
+            Optional<Duration> timeout,
+            Optional<Integer> maxMessages,
+            ReceiveMessageHandler handler
     ) throws IOException, AlreadyReceivingException {
         receiveMessages(timeout.orElse(Duration.ofMinutes(1)), timeout.isPresent(), maxMessages.orElse(null), handler);
     }
@@ -1254,7 +1329,10 @@ public class ManagerImpl implements Manager {
     }
 
     private void receiveMessages(
-            Duration timeout, boolean returnOnTimeout, Integer maxMessages, ReceiveMessageHandler handler
+            Duration timeout,
+            boolean returnOnTimeout,
+            Integer maxMessages,
+            ReceiveMessageHandler handler
     ) throws IOException, AlreadyReceivingException {
         synchronized (messageHandlers) {
             if (isReceiving()) {
@@ -1410,7 +1488,8 @@ public class ManagerImpl implements Manager {
 
     @Override
     public boolean trustIdentityVerified(
-            RecipientIdentifier.Single recipient, IdentityVerificationCode verificationCode
+            RecipientIdentifier.Single recipient,
+            IdentityVerificationCode verificationCode
     ) throws UnregisteredRecipientException {
         return switch (verificationCode) {
             case IdentityVerificationCode.Fingerprint fingerprint -> trustIdentity(recipient,
@@ -1429,7 +1508,8 @@ public class ManagerImpl implements Manager {
     }
 
     private boolean trustIdentity(
-            RecipientIdentifier.Single recipient, Function<RecipientId, Boolean> trustMethod
+            RecipientIdentifier.Single recipient,
+            Function<RecipientId, Boolean> trustMethod
     ) throws UnregisteredRecipientException {
         final var recipientId = context.getRecipientHelper().resolveRecipient(recipient);
         final var updated = trustMethod.apply(recipientId);
@@ -1525,7 +1605,8 @@ public class ManagerImpl implements Manager {
         context.close();
         executor.close();
 
-        dependencies.getSignalWebSocket().disconnect();
+        dependencies.getAuthenticatedSignalWebSocket().disconnect();
+        dependencies.getUnauthenticatedSignalWebSocket().disconnect();
         dependencies.getPushServiceSocket().close();
         disposable.dispose();