X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/62687d103fab1ade650b920008060c220361d581..c88c92086efcf5c0ad417589db997ef1a034e775:/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 df757a8b..2f58657f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -30,8 +30,8 @@ import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResults; 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.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -48,6 +48,7 @@ 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.RecipientHelper; import org.asamk.signal.manager.helper.SendHelper; import org.asamk.signal.manager.helper.StorageHelper; import org.asamk.signal.manager.helper.SyncHelper; @@ -76,16 +77,12 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; 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.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; 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.websocket.WebSocketConnectionState; import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException; -import org.whispersystems.signalservice.internal.contacts.crypto.Quote; -import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; -import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.signalservice.internal.util.Util; @@ -96,7 +93,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.security.SignatureException; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -122,7 +119,6 @@ public class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); - private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final SignalDependencies dependencies; private SignalAccount account; @@ -140,6 +136,7 @@ public class ManagerImpl implements Manager { private final IncomingMessageHandler incomingMessageHandler; private final PreKeyHelper preKeyHelper; private final IdentityHelper identityHelper; + private final RecipientHelper recipientHelper; private final Context context; private boolean hasCaughtUpWithOldMessages = false; @@ -150,6 +147,7 @@ public class ManagerImpl implements Manager { private final Set messageHandlers = new HashSet<>(); private final List closedListeners = new ArrayList<>(); private boolean isReceivingSynchronous; + private boolean needsToRetryFailedMessages = false; ManagerImpl( SignalAccount account, @@ -158,7 +156,6 @@ public class ManagerImpl implements Manager { String userAgent ) { this.account = account; - this.serviceEnvironmentConfig = serviceEnvironmentConfig; final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(), account.getAccount(), @@ -189,32 +186,33 @@ public class ManagerImpl implements Manager { dependencies, account::getProfileKey, this::getRecipientProfile); + this.recipientHelper = new RecipientHelper(account, dependencies, serviceEnvironmentConfig); this.profileHelper = new ProfileHelper(account, dependencies, avatarStore, unidentifiedAccessHelper::getAccessFor, - this::resolveSignalServiceAddress); - final GroupV2Helper groupV2Helper = new GroupV2Helper(profileHelper::getRecipientProfileKeyCredential, - this::getRecipientProfile, + recipientHelper::resolveSignalServiceAddress); + final GroupV2Helper groupV2Helper = new GroupV2Helper(profileHelper, account::getSelfRecipientId, dependencies.getGroupsV2Operations(), dependencies.getGroupsV2Api(), - this::resolveSignalServiceAddress); + recipientHelper::resolveSignalServiceAddress); this.sendHelper = new SendHelper(account, dependencies, unidentifiedAccessHelper, - this::resolveSignalServiceAddress, + recipientHelper::resolveSignalServiceAddress, account.getRecipientStore(), this::handleIdentityFailure, this::getGroupInfo, - this::refreshRegisteredUser); + profileHelper, + recipientHelper::refreshRegisteredUser); this.groupHelper = new GroupHelper(account, dependencies, attachmentHelper, sendHelper, groupV2Helper, avatarStore, - this::resolveSignalServiceAddress, + recipientHelper::resolveSignalServiceAddress, account.getRecipientStore()); this.storageHelper = new StorageHelper(account, dependencies, groupHelper, profileHelper); this.contactHelper = new ContactHelper(account); @@ -223,7 +221,7 @@ public class ManagerImpl implements Manager { sendHelper, groupHelper, avatarStore, - this::resolveSignalServiceAddress); + recipientHelper::resolveSignalServiceAddress); preKeyHelper = new PreKeyHelper(account, dependencies); this.context = new Context(account, @@ -240,16 +238,16 @@ public class ManagerImpl implements Manager { this.incomingMessageHandler = new IncomingMessageHandler(account, dependencies, account.getRecipientStore(), - this::resolveSignalServiceAddress, + recipientHelper::resolveSignalServiceAddress, groupHelper, contactHelper, attachmentHelper, syncHelper, - this::getRecipientProfile, + profileHelper::getRecipientProfile, jobExecutor); this.identityHelper = new IdentityHelper(account, dependencies, - this::resolveSignalServiceAddress, + recipientHelper::resolveSignalServiceAddress, syncHelper, profileHelper); } @@ -293,7 +291,7 @@ public class ManagerImpl implements Manager { */ @Override public Map> areUsersRegistered(Set numbers) throws IOException { - Map canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { + final var canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { try { final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getAccount()); if (!canonicalizedNumber.equals(n)) { @@ -306,10 +304,11 @@ public class ManagerImpl implements Manager { })); // Note "registeredUsers" has no optionals. It only gives us info on users who are registered - var registeredUsers = getRegisteredUsers(canonicalizedNumbers.values() + final var canonicalizedNumbersSet = canonicalizedNumbers.values() .stream() .filter(s -> !s.isEmpty()) - .collect(Collectors.toSet())); + .collect(Collectors.toSet()); + final var registeredUsers = recipientHelper.getRegisteredUsers(canonicalizedNumbersSet); return numbers.stream().collect(Collectors.toMap(n -> n, n -> { final var number = canonicalizedNumbers.get(n); @@ -507,8 +506,8 @@ public class ManagerImpl implements Manager { } @Override - public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws IOException { - return profileHelper.getRecipientProfile(resolveRecipient(recipient)); + public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + return profileHelper.getRecipientProfile(recipientHelper.resolveRecipient(recipient)); } private Profile getRecipientProfile(RecipientId recipientId) { @@ -557,8 +556,8 @@ public class ManagerImpl implements Manager { @Override public SendGroupMessageResults quitGroup( GroupId groupId, Set groupAdmins - ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException { - final var newAdmins = resolveRecipients(groupAdmins); + ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException, UnregisteredRecipientException { + final var newAdmins = recipientHelper.resolveRecipients(groupAdmins); return groupHelper.quitGroup(groupId, newAdmins); } @@ -570,21 +569,27 @@ public class ManagerImpl implements Manager { @Override public Pair createGroup( String name, Set members, File avatarFile - ) throws IOException, AttachmentInvalidException { - return groupHelper.createGroup(name, members == null ? null : resolveRecipients(members), avatarFile); + ) throws IOException, AttachmentInvalidException, UnregisteredRecipientException { + return groupHelper.createGroup(name, + members == null ? null : recipientHelper.resolveRecipients(members), + avatarFile); } @Override public SendGroupMessageResults updateGroup( final GroupId groupId, final UpdateGroup updateGroup - ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException { + ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException, UnregisteredRecipientException { return groupHelper.updateGroup(groupId, updateGroup.getName(), updateGroup.getDescription(), - updateGroup.getMembers() == null ? null : resolveRecipients(updateGroup.getMembers()), - updateGroup.getRemoveMembers() == null ? null : resolveRecipients(updateGroup.getRemoveMembers()), - updateGroup.getAdmins() == null ? null : resolveRecipients(updateGroup.getAdmins()), - updateGroup.getRemoveAdmins() == null ? null : resolveRecipients(updateGroup.getRemoveAdmins()), + updateGroup.getMembers() == null ? null : recipientHelper.resolveRecipients(updateGroup.getMembers()), + updateGroup.getRemoveMembers() == null + ? null + : recipientHelper.resolveRecipients(updateGroup.getRemoveMembers()), + updateGroup.getAdmins() == null ? null : recipientHelper.resolveRecipients(updateGroup.getAdmins()), + updateGroup.getRemoveAdmins() == null + ? null + : recipientHelper.resolveRecipients(updateGroup.getRemoveAdmins()), updateGroup.isResetGroupLink(), updateGroup.getGroupLinkState(), updateGroup.getAddMemberPermission(), @@ -609,12 +614,17 @@ public class ManagerImpl implements Manager { messageBuilder.withTimestamp(timestamp); for (final var recipient : recipients) { if (recipient instanceof RecipientIdentifier.Single single) { - final var recipientId = resolveRecipient(single); - final var result = sendHelper.sendMessage(messageBuilder, recipientId); - results.put(recipient, - List.of(SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress))); + try { + final var recipientId = recipientHelper.resolveRecipient(single); + final var result = sendHelper.sendMessage(messageBuilder, recipientId); + results.put(recipient, + List.of(SendMessageResult.from(result, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress))); + } catch (UnregisteredRecipientException e) { + results.put(recipient, + List.of(SendMessageResult.unregisteredFailure(single.toPartialRecipientAddress()))); + } } else if (recipient instanceof RecipientIdentifier.NoteToSelf) { final var result = sendHelper.sendSelfMessage(messageBuilder); results.put(recipient, @@ -640,14 +650,19 @@ public class ManagerImpl implements Manager { var results = new HashMap>(); final var timestamp = System.currentTimeMillis(); for (var recipient : recipients) { - if (recipient instanceof RecipientIdentifier.Single) { + if (recipient instanceof RecipientIdentifier.Single single) { final var message = new SignalServiceTypingMessage(action, timestamp, Optional.absent()); - final var recipientId = resolveRecipient((RecipientIdentifier.Single) recipient); - final var result = sendHelper.sendTypingMessage(message, recipientId); - results.put(recipient, - List.of(SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress))); + try { + final var recipientId = recipientHelper.resolveRecipient(single); + final var result = sendHelper.sendTypingMessage(message, recipientId); + results.put(recipient, + List.of(SendMessageResult.from(result, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress))); + } catch (UnregisteredRecipientException e) { + results.put(recipient, + List.of(SendMessageResult.unregisteredFailure(single.toPartialRecipientAddress()))); + } } else if (recipient instanceof RecipientIdentifier.Group) { final var groupId = ((RecipientIdentifier.Group) recipient).groupId(); final var message = new SignalServiceTypingMessage(action, timestamp, Optional.of(groupId.serialize())); @@ -679,12 +694,7 @@ public class ManagerImpl implements Manager { messageIds, timestamp); - final var result = sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(sender)); - return new SendMessageResults(timestamp, - Map.of(sender, - List.of(SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress)))); + return sendReceiptMessage(sender, timestamp, receiptMessage); } @Override @@ -696,18 +706,31 @@ public class ManagerImpl implements Manager { messageIds, timestamp); - final var result = sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(sender)); - return new SendMessageResults(timestamp, - Map.of(sender, - List.of(SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress)))); + return sendReceiptMessage(sender, timestamp, receiptMessage); + } + + private SendMessageResults sendReceiptMessage( + final RecipientIdentifier.Single sender, + final long timestamp, + final SignalServiceReceiptMessage receiptMessage + ) throws IOException { + try { + final var result = sendHelper.sendReceiptMessage(receiptMessage, recipientHelper.resolveRecipient(sender)); + return new SendMessageResults(timestamp, + Map.of(sender, + List.of(SendMessageResult.from(result, + account.getRecipientStore(), + account.getRecipientStore()::resolveRecipientAddress)))); + } catch (UnregisteredRecipientException e) { + return new SendMessageResults(timestamp, + Map.of(sender, List.of(SendMessageResult.unregisteredFailure(sender.toPartialRecipientAddress())))); + } } @Override public SendMessageResults sendMessage( Message message, Set recipients - ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { + ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException { final var messageBuilder = SignalServiceDataMessage.newBuilder(); applyMessage(messageBuilder, message); return sendMessage(messageBuilder, recipients); @@ -715,7 +738,7 @@ public class ManagerImpl implements Manager { private void applyMessage( final SignalServiceDataMessage.Builder messageBuilder, final Message message - ) throws AttachmentInvalidException, IOException { + ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException { messageBuilder.withBody(message.messageText()); final var attachments = message.attachments(); if (attachments != null) { @@ -727,20 +750,19 @@ public class ManagerImpl implements Manager { if (message.quote().isPresent()) { final var quote = message.quote().get(); messageBuilder.withQuote(new SignalServiceDataMessage.Quote(quote.timestamp(), - resolveSignalServiceAddress(resolveRecipient(quote.author())), + recipientHelper.resolveSignalServiceAddress(recipientHelper.resolveRecipient(quote.author())), quote.message(), List.of(), resolveMentions(quote.mentions()))); } } - private ArrayList resolveMentions(final List mentionList) throws IOException { + private ArrayList resolveMentions(final List mentionList) throws IOException, UnregisteredRecipientException { final var mentions = new ArrayList(); for (final var m : mentionList) { - final var recipientId = resolveRecipient(m.recipient()); - mentions.add(new SignalServiceDataMessage.Mention(resolveSignalServiceAddress(recipientId).getAci(), - m.start(), - m.length())); + final var recipientId = recipientHelper.resolveRecipient(m.recipient()); + mentions.add(new SignalServiceDataMessage.Mention(recipientHelper.resolveSignalServiceAddress(recipientId) + .getAci(), m.start(), m.length())); } return mentions; } @@ -761,11 +783,11 @@ public class ManagerImpl implements Manager { RecipientIdentifier.Single targetAuthor, long targetSentTimestamp, Set recipients - ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { - var targetAuthorRecipientId = resolveRecipient(targetAuthor); + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException { + var targetAuthorRecipientId = recipientHelper.resolveRecipient(targetAuthor); var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, - resolveSignalServiceAddress(targetAuthorRecipientId), + recipientHelper.resolveSignalServiceAddress(targetAuthorRecipientId), targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendMessage(messageBuilder, recipients); @@ -782,41 +804,47 @@ public class ManagerImpl implements Manager { throw new AssertionError(e); } finally { for (var recipient : recipients) { - final var recipientId = resolveRecipient(recipient); + final RecipientId recipientId; + try { + recipientId = recipientHelper.resolveRecipient(recipient); + } catch (UnregisteredRecipientException e) { + continue; + } account.getSessionStore().deleteAllSessions(recipientId); } } } @Override - public void deleteRecipient(final RecipientIdentifier.Single recipient) throws IOException { - account.removeRecipient(resolveRecipient(recipient)); + public void deleteRecipient(final RecipientIdentifier.Single recipient) { + account.removeRecipient(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override - public void deleteContact(final RecipientIdentifier.Single recipient) throws IOException { - account.getContactStore().deleteContact(resolveRecipient(recipient)); + public void deleteContact(final RecipientIdentifier.Single recipient) { + account.getContactStore() + .deleteContact(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override public void setContactName( RecipientIdentifier.Single recipient, String name - ) throws NotMasterDeviceException, IOException { + ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } - contactHelper.setContactName(resolveRecipient(recipient), name); + contactHelper.setContactName(recipientHelper.resolveRecipient(recipient), name); } @Override public void setContactBlocked( RecipientIdentifier.Single recipient, boolean blocked - ) throws NotMasterDeviceException, IOException { + ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } - contactHelper.setContactBlocked(resolveRecipient(recipient), blocked); - // TODO cycle our profile key + contactHelper.setContactBlocked(recipientHelper.resolveRecipient(recipient), blocked); + // TODO cycle our profile key, if we're not together in a group with recipient syncHelper.sendBlockedList(); } @@ -838,8 +866,8 @@ public class ManagerImpl implements Manager { @Override public void setExpirationTimer( RecipientIdentifier.Single recipient, int messageExpirationTimer - ) throws IOException { - var recipientId = resolveRecipient(recipient); + ) throws IOException, UnregisteredRecipientException { + var recipientId = recipientHelper.resolveRecipient(recipient); contactHelper.setExpirationTimer(recipientId, messageExpirationTimer); final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate(); try { @@ -893,47 +921,6 @@ public class ManagerImpl implements Manager { } } - private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException { - final var address = resolveSignalServiceAddress(recipientId); - if (!address.getNumber().isPresent()) { - return recipientId; - } - final var number = address.getNumber().get(); - final var uuid = getRegisteredUser(number); - return resolveRecipientTrusted(new SignalServiceAddress(uuid, number)); - } - - private ACI getRegisteredUser(final String number) throws IOException { - final Map aciMap; - try { - aciMap = getRegisteredUsers(Set.of(number)); - } catch (NumberFormatException e) { - throw new IOException(number, e); - } - final var uuid = aciMap.get(number); - if (uuid == null) { - throw new IOException(number, null); - } - return uuid; - } - - private Map getRegisteredUsers(final Set numbers) throws IOException { - final Map registeredUsers; - try { - registeredUsers = dependencies.getAccountManager() - .getRegisteredUsers(ServiceConfig.getIasKeyStore(), - numbers, - serviceEnvironmentConfig.getCdsMrenclave()); - } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { - throw new IOException(e); - } - - // Store numbers as recipients, so we have the number/uuid association - registeredUsers.forEach((number, aci) -> resolveRecipientTrusted(new SignalServiceAddress(aci, number))); - - return registeredUsers; - } - private void retryFailedReceivedMessages(ReceiveMessageHandler handler) { Set queuedActions = new HashSet<>(); for (var cachedMessage : account.getMessageCache().getCachedMessages()) { @@ -1004,7 +991,7 @@ public class ManagerImpl implements Manager { logger.debug("Starting receiving messages"); while (!Thread.interrupted()) { try { - receiveMessagesInternal(1L, TimeUnit.HOURS, false, (envelope, e) -> { + receiveMessagesInternal(Duration.ofMinutes(1), false, (envelope, e) -> { synchronized (messageHandlers) { Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { try { @@ -1071,17 +1058,17 @@ public class ManagerImpl implements Manager { } @Override - public void receiveMessages(long timeout, TimeUnit unit, ReceiveMessageHandler handler) throws IOException { - receiveMessages(timeout, unit, true, handler); + public void receiveMessages(Duration timeout, ReceiveMessageHandler handler) throws IOException { + receiveMessages(timeout, true, handler); } @Override public void receiveMessages(ReceiveMessageHandler handler) throws IOException { - receiveMessages(1L, TimeUnit.HOURS, false, handler); + receiveMessages(Duration.ofMinutes(1), false, handler); } private void receiveMessages( - long timeout, TimeUnit unit, boolean returnOnTimeout, ReceiveMessageHandler handler + Duration timeout, boolean returnOnTimeout, ReceiveMessageHandler handler ) throws IOException { if (isReceiving()) { throw new IllegalStateException("Already receiving message."); @@ -1089,7 +1076,7 @@ public class ManagerImpl implements Manager { isReceivingSynchronous = true; receiveThread = Thread.currentThread(); try { - receiveMessagesInternal(timeout, unit, returnOnTimeout, handler); + receiveMessagesInternal(timeout, returnOnTimeout, handler); } finally { receiveThread = null; hasCaughtUpWithOldMessages = false; @@ -1098,9 +1085,9 @@ public class ManagerImpl implements Manager { } private void receiveMessagesInternal( - long timeout, TimeUnit unit, boolean returnOnTimeout, ReceiveMessageHandler handler + Duration timeout, boolean returnOnTimeout, ReceiveMessageHandler handler ) throws IOException { - retryFailedReceivedMessages(handler); + needsToRetryFailedMessages = true; // Use a Map here because java Set doesn't have a get method ... Map queuedActions = new HashMap<>(); @@ -1119,6 +1106,10 @@ public class ManagerImpl implements Manager { final var MAX_BACKOFF_COUNTER = 9; while (!Thread.interrupted()) { + if (needsToRetryFailedMessages) { + retryFailedReceivedMessages(handler); + needsToRetryFailedMessages = false; + } SignalServiceEnvelope envelope; final CachedMessage[] cachedMessage = {null}; final var nowMillis = System.currentTimeMillis(); @@ -1127,10 +1118,9 @@ public class ManagerImpl implements Manager { } logger.debug("Checking for new message from server"); try { - var result = signalWebSocket.readOrEmpty(unit.toMillis(timeout), envelope1 -> { - final var recipientId = envelope1.hasSourceUuid() - ? resolveRecipient(envelope1.getSourceAddress()) - : null; + var result = signalWebSocket.readOrEmpty(timeout.toMillis(), envelope1 -> { + final var recipientId = envelope1.hasSourceUuid() ? account.getRecipientStore() + .resolveRecipient(envelope1.getSourceAddress()) : null; // store message on disk, before acknowledging receipt to the server cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId); }); @@ -1200,7 +1190,7 @@ public class ManagerImpl implements Manager { if (exception instanceof UntrustedIdentityException) { logger.debug("Keeping message with untrusted identity in message cache"); final var address = ((UntrustedIdentityException) exception).getSender(); - final var recipientId = resolveRecipient(address); + final var recipientId = account.getRecipientStore().resolveRecipient(address); if (!envelope.hasSourceUuid()) { try { cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); @@ -1245,6 +1235,7 @@ public class ManagerImpl implements Manager { logger.debug("Handling message actions"); var interrupted = false; for (var action : queuedActions) { + logger.debug("Executing action {}", action.getClass().getSimpleName()); try { action.execute(context); } catch (Throwable e) { @@ -1265,8 +1256,8 @@ public class ManagerImpl implements Manager { public boolean isContactBlocked(final RecipientIdentifier.Single recipient) { final RecipientId recipientId; try { - recipientId = resolveRecipient(recipient); - } catch (IOException e) { + recipientId = recipientHelper.resolveRecipient(recipient); + } catch (IOException | UnregisteredRecipientException e) { return false; } return contactHelper.isContactBlocked(recipientId); @@ -1290,8 +1281,8 @@ public class ManagerImpl implements Manager { public String getContactOrProfileName(RecipientIdentifier.Single recipient) { final RecipientId recipientId; try { - recipientId = resolveRecipient(recipient); - } catch (IOException e) { + recipientId = recipientHelper.resolveRecipient(recipient); + } catch (IOException | UnregisteredRecipientException e) { return null; } @@ -1300,7 +1291,7 @@ public class ManagerImpl implements Manager { return contact.getName(); } - final var profile = getRecipientProfile(recipientId); + final var profile = profileHelper.getRecipientProfile(recipientId); if (profile != null) { return profile.getDisplayName(); } @@ -1342,8 +1333,8 @@ public class ManagerImpl implements Manager { public List getIdentities(RecipientIdentifier.Single recipient) { IdentityInfo identity; try { - identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient)); - } catch (IOException e) { + identity = account.getIdentityKeyStore().getIdentity(recipientHelper.resolveRecipient(recipient)); + } catch (IOException | UnregisteredRecipientException e) { identity = null; } return identity == null ? List.of() : List.of(toIdentity(identity)); @@ -1356,14 +1347,20 @@ public class ManagerImpl implements Manager { * @param fingerprint Fingerprint */ @Override - public boolean trustIdentityVerified(RecipientIdentifier.Single recipient, byte[] fingerprint) { + public boolean trustIdentityVerified( + RecipientIdentifier.Single recipient, byte[] fingerprint + ) throws UnregisteredRecipientException { RecipientId recipientId; try { - recipientId = resolveRecipient(recipient); + recipientId = recipientHelper.resolveRecipient(recipient); } catch (IOException e) { return false; } - return identityHelper.trustIdentityVerified(recipientId, fingerprint); + final var updated = identityHelper.trustIdentityVerified(recipientId, fingerprint); + if (updated && this.isReceiving()) { + needsToRetryFailedMessages = true; + } + return updated; } /** @@ -1373,14 +1370,20 @@ public class ManagerImpl implements Manager { * @param safetyNumber Safety number */ @Override - public boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, String safetyNumber) { + public boolean trustIdentityVerifiedSafetyNumber( + RecipientIdentifier.Single recipient, String safetyNumber + ) throws UnregisteredRecipientException { RecipientId recipientId; try { - recipientId = resolveRecipient(recipient); + recipientId = recipientHelper.resolveRecipient(recipient); } catch (IOException e) { return false; } - return identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber); + final var updated = identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber); + if (updated && this.isReceiving()) { + needsToRetryFailedMessages = true; + } + return updated; } /** @@ -1390,14 +1393,20 @@ public class ManagerImpl implements Manager { * @param safetyNumber Scannable safety number */ @Override - public boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, byte[] safetyNumber) { + public boolean trustIdentityVerifiedSafetyNumber( + RecipientIdentifier.Single recipient, byte[] safetyNumber + ) throws UnregisteredRecipientException { RecipientId recipientId; try { - recipientId = resolveRecipient(recipient); + recipientId = recipientHelper.resolveRecipient(recipient); } catch (IOException e) { return false; } - return identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber); + final var updated = identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber); + if (updated && this.isReceiving()) { + needsToRetryFailedMessages = true; + } + return updated; } /** @@ -1406,14 +1415,18 @@ public class ManagerImpl implements Manager { * @param recipient account of the identity */ @Override - public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) { + public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { RecipientId recipientId; try { - recipientId = resolveRecipient(recipient); + recipientId = recipientHelper.resolveRecipient(recipient); } catch (IOException e) { return false; } - return identityHelper.trustIdentityAllKeys(recipientId); + final var updated = identityHelper.trustIdentityAllKeys(recipientId); + if (updated && this.isReceiving()) { + needsToRetryFailedMessages = true; + } + return updated; } @Override @@ -1430,62 +1443,6 @@ public class ManagerImpl implements Manager { this.identityHelper.handleIdentityFailure(recipientId, identityFailure); } - private SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { - final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); - if (address.uuid().isPresent()) { - return address.toSignalServiceAddress(); - } - - // Address in recipient store doesn't have a uuid, this shouldn't happen - // Try to retrieve the uuid from the server - final var number = address.number().get(); - final ACI aci; - try { - aci = getRegisteredUser(number); - } catch (IOException e) { - logger.warn("Failed to get uuid for e164 number: {}", number, e); - // Return SignalServiceAddress with unknown UUID - return address.toSignalServiceAddress(); - } - return resolveSignalServiceAddress(account.getRecipientStore().resolveRecipient(aci)); - } - - private Set resolveRecipients(Collection recipients) throws IOException { - final var recipientIds = new HashSet(recipients.size()); - for (var number : recipients) { - final var recipientId = resolveRecipient(number); - recipientIds.add(recipientId); - } - return recipientIds; - } - - private RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws IOException { - if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { - return account.getRecipientStore().resolveRecipient(ACI.from(uuidRecipient.uuid())); - } else { - final var number = ((RecipientIdentifier.Number) recipient).number(); - return account.getRecipientStore().resolveRecipient(number, () -> { - try { - return getRegisteredUser(number); - } catch (IOException e) { - return null; - } - }); - } - } - - private RecipientId resolveRecipient(RecipientAddress address) { - return account.getRecipientStore().resolveRecipient(address); - } - - private RecipientId resolveRecipient(SignalServiceAddress address) { - return account.getRecipientStore().resolveRecipient(address); - } - - private RecipientId resolveRecipientTrusted(SignalServiceAddress address) { - return account.getRecipientStore().resolveRecipientTrusted(address); - } - @Override public void close() throws IOException { Thread thread;