X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/5cccf521032954d7ad8e3f70e3cbef2ce1293e85..27dbc671e09c3efbaaf4d0f16d3ccdfb640ae603:/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index c15f1612..daff69e0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -25,8 +25,9 @@ import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; -import org.asamk.signal.manager.api.NotMasterDeviceException; +import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.ReceiveConfig; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResult; @@ -38,6 +39,8 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; +import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -45,13 +48,13 @@ import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; +import org.asamk.signal.manager.helper.AccountFileUpdater; import org.asamk.signal.manager.helper.Context; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.identities.IdentityInfo; -import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; -import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; @@ -61,15 +64,15 @@ import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.StickerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.whispersystems.signalservice.api.messages.SignalServicePreview; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; +import org.whispersystems.signalservice.api.push.ACI; 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.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.signalservice.internal.util.Util; @@ -78,12 +81,14 @@ import java.io.IOException; import java.net.URI; import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @@ -109,20 +114,18 @@ class ManagerImpl implements Manager { private final Set weakHandlers = new HashSet<>(); private final Set messageHandlers = new HashSet<>(); private final List closedListeners = new ArrayList<>(); + private final List addressChangedListeners = new ArrayList<>(); private final CompositeDisposable disposable = new CompositeDisposable(); ManagerImpl( SignalAccount account, PathConfig pathConfig, + AccountFileUpdater accountFileUpdater, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent ) { this.account = account; - final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(), - account.getAccount(), - account.getPassword(), - account.getDeviceId()); final var sessionLock = new SignalSessionLock() { private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); @@ -134,15 +137,28 @@ class ManagerImpl implements Manager { }; this.dependencies = new SignalDependencies(serviceEnvironmentConfig, userAgent, - credentialsProvider, - account.getSignalProtocolStore(), + account.getCredentialsProvider(), + account.getSignalServiceDataStore(), executor, sessionLock); final var avatarStore = new AvatarStore(pathConfig.avatarsPath()); final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath()); final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath()); - this.context = new Context(account, dependencies, avatarStore, attachmentStore, stickerPackStore); + this.context = new Context(account, new AccountFileUpdater() { + @Override + public void updateAccountIdentifiers(final String number, final ACI aci) { + accountFileUpdater.updateAccountIdentifiers(number, aci); + synchronized (addressChangedListeners) { + addressChangedListeners.forEach(Runnable::run); + } + } + + @Override + public void removeAccount() { + accountFileUpdater.removeAccount(); + } + }, dependencies, avatarStore, attachmentStore, stickerPackStore); this.context.getAccountHelper().setUnregisteredListener(this::close); this.context.getReceiveHelper().setAuthenticationFailureListener(this::close); this.context.getReceiveHelper().setCaughtUpWithOldMessagesListener(() -> { @@ -154,9 +170,9 @@ class ManagerImpl implements Manager { logger.trace("Archiving old sessions for {}", recipientId); account.getSessionStore().archiveSessions(recipientId); account.getSenderKeyStore().deleteSharedWith(recipientId); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) @@ -168,7 +184,7 @@ class ManagerImpl implements Manager { @Override public String getSelfNumber() { - return account.getAccount(); + return account.getNumber(); } void checkAccountState() throws IOException { @@ -176,10 +192,10 @@ class ManagerImpl implements Manager { } @Override - public Map> areUsersRegistered(Set numbers) throws IOException { + public Map getUserStatus(Set numbers) throws IOException { final var canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { try { - final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getAccount()); + final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getNumber()); if (!canonicalizedNumber.equals(n)) { logger.debug("Normalized number {} to {}.", n, canonicalizedNumber); } @@ -199,7 +215,14 @@ class ManagerImpl implements Manager { return numbers.stream().collect(Collectors.toMap(n -> n, n -> { final var number = canonicalizedNumbers.get(n); final var aci = registeredUsers.get(number); - return new Pair<>(number.isEmpty() ? null : number, aci == null ? null : aci.uuid()); + final var profile = aci == null + ? null + : context.getProfileHelper() + .getRecipientProfile(account.getRecipientResolver().resolveRecipient(aci)); + return new UserStatus(number.isEmpty() ? null : number, + aci == null ? null : aci.uuid(), + profile != null + && profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED); })); } @@ -220,9 +243,9 @@ class ManagerImpl implements Manager { @Override public void updateConfiguration( Configuration configuration - ) throws NotMasterDeviceException { - if (!account.isMasterDevice()) { - throw new NotMasterDeviceException(); + ) throws NotPrimaryDeviceException { + if (!account.isPrimaryDevice()) { + throw new NotPrimaryDeviceException(); } final var configurationStore = account.getConfigurationStore(); @@ -242,15 +265,16 @@ class ManagerImpl implements Manager { } @Override - public void setProfile( - String givenName, final String familyName, String about, String aboutEmoji, java.util.Optional avatar - ) throws IOException { + public void updateProfile(UpdateProfile updateProfile) throws IOException { context.getProfileHelper() - .setProfile(givenName, - familyName, - about, - aboutEmoji, - avatar == null ? null : Optional.fromNullable(avatar.orElse(null))); + .setProfile(updateProfile.getGivenName(), + updateProfile.getFamilyName(), + updateProfile.getAbout(), + updateProfile.getAboutEmoji(), + updateProfile.isDeleteAvatar() + ? Optional.empty() + : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar()), + updateProfile.getMobileCoinAddress()); context.getSyncHelper().sendSyncFetchProfileMessage(); } @@ -275,7 +299,7 @@ class ManagerImpl implements Manager { public List getLinkedDevices() throws IOException { var devices = dependencies.getAccountManager().getDevices(); account.setMultiDevice(devices.size() > 1); - var identityKey = account.getIdentityKeyPair().getPrivateKey(); + var identityKey = account.getAciIdentityKeyPair().getPrivateKey(); return devices.stream().map(d -> { String deviceName = d.getName(); if (deviceName != null) { @@ -305,9 +329,9 @@ class ManagerImpl implements Manager { } @Override - public void setRegistrationLockPin(java.util.Optional pin) throws IOException, NotMasterDeviceException { - if (!account.isMasterDevice()) { - throw new NotMasterDeviceException(); + public void setRegistrationLockPin(Optional pin) throws IOException, NotPrimaryDeviceException { + if (!account.isPrimaryDevice()) { + throw new NotPrimaryDeviceException(); } if (pin.isPresent()) { context.getAccountHelper().setRegistrationPin(pin.get()); @@ -321,7 +345,7 @@ class ManagerImpl implements Manager { } @Override - public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { return context.getProfileHelper().getRecipientProfile(context.getRecipientHelper().resolveRecipient(recipient)); } @@ -335,9 +359,7 @@ class ManagerImpl implements Manager { return null; } - return Group.from(groupInfo, - account.getRecipientStore()::resolveRecipientAddress, - account.getSelfRecipientId()); + return Group.from(groupInfo, account.getRecipientAddressResolver(), account.getSelfRecipientId()); } @Override @@ -350,6 +372,11 @@ class ManagerImpl implements Manager { @Override public void deleteGroup(GroupId groupId) throws IOException { + final var group = context.getGroupHelper().getGroup(groupId); + if (group.isMember(account.getSelfRecipientId())) { + throw new IOException( + "The local group information cannot be removed, as the user is still a member of the group"); + } context.getGroupHelper().deleteGroup(groupId); } @@ -383,6 +410,12 @@ class ManagerImpl implements Manager { updateGroup.getRemoveAdmins() == null ? null : context.getRecipientHelper().resolveRecipients(updateGroup.getRemoveAdmins()), + updateGroup.getBanMembers() == null + ? null + : context.getRecipientHelper().resolveRecipients(updateGroup.getBanMembers()), + updateGroup.getUnbanMembers() == null + ? null + : context.getRecipientHelper().resolveRecipients(updateGroup.getUnbanMembers()), updateGroup.isResetGroupLink(), updateGroup.getGroupLinkState(), updateGroup.getAddMemberPermission(), @@ -427,9 +460,7 @@ class ManagerImpl implements Manager { } private SendMessageResult toSendMessageResult(final org.whispersystems.signalservice.api.messages.SendMessageResult result) { - return SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress); + return SendMessageResult.from(result, account.getRecipientResolver(), account.getRecipientAddressResolver()); } private SendMessageResults sendTypingMessage( @@ -439,7 +470,7 @@ class ManagerImpl implements Manager { final var timestamp = System.currentTimeMillis(); for (var recipient : recipients) { if (recipient instanceof RecipientIdentifier.Single single) { - final var message = new SignalServiceTypingMessage(action, timestamp, Optional.absent()); + final var message = new SignalServiceTypingMessage(action, timestamp, Optional.empty()); try { final var recipientId = context.getRecipientHelper().resolveRecipient(single); final var result = context.getSendHelper().sendTypingMessage(message, recipientId); @@ -468,7 +499,7 @@ class ManagerImpl implements Manager { @Override public SendMessageResults sendReadReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException { + ) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, @@ -480,7 +511,7 @@ class ManagerImpl implements Manager { @Override public SendMessageResults sendViewedReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException { + ) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, messageIds, @@ -493,7 +524,7 @@ class ManagerImpl implements Manager { final RecipientIdentifier.Single sender, final long timestamp, final SignalServiceReceiptMessage receiptMessage - ) throws IOException { + ) { try { final var result = context.getSendHelper() .sendReceiptMessage(receiptMessage, context.getRecipientHelper().resolveRecipient(sender)); @@ -508,6 +539,11 @@ class ManagerImpl implements Manager { public SendMessageResults sendMessage( Message message, Set recipients ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException { + final var selfProfile = context.getProfileHelper().getSelfProfile(); + if (selfProfile == null || selfProfile.getDisplayName().isEmpty()) { + logger.warn( + "No profile name set. When sending a message it's recommended to set a profile name wit the updateProfile command. This may become mandatory in the future."); + } final var messageBuilder = SignalServiceDataMessage.newBuilder(); applyMessage(messageBuilder, message); return sendMessage(messageBuilder, recipients); @@ -517,9 +553,8 @@ class ManagerImpl implements Manager { final SignalServiceDataMessage.Builder messageBuilder, final Message message ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException, InvalidStickerException { messageBuilder.withBody(message.messageText()); - final var attachments = message.attachments(); - if (attachments != null) { - messageBuilder.withAttachments(context.getAttachmentHelper().uploadAttachments(attachments)); + if (message.attachments().size() > 0) { + messageBuilder.withAttachments(context.getAttachmentHelper().uploadAttachments(message.attachments())); } if (message.mentions().size() > 0) { messageBuilder.withMentions(resolveMentions(message.mentions())); @@ -531,7 +566,8 @@ class ManagerImpl implements Manager { .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())), quote.message(), List.of(), - resolveMentions(quote.mentions()))); + resolveMentions(quote.mentions()), + SignalServiceDataMessage.Quote.Type.NORMAL)); } if (message.sticker().isPresent()) { final var sticker = message.sticker().get(); @@ -555,17 +591,30 @@ class ManagerImpl implements Manager { stickerPack.getPackKey(), stickerId, manifestSticker.emoji(), - AttachmentUtils.createAttachment(streamDetails, Optional.absent()))); + AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty()))); + } + if (message.previews().size() > 0) { + final var previews = new ArrayList(message.previews().size()); + for (final var p : message.previews()) { + final var image = p.image().isPresent() ? context.getAttachmentHelper() + .uploadAttachment(p.image().get()) : null; + previews.add(new SignalServicePreview(p.url(), + p.title(), + p.description(), + 0, + Optional.ofNullable(image))); + } + messageBuilder.withPreviews(previews); } } - private ArrayList resolveMentions(final List mentionList) throws IOException, UnregisteredRecipientException { + private ArrayList resolveMentions(final List mentionList) throws UnregisteredRecipientException { final var mentions = new ArrayList(); for (final var m : mentionList) { final var recipientId = context.getRecipientHelper().resolveRecipient(m.recipient()); mentions.add(new SignalServiceDataMessage.Mention(context.getRecipientHelper() .resolveSignalServiceAddress(recipientId) - .getAci(), m.start(), m.length())); + .getServiceId(), m.start(), m.length())); } return mentions; } @@ -607,6 +656,20 @@ class ManagerImpl implements Manager { return sendMessage(messageBuilder, recipients); } + @Override + public SendMessageResults sendPaymentNotificationMessage( + byte[] receipt, String note, RecipientIdentifier.Single recipient + ) throws IOException { + final var paymentNotification = new SignalServiceDataMessage.PaymentNotification(receipt, note); + final var payment = new SignalServiceDataMessage.Payment(paymentNotification); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withPayment(payment); + try { + return sendMessage(messageBuilder, Set.of(recipient)); + } catch (NotAGroupMemberException | GroupNotFoundException | GroupSendingNotAllowedException e) { + throw new AssertionError(e); + } + } + @Override public SendMessageResults sendEndSessionMessage(Set recipients) throws IOException { var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); @@ -631,46 +694,78 @@ class ManagerImpl implements Manager { @Override public void deleteRecipient(final RecipientIdentifier.Single recipient) { - account.removeRecipient(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); + account.removeRecipient(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override public void deleteContact(final RecipientIdentifier.Single recipient) { account.getContactStore() - .deleteContact(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); + .deleteContact(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override public void setContactName( RecipientIdentifier.Single recipient, String name - ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { - if (!account.isMasterDevice()) { - throw new NotMasterDeviceException(); + ) throws NotPrimaryDeviceException, UnregisteredRecipientException { + if (!account.isPrimaryDevice()) { + throw new NotPrimaryDeviceException(); } context.getContactHelper().setContactName(context.getRecipientHelper().resolveRecipient(recipient), name); } @Override - public void setContactBlocked( - RecipientIdentifier.Single recipient, boolean blocked - ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { - if (!account.isMasterDevice()) { - throw new NotMasterDeviceException(); + public void setContactsBlocked( + Collection recipients, boolean blocked + ) throws NotPrimaryDeviceException, IOException, UnregisteredRecipientException { + if (!account.isPrimaryDevice()) { + throw new NotPrimaryDeviceException(); + } + if (recipients.size() == 0) { + return; + } + final var recipientIds = context.getRecipientHelper().resolveRecipients(recipients); + final var selfRecipientId = account.getSelfRecipientId(); + boolean shouldRotateProfileKey = false; + for (final var recipientId : recipientIds) { + if (context.getContactHelper().isContactBlocked(recipientId) == blocked) { + continue; + } + context.getContactHelper().setContactBlocked(recipientId, blocked); + // if we don't have a common group with the blocked contact we need to rotate the profile key + shouldRotateProfileKey = blocked && ( + shouldRotateProfileKey || account.getGroupStore() + .getGroups() + .stream() + .noneMatch(g -> g.isMember(selfRecipientId) && g.isMember(recipientId)) + ); + } + if (shouldRotateProfileKey) { + context.getProfileHelper().rotateProfileKey(); } - context.getContactHelper().setContactBlocked(context.getRecipientHelper().resolveRecipient(recipient), blocked); - // TODO cycle our profile key, if we're not together in a group with recipient context.getSyncHelper().sendBlockedList(); } @Override - public void setGroupBlocked( - final GroupId groupId, final boolean blocked - ) throws GroupNotFoundException, NotMasterDeviceException { - if (!account.isMasterDevice()) { - throw new NotMasterDeviceException(); + public void setGroupsBlocked( + final Collection groupIds, final boolean blocked + ) throws GroupNotFoundException, NotPrimaryDeviceException, IOException { + if (!account.isPrimaryDevice()) { + throw new NotPrimaryDeviceException(); + } + if (groupIds.size() == 0) { + return; + } + boolean shouldRotateProfileKey = false; + for (final var groupId : groupIds) { + if (context.getGroupHelper().isGroupBlocked(groupId) == blocked) { + continue; + } + context.getGroupHelper().setGroupBlocked(groupId, blocked); + shouldRotateProfileKey = blocked; + } + if (shouldRotateProfileKey) { + context.getProfileHelper().rotateProfileKey(); } - context.getGroupHelper().setGroupBlocked(groupId, blocked); - // TODO cycle our profile key context.getSyncHelper().sendBlockedList(); } @@ -716,7 +811,7 @@ class ManagerImpl implements Manager { pack.isInstalled(), manifest.title(), manifest.author(), - java.util.Optional.ofNullable(manifest.cover() == null ? null : manifest.cover().toApi()), + Optional.ofNullable(manifest.cover() == null ? null : manifest.cover().toApi()), manifest.stickers().stream().map(JsonStickerPack.JsonSticker::toApi).toList()); } catch (Exception e) { logger.warn("Failed to read local sticker pack manifest: {}", e.getMessage(), e); @@ -762,24 +857,17 @@ class ManagerImpl implements Manager { } receiveThread = new Thread(() -> { logger.debug("Starting receiving messages"); - while (!Thread.interrupted()) { - try { - context.getReceiveHelper().receiveMessages(Duration.ofMinutes(1), false, (envelope, e) -> { - synchronized (messageHandlers) { - Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { - try { - h.handleMessage(envelope, e); - } catch (Exception ex) { - logger.warn("Message handler failed, ignoring", ex); - } - }); + context.getReceiveHelper().receiveMessagesContinuously((envelope, e) -> { + synchronized (messageHandlers) { + Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { + try { + h.handleMessage(envelope, e); + } catch (Throwable ex) { + logger.warn("Message handler failed, ignoring", ex); } }); - break; - } catch (IOException e) { - logger.warn("Receiving messages failed, retrying", e); } - } + }); logger.debug("Finished receiving messages"); synchronized (messageHandlers) { receiveThread = null; @@ -813,7 +901,10 @@ class ManagerImpl implements Manager { } private void stopReceiveThread(final Thread thread) { - thread.interrupt(); + if (context.getReceiveHelper().requestStopReceiveMessages()) { + logger.debug("Receive stop requested, interrupting read from server."); + thread.interrupt(); + } try { thread.join(); } catch (InterruptedException ignored) { @@ -857,8 +948,8 @@ class ManagerImpl implements Manager { } @Override - public void setIgnoreAttachments(final boolean ignoreAttachments) { - context.getReceiveHelper().setIgnoreAttachments(ignoreAttachments); + public void setReceiveConfig(final ReceiveConfig receiveConfig) { + context.getReceiveHelper().setReceiveConfig(receiveConfig); } @Override @@ -871,7 +962,7 @@ class ManagerImpl implements Manager { final RecipientId recipientId; try { recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { return false; } return context.getContactHelper().isContactBlocked(recipientId); @@ -883,12 +974,22 @@ class ManagerImpl implements Manager { } @Override - public List> getContacts() { - return account.getContactStore() - .getContacts() - .stream() - .map(p -> new Pair<>(account.getRecipientStore().resolveRecipientAddress(p.first()), p.second())) - .toList(); + public List getRecipients( + boolean onlyContacts, + Optional blocked, + Collection recipients, + Optional name + ) { + final var recipientIds = recipients.stream().map(a -> { + try { + return context.getRecipientHelper().resolveRecipient(a); + } catch (UnregisteredRecipientException e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toSet()); + // refresh profiles of explicitly given recipients + context.getProfileHelper().refreshRecipientProfiles(recipientIds); + return account.getRecipientStore().getRecipients(onlyContacts, blocked, recipientIds, name); } @Override @@ -896,7 +997,7 @@ class ManagerImpl implements Manager { final RecipientId recipientId; try { recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { return null; } @@ -928,7 +1029,8 @@ class ManagerImpl implements Manager { return null; } - final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId()); + final var address = account.getRecipientAddressResolver() + .resolveRecipientAddress(identityInfo.getRecipientId()); final var scannableFingerprint = context.getIdentityHelper() .computeSafetyNumberForScanning(identityInfo.getRecipientId(), identityInfo.getIdentityKey()); return new Identity(address, @@ -946,7 +1048,7 @@ class ManagerImpl implements Manager { try { identity = account.getIdentityKeyStore() .getIdentity(context.getRecipientHelper().resolveRecipient(recipient)); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { identity = null; } return identity == null ? List.of() : List.of(toIdentity(identity)); @@ -983,12 +1085,7 @@ class ManagerImpl implements Manager { private boolean trustIdentity( RecipientIdentifier.Single recipient, Function trustMethod ) throws UnregisteredRecipientException { - RecipientId recipientId; - try { - recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException e) { - return false; - } + final var recipientId = context.getRecipientHelper().resolveRecipient(recipient); final var updated = trustMethod.apply(recipientId); if (updated && this.isReceiving()) { context.getReceiveHelper().setNeedsToRetryFailedMessages(true); @@ -996,6 +1093,13 @@ class ManagerImpl implements Manager { return updated; } + @Override + public void addAddressChangedListener(final Runnable listener) { + synchronized (addressChangedListeners) { + addressChangedListeners.add(listener); + } + } + @Override public void addClosedListener(final Runnable listener) { synchronized (closedListeners) { @@ -1020,14 +1124,15 @@ class ManagerImpl implements Manager { dependencies.getSignalWebSocket().disconnect(); disposable.dispose(); + if (account != null) { + account.close(); + } + synchronized (closedListeners) { closedListeners.forEach(Runnable::run); closedListeners.clear(); } - if (account != null) { - account.close(); - } account = null; } }