X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/8bc6c0abcbdc70b1049df08712cdeff046f48f5e..62d8873a9288bcfe79d8eb3a2b7b3b467451db72:/lib/src/main/java/org/asamk/signal/manager/Manager.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 0d06dfba..05700379 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -40,8 +40,10 @@ import org.asamk.signal.manager.helper.GroupHelper; import org.asamk.signal.manager.helper.GroupV2Helper; import org.asamk.signal.manager.helper.IncomingMessageHandler; import org.asamk.signal.manager.helper.PinHelper; +import org.asamk.signal.manager.helper.PreKeyHelper; import org.asamk.signal.manager.helper.ProfileHelper; import org.asamk.signal.manager.helper.SendHelper; +import org.asamk.signal.manager.helper.StorageHelper; import org.asamk.signal.manager.helper.SyncHelper; import org.asamk.signal.manager.helper.UnidentifiedAccessHelper; import org.asamk.signal.manager.jobs.Context; @@ -58,22 +60,17 @@ 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; -import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.fingerprint.Fingerprint; import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalSessionLock; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; @@ -83,6 +80,7 @@ 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.SignalServiceAddress; +import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; @@ -134,14 +132,17 @@ public class Manager implements Closeable { private final ProfileHelper profileHelper; private final PinHelper pinHelper; + private final StorageHelper storageHelper; private final SendHelper sendHelper; private final SyncHelper syncHelper; private final AttachmentHelper attachmentHelper; private final GroupHelper groupHelper; private final ContactHelper contactHelper; private final IncomingMessageHandler incomingMessageHandler; + private final PreKeyHelper preKeyHelper; private final Context context; + private boolean hasCaughtUpWithOldMessages = false; Manager( SignalAccount account, @@ -165,8 +166,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(), @@ -187,8 +187,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, @@ -200,7 +198,7 @@ public class Manager implements Closeable { dependencies, unidentifiedAccessHelper, this::resolveSignalServiceAddress, - this::resolveRecipient, + account.getRecipientStore(), this::handleIdentityFailure, this::getGroup, this::refreshRegisteredUser); @@ -211,33 +209,37 @@ public class Manager implements Closeable { groupV2Helper, avatarStore, this::resolveSignalServiceAddress, - this::resolveRecipient); + account.getRecipientStore()); + this.storageHelper = new StorageHelper(account, dependencies, groupHelper); this.contactHelper = new ContactHelper(account); this.syncHelper = new SyncHelper(account, attachmentHelper, sendHelper, groupHelper, avatarStore, - this::resolveSignalServiceAddress, - this::resolveRecipient); + this::resolveSignalServiceAddress); + preKeyHelper = new PreKeyHelper(account, dependencies); this.context = new Context(account, - dependencies.getAccountManager(), - dependencies.getMessageReceiver(), + dependencies, stickerPackStore, sendHelper, groupHelper, syncHelper, - profileHelper); + profileHelper, + storageHelper, + preKeyHelper); var jobExecutor = new JobExecutor(context); this.incomingMessageHandler = new IncomingMessageHandler(account, dependencies, - this::resolveRecipient, + account.getRecipientStore(), + this::resolveSignalServiceAddress, groupHelper, contactHelper, attachmentHelper, syncHelper, + this::getRecipientProfile, jobExecutor); } @@ -249,10 +251,6 @@ public class Manager implements Closeable { return account.getSelfRecipientId(); } - private IdentityKeyPair getIdentityKeyPair() { - return account.getIdentityKeyPair(); - } - public int getDeviceId() { return account.getDeviceId(); } @@ -309,13 +307,11 @@ public class Manager implements Closeable { days); } } - if (dependencies.getAccountManager().getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { - refreshPreKeys(); - } + preKeyHelper.refreshPreKeysIfNecessary(); if (account.getUuid() == null) { account.setUuid(dependencies.getAccountManager().getOwnUuid()); } - updateAccountAttributes(); + updateAccountAttributes(null); } /** @@ -328,7 +324,7 @@ public class Manager implements Closeable { public Map> areUsersRegistered(Set numbers) throws IOException { Map canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { try { - return canonicalizePhoneNumber(n); + return PhoneNumberFormatter.formatNumber(n, account.getUsername()); } catch (InvalidNumberException e) { return ""; } @@ -347,14 +343,21 @@ public class Manager implements Closeable { })); } - public void updateAccountAttributes() throws IOException { + public void updateAccountAttributes(String deviceName) throws IOException { + final String encryptedDeviceName; + if (deviceName == null) { + encryptedDeviceName = account.getEncryptedDeviceName(); + } else { + final var privateKey = account.getIdentityKeyPair().getPrivateKey(); + encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey); + account.setEncryptedDeviceName(encryptedDeviceName); + } dependencies.getAccountManager() - .setAccountAttributes(account.getEncryptedDeviceName(), + .setAccountAttributes(encryptedDeviceName, null, account.getLocalRegistrationId(), true, - // set legacy pin only if no KBS master key is set - account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, + null, account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), @@ -386,11 +389,22 @@ 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); } + public void submitRateLimitRecaptchaChallenge(String challenge, String captcha) throws IOException { + dependencies.getAccountManager().submitRateLimitRecaptchaChallenge(challenge, captcha); + } + public List getLinkedDevices() throws IOException { var devices = dependencies.getAccountManager().getDevices(); account.setMultiDevice(devices.size() > 1); @@ -421,7 +435,7 @@ public class Manager implements Closeable { } private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { - var identityKeyPair = getIdentityKeyPair(); + var identityKeyPair = account.getIdentityKeyPair(); var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode(); dependencies.getAccountManager() @@ -454,29 +468,7 @@ public class Manager implements Closeable { } void refreshPreKeys() throws IOException { - var oneTimePreKeys = generatePreKeys(); - final var identityKeyPair = getIdentityKeyPair(); - var signedPreKeyRecord = generateSignedPreKey(identityKeyPair); - - dependencies.getAccountManager().setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); - } - - private List generatePreKeys() { - final var offset = account.getPreKeyIdOffset(); - - var records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); - account.addPreKeys(records); - - return records; - } - - private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) { - final var signedPreKeyId = account.getNextSignedPreKeyId(); - - var record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); - account.addSignedPreKey(record); - - return record; + preKeyHelper.refreshPreKeys(); } public Profile getRecipientProfile(RecipientId recipientId) { @@ -490,7 +482,7 @@ public class Manager implements Closeable { public SendGroupMessageResults quitGroup( GroupId groupId, Set groupAdmins ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException { - final var newAdmins = getRecipientIds(groupAdmins); + final var newAdmins = resolveRecipients(groupAdmins); return groupHelper.quitGroup(groupId, newAdmins); } @@ -501,7 +493,7 @@ public class Manager implements Closeable { public Pair createGroup( String name, Set members, File avatarFile ) throws IOException, AttachmentInvalidException { - return groupHelper.createGroup(name, members == null ? null : getRecipientIds(members), avatarFile); + return groupHelper.createGroup(name, members == null ? null : resolveRecipients(members), avatarFile); } public SendGroupMessageResults updateGroup( @@ -523,10 +515,10 @@ public class Manager implements Closeable { return groupHelper.updateGroup(groupId, name, description, - members == null ? null : getRecipientIds(members), - removeMembers == null ? null : getRecipientIds(removeMembers), - admins == null ? null : getRecipientIds(admins), - removeAdmins == null ? null : getRecipientIds(removeAdmins), + members == null ? null : resolveRecipients(members), + removeMembers == null ? null : resolveRecipients(removeMembers), + admins == null ? null : resolveRecipients(admins), + removeAdmins == null ? null : resolveRecipients(removeAdmins), resetGroupLink, groupLinkState, addMemberPermission, @@ -662,7 +654,7 @@ public class Manager implements Closeable { public void setContactName( RecipientIdentifier.Single recipient, String name - ) throws NotMasterDeviceException { + ) throws NotMasterDeviceException, UnregisteredUserException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } @@ -737,6 +729,13 @@ public class Manager implements Closeable { public void requestAllSyncData() throws IOException { syncHelper.requestAllSyncData(); + retrieveRemoteStorage(); + } + + void retrieveRemoteStorage() throws IOException { + if (account.getStorageKey() != null) { + storageHelper.readDataFromStorage(); + } } private byte[] getSenderCertificate() { @@ -755,53 +754,28 @@ public class Manager implements Closeable { return certificate; } - private Set getRecipientIds(Collection recipients) { - final var signalServiceAddresses = new HashSet(recipients.size()); - final var addressesMissingUuid = new HashSet(); - - for (var number : recipients) { - final var resolvedAddress = resolveSignalServiceAddress(resolveRecipient(number)); - if (resolvedAddress.getUuid().isPresent()) { - signalServiceAddresses.add(resolvedAddress); - } else { - addressesMissingUuid.add(resolvedAddress); - } - } - - final var numbersMissingUuid = addressesMissingUuid.stream() - .map(a -> a.getNumber().get()) - .collect(Collectors.toSet()); - Map registeredUsers; - try { - registeredUsers = getRegisteredUsers(numbersMissingUuid); - } catch (IOException e) { - logger.warn("Failed to resolve uuids from server, ignoring: {}", e.getMessage()); - registeredUsers = Map.of(); - } - - for (var address : addressesMissingUuid) { - final var number = address.getNumber().get(); - if (registeredUsers.containsKey(number)) { - final var newAddress = resolveSignalServiceAddress(resolveRecipientTrusted(new SignalServiceAddress( - registeredUsers.get(number), - number))); - signalServiceAddresses.add(newAddress); - } else { - signalServiceAddresses.add(address); - } - } - - return signalServiceAddresses.stream().map(this::resolveRecipient).collect(Collectors.toSet()); - } - 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 uuidMap = getRegisteredUsers(Set.of(number)); - return resolveRecipientTrusted(new SignalServiceAddress(uuidMap.getOrDefault(number, null), number)); + final var uuid = getRegisteredUser(number); + return resolveRecipientTrusted(new SignalServiceAddress(uuid, number)); + } + + private UUID getRegisteredUser(final String number) throws IOException { + final Map uuidMap; + try { + uuidMap = getRegisteredUsers(Set.of(number)); + } catch (NumberFormatException e) { + throw new UnregisteredUserException(number, e); + } + final var uuid = uuidMap.get(number); + if (uuid == null) { + throw new UnregisteredUserException(number, null); + } + return uuid; } private Map getRegisteredUsers(final Set numbers) throws IOException { @@ -843,32 +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 (!envelope.hasSource()) { - final var identifier = e.getSender(); - final var recipientId = 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; } @@ -887,7 +862,7 @@ public class Manager implements Closeable { final var signalWebSocket = dependencies.getSignalWebSocket(); signalWebSocket.connect(); - var hasCaughtUpWithOldMessages = false; + hasCaughtUpWithOldMessages = false; while (!Thread.interrupted()) { SignalServiceEnvelope envelope; @@ -896,22 +871,25 @@ public class Manager implements Closeable { logger.debug("Checking for new message from server"); try { var result = signalWebSocket.readOrEmpty(unit.toMillis(timeout), envelope1 -> { - final var recipientId = envelope1.hasSource() - ? resolveRecipient(envelope1.getSourceIdentifier()) + final var recipientId = envelope1.hasSourceUuid() + ? resolveRecipient(envelope1.getSourceAddress()) : null; // store message on disk, before acknowledging receipt to the server cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId); }); - logger.debug("New message received from server"); if (result.isPresent()) { envelope = result.get(); + logger.debug("New message received from server"); } else { - // Received indicator that server queue is empty - hasCaughtUpWithOldMessages = true; - + logger.debug("Received indicator that server queue is empty"); handleQueuedActions(queuedActions); queuedActions.clear(); + hasCaughtUpWithOldMessages = true; + synchronized (this) { + this.notifyAll(); + } + // Continue to wait another timeout for new messages continue; } @@ -939,10 +917,10 @@ public class Manager implements Closeable { handleQueuedActions(queuedActions); } if (cachedMessage[0] != null) { - if (exception instanceof ProtocolUntrustedIdentityException) { - final var identifier = ((ProtocolUntrustedIdentityException) exception).getSender(); - final var recipientId = resolveRecipient(identifier); - if (!envelope.hasSource()) { + if (exception instanceof UntrustedIdentityException) { + final var address = ((UntrustedIdentityException) exception).getSender(); + final var recipientId = resolveRecipient(address); + if (!envelope.hasSourceUuid()) { try { cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); } catch (IOException ioException) { @@ -958,21 +936,36 @@ public class Manager implements Closeable { handleQueuedActions(queuedActions); } + public boolean hasCaughtUpWithOldMessages() { + return hasCaughtUpWithOldMessages; + } + private void handleQueuedActions(final Collection queuedActions) { + var interrupted = false; for (var action : queuedActions) { try { action.execute(context); } catch (Throwable e) { - if (e instanceof AssertionError && e.getCause() instanceof InterruptedException) { - Thread.currentThread().interrupt(); + if ((e instanceof AssertionError || e instanceof RuntimeException) + && e.getCause() instanceof InterruptedException) { + interrupted = true; + continue; } logger.warn("Message action failed.", e); } } + if (interrupted) { + Thread.currentThread().interrupt(); + } } public boolean isContactBlocked(final RecipientIdentifier.Single recipient) { - final var recipientId = resolveRecipient(recipient); + final RecipientId recipientId; + try { + recipientId = resolveRecipient(recipient); + } catch (UnregisteredUserException e) { + return false; + } return contactHelper.isContactBlocked(recipientId); } @@ -989,7 +982,12 @@ public class Manager implements Closeable { } public String getContactOrProfileName(RecipientIdentifier.Single recipientIdentifier) { - final var recipientId = resolveRecipient(recipientIdentifier); + final RecipientId recipientId; + try { + recipientId = resolveRecipient(recipientIdentifier); + } catch (UnregisteredUserException e) { + return null; + } final var contact = account.getContactStore().getContact(recipientId); if (contact != null && !Util.isEmpty(contact.getName())) { @@ -1013,7 +1011,12 @@ public class Manager implements Closeable { } public List getIdentities(RecipientIdentifier.Single recipient) { - final var identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient)); + IdentityInfo identity; + try { + identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient)); + } catch (UnregisteredUserException e) { + identity = null; + } return identity == null ? List.of() : List.of(identity); } @@ -1024,7 +1027,12 @@ public class Manager implements Closeable { * @param fingerprint Fingerprint */ public boolean trustIdentityVerified(RecipientIdentifier.Single recipient, byte[] fingerprint) { - var recipientId = resolveRecipient(recipient); + RecipientId recipientId; + try { + recipientId = resolveRecipient(recipient); + } catch (UnregisteredUserException e) { + return false; + } return trustIdentity(recipientId, identityKey -> Arrays.equals(identityKey.serialize(), fingerprint), TrustLevel.TRUSTED_VERIFIED); @@ -1037,8 +1045,13 @@ public class Manager implements Closeable { * @param safetyNumber Safety number */ public boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, String safetyNumber) { - var recipientId = resolveRecipient(recipient); - var address = account.getRecipientStore().resolveServiceAddress(recipientId); + RecipientId recipientId; + try { + recipientId = resolveRecipient(recipient); + } catch (UnregisteredUserException e) { + return false; + } + var address = resolveSignalServiceAddress(recipientId); return trustIdentity(recipientId, identityKey -> safetyNumber.equals(computeSafetyNumber(address, identityKey)), TrustLevel.TRUSTED_VERIFIED); @@ -1051,8 +1064,13 @@ public class Manager implements Closeable { * @param safetyNumber Scannable safety number */ public boolean trustIdentityVerifiedSafetyNumber(RecipientIdentifier.Single recipient, byte[] safetyNumber) { - var recipientId = resolveRecipient(recipient); - var address = account.getRecipientStore().resolveServiceAddress(recipientId); + RecipientId recipientId; + try { + recipientId = resolveRecipient(recipient); + } catch (UnregisteredUserException e) { + return false; + } + var address = resolveSignalServiceAddress(recipientId); return trustIdentity(recipientId, identityKey -> { final var fingerprint = computeSafetyNumberFingerprint(address, identityKey); try { @@ -1069,7 +1087,12 @@ public class Manager implements Closeable { * @param recipient username of the identity */ public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) { - var recipientId = resolveRecipient(recipient); + RecipientId recipientId; + try { + recipientId = resolveRecipient(recipient); + } catch (UnregisteredUserException e) { + return false; + } return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED); } @@ -1087,7 +1110,7 @@ public class Manager implements Closeable { account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel); try { - var address = account.getRecipientStore().resolveServiceAddress(recipientId); + var address = resolveSignalServiceAddress(recipientId); syncHelper.sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel); } catch (IOException e) { logger.warn("Failed to send verification sync message: {}", e.getMessage()); @@ -1126,53 +1149,62 @@ public class Manager implements Closeable { ) { return Utils.computeSafetyNumber(capabilities.isUuid(), account.getSelfAddress(), - getIdentityKeyPair().getPublicKey(), + account.getIdentityKeyPair().getPublicKey(), theirAddress, theirIdentityKey); } - @Deprecated - public SignalServiceAddress resolveSignalServiceAddress(String identifier) { - var address = Utils.getSignalServiceAddressFromIdentifier(identifier); - - return resolveSignalServiceAddress(address); - } - - @Deprecated public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) { - if (address.matches(account.getSelfAddress())) { - return account.getSelfAddress(); - } + return resolveSignalServiceAddress(resolveRecipient(address)); + } - return account.getRecipientStore().resolveServiceAddress(address); + public SignalServiceAddress resolveSignalServiceAddress(UUID uuid) { + return resolveSignalServiceAddress(account.getRecipientStore().resolveRecipient(uuid)); } public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { - return account.getRecipientStore().resolveServiceAddress(recipientId); - } + final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); + if (address.getUuid().isPresent()) { + return address.toSignalServiceAddress(); + } - private String canonicalizePhoneNumber(final String number) throws InvalidNumberException { - return PhoneNumberFormatter.formatNumber(number, account.getUsername()); + // 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.getNumber().get(); + try { + return resolveSignalServiceAddress(getRegisteredUser(number)); + } catch (IOException e) { + logger.warn("Failed to get uuid for e164 number: {}", number, e); + // Return SignalServiceAddress with unknown UUID + return address.toSignalServiceAddress(); + } } - private RecipientId resolveRecipient(final String identifier) { - var address = Utils.getSignalServiceAddressFromIdentifier(identifier); - - return resolveRecipient(address); + private Set resolveRecipients(Collection recipients) throws UnregisteredUserException { + 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) { - final SignalServiceAddress address; + private RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredUserException { if (recipient instanceof RecipientIdentifier.Uuid) { - address = new SignalServiceAddress(((RecipientIdentifier.Uuid) recipient).uuid, null); + return account.getRecipientStore().resolveRecipient(((RecipientIdentifier.Uuid) recipient).uuid); } else { - address = new SignalServiceAddress(null, ((RecipientIdentifier.Number) recipient).number); + final var number = ((RecipientIdentifier.Number) recipient).number; + return account.getRecipientStore().resolveRecipient(number, () -> { + try { + return getRegisteredUser(number); + } catch (IOException e) { + return null; + } + }); } - - return resolveRecipient(address); } - public RecipientId resolveRecipient(SignalServiceAddress address) { + private RecipientId resolveRecipient(SignalServiceAddress address) { return account.getRecipientStore().resolveRecipient(address); }