From 8bcd8d87d219ae0496986cba4bd6b89f3b2ad6f6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 26 Aug 2021 21:23:30 +0200 Subject: [PATCH] Update libsignal-service-java --- lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/AvatarStore.java | 2 +- .../org/asamk/signal/manager/Manager.java | 217 ++++++++------- .../manager/UntrustedIdentityException.java | 27 ++ .../manager/api/RecipientIdentifier.java | 4 +- .../signal/manager/helper/GroupV2Helper.java | 62 ++--- .../helper/IncomingMessageHandler.java | 262 +++++++++--------- .../signal/manager/helper/ProfileHelper.java | 10 +- .../signal/manager/helper/SendHelper.java | 54 +++- .../signal/manager/helper/SyncHelper.java | 51 +--- .../signal/manager/storage/SignalAccount.java | 16 +- .../asamk/signal/manager/storage/Utils.java | 12 + .../storage/contacts/LegacyContactInfo.java | 6 +- .../manager/storage/groups/GroupInfoV2.java | 9 +- .../manager/storage/groups/GroupStore.java | 14 +- .../storage/identities/IdentityKeyStore.java | 3 +- .../storage/profiles/LegacyProfileStore.java | 6 +- .../profiles/LegacySignalProfileEntry.java | 12 +- .../storage/protocol/LegacyIdentityInfo.java | 10 +- .../protocol/LegacyJsonIdentityKeyStore.java | 16 +- .../protocol/LegacyJsonSessionStore.java | 12 +- .../storage/protocol/LegacySessionInfo.java | 6 +- .../storage/protocol/SignalProtocolStore.java | 12 + .../recipients/LegacyRecipientStore.java | 14 +- .../manager/storage/recipients/Recipient.java | 11 +- .../storage/recipients/RecipientAddress.java | 89 ++++++ .../storage/recipients/RecipientResolver.java | 8 + .../storage/recipients/RecipientStore.java | 97 ++++--- .../storage/sessions/SessionStore.java | 17 +- .../org/asamk/signal/manager/util/Utils.java | 14 +- run_tests.sh | 4 +- .../signal/JsonDbusReceiveMessageHandler.java | 2 +- .../asamk/signal/ReceiveMessageHandler.java | 13 +- .../signal/commands/JoinGroupCommand.java | 9 +- .../signal/commands/ListContactsCommand.java | 3 +- .../signal/commands/ListGroupsCommand.java | 4 +- .../commands/ListIdentitiesCommand.java | 3 +- .../signal/commands/QuitGroupCommand.java | 6 +- .../signal/commands/RemoteDeleteCommand.java | 6 +- .../asamk/signal/commands/SendCommand.java | 21 +- .../signal/commands/SendReactionCommand.java | 6 +- .../signal/commands/SendReceiptCommand.java | 5 +- .../signal/commands/SendTypingCommand.java | 5 +- .../signal/commands/UpdateGroupCommand.java | 6 +- .../org/asamk/signal/dbus/DbusSignalImpl.java | 3 + .../org/asamk/signal/json/JsonMention.java | 7 +- .../signal/json/JsonMessageEnvelope.java | 30 +- .../java/org/asamk/signal/json/JsonQuote.java | 3 +- .../org/asamk/signal/json/JsonReaction.java | 4 +- .../signal/json/JsonSyncDataMessage.java | 4 +- .../signal/json/JsonSyncReadMessage.java | 4 +- src/main/java/org/asamk/signal/util/Util.java | 2 +- 52 files changed, 683 insertions(+), 542 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index dcb99cee..316ce564 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - api("com.github.turasa:signal-service-java:2.15.3_unofficial_25") + api("com.github.turasa:signal-service-java:2.15.3_unofficial_26") implementation("com.google.protobuf:protobuf-javalite:3.10.0") implementation("org.bouncycastle:bcprov-jdk15on:1.69") implementation("org.slf4j:slf4j-api:1.7.30") diff --git a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java index 8a1e6172..12a6525e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java @@ -82,7 +82,7 @@ public class AvatarStore { } private String getLegacyIdentifier(final SignalServiceAddress address) { - return address.getNumber().or(() -> address.getUuid().get().toString()); + return address.getNumber().or(() -> address.getUuid().toString()); } private File getProfileAvatarFile(SignalServiceAddress address) { 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 bbdfa9e5..9e38853b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -73,7 +73,6 @@ 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 +82,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; @@ -200,7 +200,7 @@ public class Manager implements Closeable { dependencies, unidentifiedAccessHelper, this::resolveSignalServiceAddress, - this::resolveRecipient, + account.getRecipientStore(), this::handleIdentityFailure, this::getGroup, this::refreshRegisteredUser); @@ -211,15 +211,14 @@ public class Manager implements Closeable { groupV2Helper, avatarStore, this::resolveSignalServiceAddress, - this::resolveRecipient); + account.getRecipientStore()); this.contactHelper = new ContactHelper(account); this.syncHelper = new SyncHelper(account, attachmentHelper, sendHelper, groupHelper, avatarStore, - this::resolveSignalServiceAddress, - this::resolveRecipient); + this::resolveSignalServiceAddress); this.context = new Context(account, dependencies.getAccountManager(), @@ -233,7 +232,8 @@ public class Manager implements Closeable { this.incomingMessageHandler = new IncomingMessageHandler(account, dependencies, - this::resolveRecipient, + account.getRecipientStore(), + this::resolveSignalServiceAddress, groupHelper, contactHelper, attachmentHelper, @@ -328,7 +328,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 ""; } @@ -490,7 +490,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 +501,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 +523,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 +662,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(); } @@ -755,53 +755,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 { @@ -856,9 +831,9 @@ public class Manager implements Closeable { cachedMessage.delete(); return null; } - if (!envelope.hasSource()) { + if (!envelope.hasSourceUuid()) { final var identifier = e.getSender(); - final var recipientId = resolveRecipient(identifier); + final var recipientId = account.getRecipientStore().resolveRecipient(identifier); try { account.getMessageCache().replaceSender(cachedMessage, recipientId); } catch (IOException ioException) { @@ -901,8 +876,8 @@ 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); @@ -944,10 +919,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) { @@ -977,7 +952,12 @@ public class Manager implements Closeable { } 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); } @@ -994,7 +974,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())) { @@ -1018,7 +1003,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); } @@ -1029,7 +1019,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); @@ -1042,8 +1037,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); @@ -1056,8 +1056,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 { @@ -1074,7 +1079,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); } @@ -1092,7 +1102,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()); @@ -1136,48 +1146,61 @@ public class Manager implements Closeable { 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 account.getRecipientStore().resolveServiceAddress(address); + return resolveSignalServiceAddress(resolveRecipient(address)); } - public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { - return account.getRecipientStore().resolveServiceAddress(recipientId); + public SignalServiceAddress resolveSignalServiceAddress(UUID uuid) { + return resolveSignalServiceAddress(account.getRecipientStore().resolveRecipient(uuid)); } - private String canonicalizePhoneNumber(final String number) throws InvalidNumberException { - return PhoneNumberFormatter.formatNumber(number, account.getUsername()); - } + public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { + final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); + if (address.getUuid().isPresent()) { + return address.toSignalServiceAddress(); + } - private RecipientId resolveRecipient(final String identifier) { - var address = Utils.getSignalServiceAddressFromIdentifier(identifier); + // 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(); + } + } - 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); } diff --git a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java b/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java new file mode 100644 index 00000000..3b90b9e4 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java @@ -0,0 +1,27 @@ +package org.asamk.signal.manager; + +import org.whispersystems.signalservice.api.push.SignalServiceAddress; + +public class UntrustedIdentityException extends Exception { + + private final SignalServiceAddress sender; + private final Integer senderDevice; + + public UntrustedIdentityException(final SignalServiceAddress sender) { + this(sender, null); + } + + public UntrustedIdentityException(final SignalServiceAddress sender, final Integer senderDevice) { + super("Untrusted identity: " + sender.getIdentifier()); + this.sender = sender; + this.senderDevice = senderDevice; + } + + public SignalServiceAddress getSender() { + return sender; + } + + public Integer getSenderDevice() { + return senderDevice; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/api/RecipientIdentifier.java b/lib/src/main/java/org/asamk/signal/manager/api/RecipientIdentifier.java index cbcf1724..4a66cbb3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/RecipientIdentifier.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/RecipientIdentifier.java @@ -32,9 +32,7 @@ public abstract class RecipientIdentifier { } public static Single fromAddress(SignalServiceAddress address) { - return address.getUuid().isPresent() - ? new Uuid(address.getUuid().get()) - : new Number(address.getNumber().get()); + return new Uuid(address.getUuid()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 19240cef..3187fca1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -150,11 +150,9 @@ public class GroupV2Helper { if (!areMembersValid(members)) return null; - var self = new GroupCandidate(addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) - .getUuid() - .orNull(), Optional.fromNullable(profileKeyCredential)); + var self = new GroupCandidate(getSelfUuid(), Optional.fromNullable(profileKeyCredential)); var candidates = members.stream() - .map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid().get(), + .map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid(), Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member)))) .collect(Collectors.toSet()); @@ -169,18 +167,6 @@ public class GroupV2Helper { } private boolean areMembersValid(final Set members) { - final var noUuidCapability = members.stream() - .map(addressResolver::resolveSignalServiceAddress) - .filter(address -> !address.getUuid().isPresent()) - .map(SignalServiceAddress::getNumber) - .map(Optional::get) - .collect(Collectors.toSet()); - if (noUuidCapability.size() > 0) { - logger.warn("Cannot create a V2 group as some members don't have a UUID: {}", - String.join(", ", noUuidCapability)); - return false; - } - final var noGv2Capability = members.stream() .map(profileProvider::getProfile) .filter(profile -> profile != null && !profile.getCapabilities().contains(Profile.Capability.gv2)) @@ -214,11 +200,8 @@ public class GroupV2Helper { change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder().setAvatar(avatarCdnKey)); } - final var uuid = addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId()) - .getUuid(); - if (uuid.isPresent()) { - change.setSourceUuid(UuidUtil.toByteString(uuid.get())); - } + final var uuid = getSelfUuid(); + change.setSourceUuid(UuidUtil.toByteString(uuid)); return commitChange(groupInfoV2, change); } @@ -233,13 +216,11 @@ public class GroupV2Helper { } var candidates = newMembers.stream() - .map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid().get(), + .map(member -> new GroupCandidate(addressResolver.resolveSignalServiceAddress(member).getUuid(), Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member)))) .collect(Collectors.toSet()); - final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) - .getUuid() - .get(); + final var uuid = getSelfUuid(); final var change = groupOperations.createModifyGroupMembershipChange(candidates, uuid); change.setSourceUuid(UuidUtil.toByteString(uuid)); @@ -251,9 +232,7 @@ public class GroupV2Helper { GroupInfoV2 groupInfoV2, Set membersToMakeAdmin ) throws IOException { var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); - final var selfUuid = addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) - .getUuid() - .get(); + final var selfUuid = getSelfUuid(); var selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, selfUuid); if (selfPendingMember.isPresent()) { @@ -263,7 +242,6 @@ public class GroupV2Helper { final var adminUuids = membersToMakeAdmin.stream() .map(addressResolver::resolveSignalServiceAddress) .map(SignalServiceAddress::getUuid) - .map(Optional::get) .collect(Collectors.toList()); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfUuid, adminUuids)); @@ -275,8 +253,6 @@ public class GroupV2Helper { final var memberUuids = members.stream() .map(addressResolver::resolveSignalServiceAddress) .map(SignalServiceAddress::getUuid) - .filter(Optional::isPresent) - .map(Optional::get) .collect(Collectors.toSet()); return ejectMembers(groupInfoV2, memberUuids); } @@ -288,8 +264,6 @@ public class GroupV2Helper { final var memberUuids = members.stream() .map(addressResolver::resolveSignalServiceAddress) .map(SignalServiceAddress::getUuid) - .filter(Optional::isPresent) - .map(Optional::get) .map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid)) .filter(Optional::isPresent) .map(Optional::get) @@ -360,8 +334,7 @@ public class GroupV2Helper { : groupOperations.createGroupJoinDirect(profileKeyCredential); change.setSourceUuid(UuidUtil.toByteString(addressResolver.resolveSignalServiceAddress(selfRecipientId) - .getUuid() - .get())); + .getUuid())); return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword); } @@ -378,9 +351,7 @@ public class GroupV2Helper { final var change = groupOperations.createAcceptInviteChange(profileKeyCredential); final var uuid = addressResolver.resolveSignalServiceAddress(selfRecipientId).getUuid(); - if (uuid.isPresent()) { - change.setSourceUuid(UuidUtil.toByteString(uuid.get())); - } + change.setSourceUuid(UuidUtil.toByteString(uuid)); return commitChange(groupInfoV2, change); } @@ -391,7 +362,7 @@ public class GroupV2Helper { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var address = addressResolver.resolveSignalServiceAddress(recipientId); final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT; - final var change = groupOperations.createChangeMemberRole(address.getUuid().get(), newRole); + final var change = groupOperations.createChangeMemberRole(address.getUuid(), newRole); return commitChange(groupInfoV2, change); } @@ -473,10 +444,7 @@ public class GroupV2Helper { final DecryptedGroup decryptedGroupState; try { - decryptedChange = groupOperations.decryptChange(changeActions, - addressResolver.resolveSignalServiceAddress(selfRecipientIdProvider.getSelfRecipientId()) - .getUuid() - .get()); + decryptedChange = groupOperations.decryptChange(changeActions, getSelfUuid()); decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange); } catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) { throw new IOException(e); @@ -543,13 +511,15 @@ public class GroupV2Helper { final var credentials = groupsV2Api.getCredentials(today); // TODO cache credentials until they expire var authCredentialResponse = credentials.get(today); - final var uuid = addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId()) - .getUuid() - .get(); + final var uuid = getSelfUuid(); try { return groupsV2Api.getGroupsV2AuthorizationString(uuid, today, groupSecretParams, authCredentialResponse); } catch (VerificationFailedException e) { throw new IOException(e); } } + + private UUID getSelfUuid() { + return addressResolver.resolveSignalServiceAddress(this.selfRecipientIdProvider.getSelfRecipientId()).getUuid(); + } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index b0b42545..f28b3638 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -4,6 +4,7 @@ import org.asamk.signal.manager.JobExecutor; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.actions.HandleAction; import org.asamk.signal.manager.actions.RenewSessionAction; import org.asamk.signal.manager.actions.RetrieveProfileAction; @@ -34,6 +35,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -48,6 +50,7 @@ public final class IncomingMessageHandler { private final SignalAccount account; private final SignalDependencies dependencies; private final RecipientResolver recipientResolver; + private final SignalServiceAddressResolver addressResolver; private final GroupHelper groupHelper; private final ContactHelper contactHelper; private final AttachmentHelper attachmentHelper; @@ -58,6 +61,7 @@ public final class IncomingMessageHandler { final SignalAccount account, final SignalDependencies dependencies, final RecipientResolver recipientResolver, + final SignalServiceAddressResolver addressResolver, final GroupHelper groupHelper, final ContactHelper contactHelper, final AttachmentHelper attachmentHelper, @@ -67,6 +71,7 @@ public final class IncomingMessageHandler { this.account = account; this.dependencies = dependencies; this.recipientResolver = recipientResolver; + this.addressResolver = addressResolver; this.groupHelper = groupHelper; this.contactHelper = contactHelper; this.attachmentHelper = attachmentHelper; @@ -80,7 +85,7 @@ public final class IncomingMessageHandler { final Manager.ReceiveMessageHandler handler ) { final var actions = new ArrayList(); - if (envelope.hasSource()) { + if (envelope.hasSourceUuid()) { // Store uuid if we don't have it already // address/uuid in envelope is sent by server account.getRecipientStore().resolveRecipientTrusted(envelope.getSourceAddress()); @@ -93,6 +98,8 @@ public final class IncomingMessageHandler { } catch (ProtocolUntrustedIdentityException e) { final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); actions.add(new RetrieveProfileAction(recipientId)); + exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress(recipientId), + e.getSenderDevice()); } catch (ProtocolInvalidMessageException e) { final var sender = account.getRecipientStore().resolveRecipient(e.getSender()); logger.debug("Received invalid message, queuing renew session action."); @@ -102,7 +109,7 @@ public final class IncomingMessageHandler { exception = e; } - if (!envelope.hasSource() && content != null) { + if (!envelope.hasSourceUuid() && content != null) { // Store uuid if we don't have it already // address/uuid is validated by unidentified sender certificate account.getRecipientStore().resolveRecipientTrusted(content.getSender()); @@ -113,7 +120,7 @@ public final class IncomingMessageHandler { logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp()); } else if (isNotAllowedToSendToGroup(envelope, content)) { logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}", - (envelope.hasSource() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(), + (envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(), envelope.getTimestamp()); } else { actions.addAll(handleMessage(envelope, content, ignoreAttachments)); @@ -126,146 +133,153 @@ public final class IncomingMessageHandler { SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments ) { var actions = new ArrayList(); - if (content != null) { - final RecipientId sender; - if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { - sender = recipientResolver.resolveRecipient(envelope.getSourceAddress()); - } else { - sender = recipientResolver.resolveRecipient(content.getSender()); + if (content == null) { + return actions; + } + + final RecipientId sender; + if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { + sender = recipientResolver.resolveRecipient(envelope.getSourceAddress()); + } else { + sender = recipientResolver.resolveRecipient(content.getSender()); + } + + if (content.getDataMessage().isPresent()) { + var message = content.getDataMessage().get(); + + if (content.isNeedsReceipt()) { + actions.add(new SendReceiptAction(sender, message.getTimestamp())); } - if (content.getDataMessage().isPresent()) { - var message = content.getDataMessage().get(); + actions.addAll(handleSignalServiceDataMessage(message, + false, + sender, + account.getSelfRecipientId(), + ignoreAttachments)); + } - if (content.isNeedsReceipt()) { - actions.add(new SendReceiptAction(sender, message.getTimestamp())); - } + if (content.getSyncMessage().isPresent()) { + var syncMessage = content.getSyncMessage().get(); + actions.addAll(handleSyncMessage(syncMessage, sender, ignoreAttachments)); + } - actions.addAll(handleSignalServiceDataMessage(message, - false, - sender, - account.getSelfRecipientId(), - ignoreAttachments)); + return actions; + } + + private List handleSyncMessage( + final SignalServiceSyncMessage syncMessage, final RecipientId sender, final boolean ignoreAttachments + ) { + var actions = new ArrayList(); + account.setMultiDevice(true); + if (syncMessage.getSent().isPresent()) { + var message = syncMessage.getSent().get(); + final var destination = message.getDestination().orNull(); + actions.addAll(handleSignalServiceDataMessage(message.getMessage(), + true, + sender, + destination == null ? null : recipientResolver.resolveRecipient(destination), + ignoreAttachments)); + } + if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) { + var rm = syncMessage.getRequest().get(); + if (rm.isContactsRequest()) { + actions.add(SendSyncContactsAction.create()); } - if (content.getSyncMessage().isPresent()) { - account.setMultiDevice(true); - var syncMessage = content.getSyncMessage().get(); - if (syncMessage.getSent().isPresent()) { - var message = syncMessage.getSent().get(); - final var destination = message.getDestination().orNull(); - actions.addAll(handleSignalServiceDataMessage(message.getMessage(), - true, - sender, - destination == null ? null : recipientResolver.resolveRecipient(destination), - ignoreAttachments)); - } - if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) { - var rm = syncMessage.getRequest().get(); - if (rm.isContactsRequest()) { - actions.add(SendSyncContactsAction.create()); - } - if (rm.isGroupsRequest()) { - actions.add(SendSyncGroupsAction.create()); - } - if (rm.isBlockedListRequest()) { - actions.add(SendSyncBlockedListAction.create()); - } - // TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest(); + if (rm.isGroupsRequest()) { + actions.add(SendSyncGroupsAction.create()); + } + if (rm.isBlockedListRequest()) { + actions.add(SendSyncBlockedListAction.create()); + } + // TODO Handle rm.isConfigurationRequest(); rm.isKeysRequest(); + } + if (syncMessage.getGroups().isPresent()) { + logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring."); + } + if (syncMessage.getBlockedList().isPresent()) { + final var blockedListMessage = syncMessage.getBlockedList().get(); + for (var address : blockedListMessage.getAddresses()) { + contactHelper.setContactBlocked(recipientResolver.resolveRecipient(address), true); + } + for (var groupId : blockedListMessage.getGroupIds() + .stream() + .map(GroupId::unknownVersion) + .collect(Collectors.toSet())) { + try { + groupHelper.setGroupBlocked(groupId, true); + } catch (GroupNotFoundException e) { + logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}", + groupId.toBase64()); } - if (syncMessage.getGroups().isPresent()) { - try { - final var groupsMessage = syncMessage.getGroups().get(); - attachmentHelper.retrieveAttachment(groupsMessage, syncHelper::handleSyncDeviceGroups); - } catch (Exception e) { - logger.warn("Failed to handle received sync groups, ignoring: {}", e.getMessage()); - } + } + } + if (syncMessage.getContacts().isPresent()) { + try { + final var contactsMessage = syncMessage.getContacts().get(); + attachmentHelper.retrieveAttachment(contactsMessage.getContactsStream(), + syncHelper::handleSyncDeviceContacts); + } catch (Exception e) { + logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage()); + } + } + if (syncMessage.getVerified().isPresent()) { + final var verifiedMessage = syncMessage.getVerified().get(); + account.getIdentityKeyStore() + .setIdentityTrustLevel(account.getRecipientStore() + .resolveRecipientTrusted(verifiedMessage.getDestination()), + verifiedMessage.getIdentityKey(), + TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); + } + if (syncMessage.getStickerPackOperations().isPresent()) { + final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); + for (var m : stickerPackOperationMessages) { + if (!m.getPackId().isPresent()) { + continue; } - if (syncMessage.getBlockedList().isPresent()) { - final var blockedListMessage = syncMessage.getBlockedList().get(); - for (var address : blockedListMessage.getAddresses()) { - contactHelper.setContactBlocked(recipientResolver.resolveRecipient(address), true); + final var stickerPackId = StickerPackId.deserialize(m.getPackId().get()); + final var installed = !m.getType().isPresent() + || m.getType().get() == StickerPackOperationMessage.Type.INSTALL; + + var sticker = account.getStickerStore().getSticker(stickerPackId); + if (m.getPackKey().isPresent()) { + if (sticker == null) { + sticker = new Sticker(stickerPackId, m.getPackKey().get()); } - for (var groupId : blockedListMessage.getGroupIds() - .stream() - .map(GroupId::unknownVersion) - .collect(Collectors.toSet())) { - try { - groupHelper.setGroupBlocked(groupId, true); - } catch (GroupNotFoundException e) { - logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}", - groupId.toBase64()); - } + if (installed) { + jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get())); } } - if (syncMessage.getContacts().isPresent()) { - try { - final var contactsMessage = syncMessage.getContacts().get(); - attachmentHelper.retrieveAttachment(contactsMessage.getContactsStream(), - syncHelper::handleSyncDeviceContacts); - } catch (Exception e) { - logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage()); - } - } - if (syncMessage.getVerified().isPresent()) { - final var verifiedMessage = syncMessage.getVerified().get(); - account.getIdentityKeyStore() - .setIdentityTrustLevel(account.getRecipientStore() - .resolveRecipientTrusted(verifiedMessage.getDestination()), - verifiedMessage.getIdentityKey(), - TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); - } - if (syncMessage.getStickerPackOperations().isPresent()) { - final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); - for (var m : stickerPackOperationMessages) { - if (!m.getPackId().isPresent()) { - continue; - } - final var stickerPackId = StickerPackId.deserialize(m.getPackId().get()); - final var installed = !m.getType().isPresent() - || m.getType().get() == StickerPackOperationMessage.Type.INSTALL; - - var sticker = account.getStickerStore().getSticker(stickerPackId); - if (m.getPackKey().isPresent()) { - if (sticker == null) { - sticker = new Sticker(stickerPackId, m.getPackKey().get()); - } - if (installed) { - jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get())); - } - } - if (sticker != null) { - sticker.setInstalled(installed); - account.getStickerStore().updateSticker(sticker); - } - } - } - if (syncMessage.getFetchType().isPresent()) { - switch (syncMessage.getFetchType().get()) { - case LOCAL_PROFILE: - actions.add(new RetrieveProfileAction(account.getSelfRecipientId())); - case STORAGE_MANIFEST: - // TODO - } - } - if (syncMessage.getKeys().isPresent()) { - final var keysMessage = syncMessage.getKeys().get(); - if (keysMessage.getStorageService().isPresent()) { - final var storageKey = keysMessage.getStorageService().get(); - account.setStorageKey(storageKey); - } + if (sticker != null) { + sticker.setInstalled(installed); + account.getStickerStore().updateSticker(sticker); } - if (syncMessage.getConfiguration().isPresent()) { + } + } + if (syncMessage.getFetchType().isPresent()) { + switch (syncMessage.getFetchType().get()) { + case LOCAL_PROFILE: + actions.add(new RetrieveProfileAction(account.getSelfRecipientId())); + case STORAGE_MANIFEST: // TODO - } } } + if (syncMessage.getKeys().isPresent()) { + final var keysMessage = syncMessage.getKeys().get(); + if (keysMessage.getStorageService().isPresent()) { + final var storageKey = keysMessage.getStorageService().get(); + account.setStorageKey(storageKey); + } + } + if (syncMessage.getConfiguration().isPresent()) { + // TODO + } return actions; } private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) { SignalServiceAddress source; - if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { + if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { source = envelope.getSourceAddress(); } else if (content != null) { source = content.getSender(); @@ -290,7 +304,7 @@ public final class IncomingMessageHandler { private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) { SignalServiceAddress source; - if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { + if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { source = envelope.getSourceAddress(); } else if (content != null) { source = content.getSender(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index ac75a573..52154798 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -273,7 +273,7 @@ public final class ProfileHelper { private Single retrieveProfile( RecipientId recipientId, SignalServiceProfile.RequestType requestType - ) throws IOException { + ) { var unidentifiedAccess = getUnidentifiedAccess(recipientId); var profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(recipientId)); @@ -286,7 +286,7 @@ public final class ProfileHelper { Optional profileKey, Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType - ) throws IOException { + ) { var profileService = profileServiceProvider.getProfileService(); Single> responseSingle; @@ -294,11 +294,7 @@ public final class ProfileHelper { responseSingle = profileService.getProfile(address, profileKey, unidentifiedAccess, requestType); } catch (NoClassDefFoundError e) { // Native zkgroup lib not available for ProfileKey - if (!address.getNumber().isPresent()) { - throw new NotFoundException("Can't request profile without number"); - } - var addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); - responseSingle = profileService.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); + responseSingle = profileService.getProfile(address, Optional.absent(), unidentifiedAccess, requestType); } return responseSingle.map(pair -> { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 058a04f2..da901a3d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -1,6 +1,7 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.SignalDependencies; +import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; @@ -13,8 +14,8 @@ import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.ContentHint; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; @@ -43,6 +44,20 @@ public class SendHelper { private final GroupProvider groupProvider; private final RecipientRegistrationRefresher recipientRegistrationRefresher; + private final SignalServiceMessageSender.IndividualSendEvents sendEvents = new SignalServiceMessageSender.IndividualSendEvents() { + @Override + public void onMessageEncrypted() { + } + + @Override + public void onMessageSent() { + } + + @Override + public void onSyncMessageSent() { + } + }; + public SendHelper( final SignalAccount account, final SignalDependencies dependencies, @@ -145,9 +160,12 @@ public class SendHelper { final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId ) throws IOException, UntrustedIdentityException { final var messageSender = dependencies.getMessageSender(); - messageSender.sendReceipt(addressResolver.resolveSignalServiceAddress(recipientId), - unidentifiedAccessHelper.getAccessFor(recipientId), - receiptMessage); + final var address = addressResolver.resolveSignalServiceAddress(recipientId); + try { + messageSender.sendReceipt(address, unidentifiedAccessHelper.getAccessFor(recipientId), receiptMessage); + } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { + throw new UntrustedIdentityException(address); + } } public SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException { @@ -162,7 +180,7 @@ public class SendHelper { final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId)); } - } catch (UntrustedIdentityException e) { + } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } @@ -183,7 +201,7 @@ public class SendHelper { var messageSender = dependencies.getMessageSender(); try { return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync()); - } catch (UntrustedIdentityException e) { + } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId()); return SendMessageResult.identityFailure(address, e.getIdentityKey()); } @@ -195,11 +213,15 @@ public class SendHelper { var messageSender = dependencies.getMessageSender(); final var address = addressResolver.resolveSignalServiceAddress(recipientId); try { - messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message); - } catch (UnregisteredUserException e) { - final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); - final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); - messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message); + try { + messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message); + } catch (UnregisteredUserException e) { + final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); + final var newAddress = addressResolver.resolveSignalServiceAddress(newRecipientId); + messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message); + } + } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { + throw new UntrustedIdentityException(address); } } @@ -247,7 +269,7 @@ public class SendHelper { message, sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()), () -> false); - } catch (UntrustedIdentityException e) { + } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { return List.of(); } } @@ -263,15 +285,17 @@ public class SendHelper { return messageSender.sendDataMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId), ContentHint.DEFAULT, - message); + message, + sendEvents); } catch (UnregisteredUserException e) { final var newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId); return messageSender.sendDataMessage(addressResolver.resolveSignalServiceAddress(newRecipientId), unidentifiedAccessHelper.getAccessFor(newRecipientId), ContentHint.DEFAULT, - message); + message, + sendEvents); } - } catch (UntrustedIdentityException e) { + } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index 48dc206e..461706f0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -2,11 +2,9 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.AvatarStore; import org.asamk.signal.manager.TrustLevel; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.recipients.Contact; -import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; @@ -21,7 +19,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; @@ -36,7 +33,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; -import java.util.List; import java.util.stream.Collectors; public class SyncHelper { @@ -49,7 +45,6 @@ public class SyncHelper { private final GroupHelper groupHelper; private final AvatarStore avatarStore; private final SignalServiceAddressResolver addressResolver; - private final RecipientResolver recipientResolver; public SyncHelper( final SignalAccount account, @@ -57,8 +52,7 @@ public class SyncHelper { final SendHelper sendHelper, final GroupHelper groupHelper, final AvatarStore avatarStore, - final SignalServiceAddressResolver addressResolver, - final RecipientResolver recipientResolver + final SignalServiceAddressResolver addressResolver ) { this.account = account; this.attachmentHelper = attachmentHelper; @@ -66,7 +60,6 @@ public class SyncHelper { this.groupHelper = groupHelper; this.avatarStore = avatarStore; this.addressResolver = addressResolver; - this.recipientResolver = recipientResolver; } public void requestAllSyncData() throws IOException { @@ -222,48 +215,6 @@ public class SyncHelper { sendHelper.sendSyncMessage(SignalServiceSyncMessage.forVerified(verifiedMessage)); } - public void handleSyncDeviceGroups(final InputStream input) { - final var s = new DeviceGroupsInputStream(input); - DeviceGroup g; - while (true) { - try { - g = s.read(); - } catch (IOException e) { - logger.warn("Sync groups contained invalid group, ignoring: {}", e.getMessage()); - continue; - } - if (g == null) { - break; - } - var syncGroup = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(g.getId())); - if (syncGroup != null) { - if (g.getName().isPresent()) { - syncGroup.name = g.getName().get(); - } - syncGroup.addMembers(g.getMembers() - .stream() - .map(recipientResolver::resolveRecipient) - .collect(Collectors.toSet())); - if (!g.isActive()) { - syncGroup.removeMember(account.getSelfRecipientId()); - } else { - // Add ourself to the member set as it's marked as active - syncGroup.addMembers(List.of(account.getSelfRecipientId())); - } - syncGroup.blocked = g.isBlocked(); - if (g.getColor().isPresent()) { - syncGroup.color = g.getColor().get(); - } - - if (g.getAvatar().isPresent()) { - groupHelper.downloadGroupAvatar(syncGroup.getGroupId(), g.getAvatar().get()); - } - syncGroup.archived = g.isArchived(); - account.getGroupStore().updateGroup(syncGroup); - } - } - } - public void handleSyncDeviceContacts(final InputStream input) { final var s = new DeviceContactsInputStream(input); DeviceContact c; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 477e02dc..c972c2c7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -152,7 +152,7 @@ public class SignalAccount implements Closeable { account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), - account.recipientStore::resolveRecipient, + account.recipientStore, account::saveGroupStore); account.stickerStore = new StickerStore(account::saveStickerStore); @@ -174,9 +174,9 @@ public class SignalAccount implements Closeable { preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, username)); signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, username)); - sessionStore = new SessionStore(getSessionsPath(dataPath, username), recipientStore::resolveRecipient); + sessionStore = new SessionStore(getSessionsPath(dataPath, username), recipientStore); identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, username), - recipientStore::resolveRecipient, + recipientStore, identityKey, registrationId, trustNewIdentity); @@ -254,7 +254,7 @@ public class SignalAccount implements Closeable { account.initStores(dataPath, identityKey, registrationId, trustNewIdentity); account.groupStore = new GroupStore(getGroupCachePath(dataPath, username), - account.recipientStore::resolveRecipient, + account.recipientStore, account::saveGroupStore); account.stickerStore = new StickerStore(account::saveStickerStore); @@ -453,12 +453,10 @@ public class SignalAccount implements Closeable { groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStore = GroupStore.fromStorage(groupStoreStorage, getGroupCachePath(dataPath, username), - recipientStore::resolveRecipient, + recipientStore, this::saveGroupStore); } else { - groupStore = new GroupStore(getGroupCachePath(dataPath, username), - recipientStore::resolveRecipient, - this::saveGroupStore); + groupStore = new GroupStore(getGroupCachePath(dataPath, username), recipientStore, this::saveGroupStore); } if (rootNode.hasNonNull("stickerStore")) { @@ -572,7 +570,7 @@ public class SignalAccount implements Closeable { var profileStoreNode = rootNode.get("profileStore"); final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); for (var profileEntry : legacyProfileStore.getProfileEntries()) { - var recipientId = recipientStore.resolveRecipient(profileEntry.getServiceAddress()); + var recipientId = recipientStore.resolveRecipient(profileEntry.getAddress()); recipientStore.storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); recipientStore.storeProfileKey(recipientId, profileEntry.getProfileKey()); final var profile = profileEntry.getProfile(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java b/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java index e542f4e3..e4b639a2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java @@ -9,7 +9,11 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.whispersystems.signalservice.api.util.UuidUtil; + import java.io.InvalidObjectException; +import java.util.Optional; public class Utils { @@ -37,4 +41,12 @@ public class Utils { return node; } + + public static RecipientAddress getRecipientAddressFromIdentifier(final String identifier) { + if (UuidUtil.isUuid(identifier)) { + return new RecipientAddress(UuidUtil.parseOrThrow(identifier)); + } else { + return new RecipientAddress(Optional.empty(), Optional.of(identifier)); + } + } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/contacts/LegacyContactInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/contacts/LegacyContactInfo.java index a90f87e0..6c6bd7ea 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/contacts/LegacyContactInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/contacts/LegacyContactInfo.java @@ -3,7 +3,7 @@ package org.asamk.signal.manager.storage.contacts; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import java.util.UUID; @@ -42,7 +42,7 @@ public class LegacyContactInfo { } @JsonIgnore - public SignalServiceAddress getAddress() { - return new SignalServiceAddress(uuid, number); + public RecipientAddress getAddress() { + return new RecipientAddress(uuid, number); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java index 59cfedb5..f86dcb04 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java @@ -9,7 +9,6 @@ import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.EnabledState; import org.signal.zkgroup.groups.GroupMasterKey; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import java.util.Set; @@ -89,7 +88,7 @@ public class GroupInfoV2 extends GroupInfo { } return group.getMembersList() .stream() - .map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) + .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } @@ -101,7 +100,7 @@ public class GroupInfoV2 extends GroupInfo { } return group.getPendingMembersList() .stream() - .map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) + .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } @@ -113,7 +112,7 @@ public class GroupInfoV2 extends GroupInfo { } return group.getRequestingMembersList() .stream() - .map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) + .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } @@ -126,7 +125,7 @@ public class GroupInfoV2 extends GroupInfo { return group.getMembersList() .stream() .filter(m -> m.getRole() == Member.Role.ADMINISTRATOR) - .map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null)) + .map(m -> UuidUtil.parseOrThrow(m.getUuid().toByteArray())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java index 86459c2a..4adc413a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java @@ -14,6 +14,7 @@ import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; @@ -22,7 +23,6 @@ import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.util.Hex; @@ -78,7 +78,7 @@ public class GroupStore { final var g1 = (Storage.GroupV1) g; final var members = g1.members.stream().map(m -> { if (m.recipientId == null) { - return recipientResolver.resolveRecipient(new SignalServiceAddress(UuidUtil.parseOrNull(m.uuid), + return recipientResolver.resolveRecipient(new RecipientAddress(UuidUtil.parseOrNull(m.uuid), m.number)); } @@ -343,17 +343,17 @@ public class GroupStore { } } - private static final class JsonSignalServiceAddress { + private static final class JsonRecipientAddress { public String uuid; public String number; // For deserialization - public JsonSignalServiceAddress() { + public JsonRecipientAddress() { } - JsonSignalServiceAddress(final String uuid, final String number) { + JsonRecipientAddress(final String uuid, final String number) { this.uuid = uuid; this.number = number; } @@ -370,7 +370,7 @@ public class GroupStore { if (address.recipientId != null) { jgen.writeNumber(address.recipientId); } else if (address.uuid != null) { - jgen.writeObject(new JsonSignalServiceAddress(address.uuid, address.number)); + jgen.writeObject(new JsonRecipientAddress(address.uuid, address.number)); } else { jgen.writeString(address.number); } @@ -393,7 +393,7 @@ public class GroupStore { } else if (n.isNumber()) { addresses.add(new Member(n.numberValue().longValue(), null, null)); } else { - var address = jsonParser.getCodec().treeToValue(n, JsonSignalServiceAddress.class); + var address = jsonParser.getCodec().treeToValue(n, JsonRecipientAddress.class); addresses.add(new Member(null, address.uuid, address.number)); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java index 0cbdf347..f24e77b1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java @@ -6,7 +6,6 @@ import org.asamk.signal.manager.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; -import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; @@ -177,7 +176,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden * @param identifier can be either a serialized uuid or a e164 phone number */ private RecipientId resolveRecipient(String identifier) { - return resolver.resolveRecipient(Utils.getSignalServiceAddressFromIdentifier(identifier)); + return resolver.resolveRecipient(identifier); } private File getIdentityFile(final RecipientId recipientId) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java index 8e1d5c88..1c6369f0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java @@ -8,10 +8,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; @@ -45,7 +45,7 @@ public class LegacyProfileStore { for (var entry : node) { var name = entry.hasNonNull("name") ? entry.get("name").asText() : null; var uuid = entry.hasNonNull("uuid") ? UuidUtil.parseOrNull(entry.get("uuid").asText()) : null; - final var serviceAddress = new SignalServiceAddress(uuid, name); + final var address = new RecipientAddress(uuid, name); ProfileKey profileKey = null; try { profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText())); @@ -61,7 +61,7 @@ public class LegacyProfileStore { } var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class); - profileEntries.add(new LegacySignalProfileEntry(serviceAddress, + profileEntries.add(new LegacySignalProfileEntry(address, profileKey, lastUpdateTimestamp, profile, diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java index 1e2f7ec8..03b11bcb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java @@ -1,12 +1,12 @@ package org.asamk.signal.manager.storage.profiles; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; public class LegacySignalProfileEntry { - private final SignalServiceAddress serviceAddress; + private final RecipientAddress address; private final ProfileKey profileKey; @@ -17,21 +17,21 @@ public class LegacySignalProfileEntry { private final ProfileKeyCredential profileKeyCredential; public LegacySignalProfileEntry( - final SignalServiceAddress serviceAddress, + final RecipientAddress address, final ProfileKey profileKey, final long lastUpdateTimestamp, final SignalProfile profile, final ProfileKeyCredential profileKeyCredential ) { - this.serviceAddress = serviceAddress; + this.address = address; this.profileKey = profileKey; this.lastUpdateTimestamp = lastUpdateTimestamp; this.profile = profile; this.profileKeyCredential = profileKeyCredential; } - public SignalServiceAddress getServiceAddress() { - return serviceAddress; + public RecipientAddress getAddress() { + return address; } public ProfileKey getProfileKey() { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java index eb66b3e5..2fa19f42 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java @@ -1,30 +1,30 @@ package org.asamk.signal.manager.storage.protocol; import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Date; public class LegacyIdentityInfo { - SignalServiceAddress address; + RecipientAddress address; IdentityKey identityKey; TrustLevel trustLevel; Date added; - LegacyIdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) { + LegacyIdentityInfo(RecipientAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) { this.address = address; this.identityKey = identityKey; this.trustLevel = trustLevel; this.added = added; } - public SignalServiceAddress getAddress() { + public RecipientAddress getAddress() { return address; } - public void setAddress(final SignalServiceAddress address) { + public void setAddress(final RecipientAddress address) { this.address = address; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java index 781b6f96..5e11ab7a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java @@ -6,13 +6,13 @@ import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import org.asamk.signal.manager.TrustLevel; -import org.asamk.signal.manager.util.Utils; +import org.asamk.signal.manager.storage.Utils; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; 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.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; @@ -55,11 +55,11 @@ public class LegacyJsonIdentityKeyStore { return localRegistrationId; } - private LegacyIdentityInfo getIdentity(SignalServiceAddress serviceAddress) { + private LegacyIdentityInfo getIdentity(RecipientAddress address) { long maxDate = 0; LegacyIdentityInfo maxIdentity = null; for (var id : this.identities) { - if (!id.address.matches(serviceAddress)) { + if (!id.getAddress().matches(address)) { continue; } @@ -98,16 +98,16 @@ public class LegacyJsonIdentityKeyStore { var uuid = trustedKey.hasNonNull("uuid") ? UuidUtil.parseOrNull(trustedKey.get("uuid").asText()) : null; - final var serviceAddress = uuid == null - ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) - : new SignalServiceAddress(uuid, trustedKeyName); + final var address = uuid == null + ? Utils.getRecipientAddressFromIdentifier(trustedKeyName) + : new RecipientAddress(uuid, trustedKeyName); try { var id = new IdentityKey(Base64.getDecoder().decode(trustedKey.get("identityKey").asText()), 0); var trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get( "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED; var added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp") .asLong()) : new Date(); - identities.add(new LegacyIdentityInfo(serviceAddress, id, trustLevel, added)); + identities.add(new LegacyIdentityInfo(address, id, trustLevel, added)); } catch (InvalidKeyException e) { logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java index 5f301aeb..9ee0cf87 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java @@ -5,8 +5,8 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; -import org.asamk.signal.manager.util.Utils; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.asamk.signal.manager.storage.Utils; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; @@ -45,12 +45,12 @@ public class LegacyJsonSessionStore { } var uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null; - final var serviceAddress = uuid == null - ? Utils.getSignalServiceAddressFromIdentifier(sessionName) - : new SignalServiceAddress(uuid, sessionName); + final var address = uuid == null + ? Utils.getRecipientAddressFromIdentifier(sessionName) + : new RecipientAddress(uuid, sessionName); final var deviceId = session.get("deviceId").asInt(); final var record = Base64.getDecoder().decode(session.get("record").asText()); - var sessionInfo = new LegacySessionInfo(serviceAddress, deviceId, record); + var sessionInfo = new LegacySessionInfo(address, deviceId, record); sessions.add(sessionInfo); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacySessionInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacySessionInfo.java index a19bbd86..2cb984fb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacySessionInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacySessionInfo.java @@ -1,16 +1,16 @@ package org.asamk.signal.manager.storage.protocol; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; public class LegacySessionInfo { - public SignalServiceAddress address; + public RecipientAddress address; public int deviceId; public byte[] sessionRecord; - LegacySessionInfo(final SignalServiceAddress address, final int deviceId, final byte[] sessionRecord) { + LegacySessionInfo(final RecipientAddress address, final int deviceId, final byte[] sessionRecord) { this.address = address; this.deviceId = deviceId; this.sessionRecord = sessionRecord; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java index 84923423..77eb764a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java @@ -129,6 +129,11 @@ public class SignalProtocolStore implements SignalServiceDataStore { sessionStore.archiveSession(address); } + @Override + public Set getAllAddressesWithActiveSessions(final List addressNames) { + return sessionStore.getAllAddressesWithActiveSessions(addressNames); + } + @Override public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); @@ -189,4 +194,11 @@ public class SignalProtocolStore implements SignalServiceDataStore { public boolean isMultiDevice() { return isMultiDevice.get(); } + + @Override + public Transaction beginTransaction() { + return () -> { + // No-op transaction should be safe, as it's only a performance improvement + }; + } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java index 49317157..2aeb349f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; @@ -18,28 +17,27 @@ public class LegacyRecipientStore { @JsonProperty("recipientStore") @JsonDeserialize(using = RecipientStoreDeserializer.class) - private final List addresses = new ArrayList<>(); + private final List addresses = new ArrayList<>(); - public List getAddresses() { + public List getAddresses() { return addresses; } - public static class RecipientStoreDeserializer extends JsonDeserializer> { + public static class RecipientStoreDeserializer extends JsonDeserializer> { @Override - public List deserialize( + public List deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - var addresses = new ArrayList(); + var addresses = new ArrayList(); if (node.isArray()) { for (var recipient : node) { var recipientName = recipient.get("name").asText(); var uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText()); - final var serviceAddress = new SignalServiceAddress(uuid, recipientName); - addresses.add(serviceAddress); + addresses.add(new RecipientAddress(uuid, recipientName)); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java index 3ccf8210..2d2950dc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java @@ -2,13 +2,12 @@ package org.asamk.signal.manager.storage.recipients; import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; public class Recipient { private final RecipientId recipientId; - private final SignalServiceAddress address; + private final RecipientAddress address; private final Contact contact; @@ -20,7 +19,7 @@ public class Recipient { public Recipient( final RecipientId recipientId, - final SignalServiceAddress address, + final RecipientAddress address, final Contact contact, final ProfileKey profileKey, final ProfileKeyCredential profileKeyCredential, @@ -62,7 +61,7 @@ public class Recipient { return recipientId; } - public SignalServiceAddress getAddress() { + public RecipientAddress getAddress() { return address; } @@ -85,7 +84,7 @@ public class Recipient { public static final class Builder { private RecipientId recipientId; - private SignalServiceAddress address; + private RecipientAddress address; private Contact contact; private ProfileKey profileKey; private ProfileKeyCredential profileKeyCredential; @@ -99,7 +98,7 @@ public class Recipient { return this; } - public Builder withAddress(final SignalServiceAddress val) { + public Builder withAddress(final RecipientAddress val) { address = val; return this; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java new file mode 100644 index 00000000..29e964b0 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java @@ -0,0 +1,89 @@ +package org.asamk.signal.manager.storage.recipients; + +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.UuidUtil; + +import java.util.Optional; +import java.util.UUID; + +public class RecipientAddress { + + private final Optional uuid; + private final Optional e164; + + /** + * Construct a RecipientAddress. + * + * @param uuid The UUID of the user, if available. + * @param e164 The phone number of the user, if available. + */ + public RecipientAddress(Optional uuid, Optional e164) { + if (!uuid.isPresent() && !e164.isPresent()) { + throw new AssertionError("Must have either a UUID or E164 number!"); + } + + this.uuid = uuid; + this.e164 = e164; + } + + public RecipientAddress(UUID uuid, String e164) { + this(Optional.ofNullable(uuid), Optional.ofNullable(e164)); + } + + public RecipientAddress(SignalServiceAddress address) { + this.uuid = Optional.of(address.getUuid()); + this.e164 = Optional.ofNullable(address.getNumber().orNull()); + } + + public RecipientAddress(UUID uuid) { + this.uuid = Optional.of(uuid); + this.e164 = Optional.empty(); + } + + public Optional getNumber() { + return e164; + } + + public Optional getUuid() { + return uuid; + } + + public String getIdentifier() { + if (uuid.isPresent()) { + return uuid.get().toString(); + } else if (e164.isPresent()) { + return e164.get(); + } else { + throw new AssertionError("Given the checks in the constructor, this should not be possible."); + } + } + + public boolean matches(RecipientAddress other) { + return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || ( + e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get()) + ); + } + + public SignalServiceAddress toSignalServiceAddress() { + return new SignalServiceAddress(uuid.orElse(UuidUtil.UNKNOWN_UUID), + org.whispersystems.libsignal.util.guava.Optional.fromNullable(e164.orElse(null))); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final RecipientAddress that = (RecipientAddress) o; + + if (!uuid.equals(that.uuid)) return false; + return e164.equals(that.e164); + } + + @Override + public int hashCode() { + int result = uuid.hashCode(); + result = 31 * result + e164.hashCode(); + return result; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java index c6d06d23..a76e5b50 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java @@ -2,7 +2,15 @@ package org.asamk.signal.manager.storage.recipients; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import java.util.UUID; + public interface RecipientResolver { + RecipientId resolveRecipient(String identifier); + + RecipientId resolveRecipient(RecipientAddress address); + RecipientId resolveRecipient(SignalServiceAddress address); + + RecipientId resolveRecipient(UUID uuid); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java index c8a11340..86164d58 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.ByteArrayInputStream; @@ -30,9 +31,10 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.function.Supplier; import java.util.stream.Collectors; -public class RecipientStore implements ContactsStore, ProfileStore { +public class RecipientStore implements RecipientResolver, ContactsStore, ProfileStore { private final static Logger logger = LoggerFactory.getLogger(RecipientStore.class); @@ -51,9 +53,8 @@ public class RecipientStore implements ContactsStore, ProfileStore { final var storage = objectMapper.readValue(inputStream, Storage.class); final var recipients = storage.recipients.stream().map(r -> { final var recipientId = new RecipientId(r.id); - final var address = new SignalServiceAddress(org.whispersystems.libsignal.util.guava.Optional.fromNullable( - r.uuid).transform(UuidUtil::parseOrThrow), - org.whispersystems.libsignal.util.guava.Optional.fromNullable(r.number)); + final var address = new RecipientAddress(Optional.ofNullable(r.uuid).map(UuidUtil::parseOrThrow), + Optional.ofNullable(r.number)); Contact contact = null; if (r.contact != null) { @@ -119,7 +120,7 @@ public class RecipientStore implements ContactsStore, ProfileStore { this.lastId = lastId; } - public SignalServiceAddress resolveServiceAddress(RecipientId recipientId) { + public RecipientAddress resolveRecipientAddress(RecipientId recipientId) { synchronized (recipients) { return getRecipient(recipientId).getAddress(); } @@ -134,24 +135,52 @@ public class RecipientStore implements ContactsStore, ProfileStore { } } - @Deprecated - public SignalServiceAddress resolveServiceAddress(SignalServiceAddress address) { - return resolveServiceAddress(resolveRecipient(address, false)); + @Override + public RecipientId resolveRecipient(UUID uuid) { + return resolveRecipient(new RecipientAddress(uuid), false); } - public RecipientId resolveRecipient(UUID uuid) { - return resolveRecipient(new SignalServiceAddress(uuid, null), false); + @Override + public RecipientId resolveRecipient(final String identifier) { + return resolveRecipient(Utils.getRecipientAddressFromIdentifier(identifier), false); } - public RecipientId resolveRecipient(String number) { - return resolveRecipient(new SignalServiceAddress(null, number), false); + public RecipientId resolveRecipient( + final String number, Supplier uuidSupplier + ) throws UnregisteredUserException { + final Optional byNumber; + synchronized (recipients) { + byNumber = findByNumberLocked(number); + } + if (byNumber.isEmpty() || byNumber.get().getAddress().getUuid().isEmpty()) { + final var uuid = uuidSupplier.get(); + if (uuid == null) { + throw new UnregisteredUserException(number, null); + } + + return resolveRecipient(new RecipientAddress(uuid, number), false); + } + return byNumber.get().getRecipientId(); } - public RecipientId resolveRecipientTrusted(SignalServiceAddress address) { + public RecipientId resolveRecipient(RecipientAddress address) { + return resolveRecipient(address, false); + } + + @Override + public RecipientId resolveRecipient(final SignalServiceAddress address) { + return resolveRecipient(new RecipientAddress(address), false); + } + + public RecipientId resolveRecipientTrusted(RecipientAddress address) { return resolveRecipient(address, true); } - public List resolveRecipientsTrusted(List addresses) { + public RecipientId resolveRecipientTrusted(SignalServiceAddress address) { + return resolveRecipient(new RecipientAddress(address), true); + } + + public List resolveRecipientsTrusted(List addresses) { final List recipientIds; final List> toBeMerged = new ArrayList<>(); synchronized (recipients) { @@ -169,10 +198,6 @@ public class RecipientStore implements ContactsStore, ProfileStore { return recipientIds; } - public RecipientId resolveRecipient(SignalServiceAddress address) { - return resolveRecipient(address, false); - } - @Override public void storeContact(final RecipientId recipientId, final Contact contact) { synchronized (recipients) { @@ -262,7 +287,7 @@ public class RecipientStore implements ContactsStore, ProfileStore { * @param isHighTrust true, if the number/uuid connection was obtained from a trusted source. * Has no effect, if the address contains only a number or a uuid. */ - private RecipientId resolveRecipient(SignalServiceAddress address, boolean isHighTrust) { + private RecipientId resolveRecipient(RecipientAddress address, boolean isHighTrust) { final Pair> pair; synchronized (recipients) { pair = resolveRecipientLocked(address, isHighTrust); @@ -278,30 +303,26 @@ public class RecipientStore implements ContactsStore, ProfileStore { } private Pair> resolveRecipientLocked( - SignalServiceAddress address, boolean isHighTrust + RecipientAddress address, boolean isHighTrust ) { - final var byNumber = !address.getNumber().isPresent() + final var byNumber = address.getNumber().isEmpty() ? Optional.empty() - : findByNameLocked(address.getNumber().get()); - final var byUuid = !address.getUuid().isPresent() + : findByNumberLocked(address.getNumber().get()); + final var byUuid = address.getUuid().isEmpty() || address.getUuid().get().equals(UuidUtil.UNKNOWN_UUID) ? Optional.empty() : findByUuidLocked(address.getUuid().get()); if (byNumber.isEmpty() && byUuid.isEmpty()) { logger.debug("Got new recipient, both uuid and number are unknown"); - if (isHighTrust || !address.getUuid().isPresent() || !address.getNumber().isPresent()) { + if (isHighTrust || address.getUuid().isEmpty() || address.getNumber().isEmpty()) { return new Pair<>(addNewRecipientLocked(address), Optional.empty()); } - return new Pair<>(addNewRecipientLocked(new SignalServiceAddress(address.getUuid().get(), null)), - Optional.empty()); + return new Pair<>(addNewRecipientLocked(new RecipientAddress(address.getUuid().get())), Optional.empty()); } - if (!isHighTrust - || !address.getUuid().isPresent() - || !address.getNumber().isPresent() - || byNumber.equals(byUuid)) { + if (!isHighTrust || address.getUuid().isEmpty() || address.getNumber().isEmpty() || byNumber.equals(byUuid)) { return new Pair<>(byUuid.or(() -> byNumber).map(Recipient::getRecipientId).get(), Optional.empty()); } @@ -317,7 +338,7 @@ public class RecipientStore implements ContactsStore, ProfileStore { "Got recipient existing with number, but different uuid, so stripping its number and adding new recipient"); updateRecipientAddressLocked(byNumber.get().getRecipientId(), - new SignalServiceAddress(byNumber.get().getAddress().getUuid().get(), null)); + new RecipientAddress(byNumber.get().getAddress().getUuid().get())); return new Pair<>(addNewRecipientLocked(address), Optional.empty()); } @@ -331,7 +352,7 @@ public class RecipientStore implements ContactsStore, ProfileStore { "Got separate recipients for high trust number and uuid, recipient for number has different uuid, so stripping its number"); updateRecipientAddressLocked(byNumber.get().getRecipientId(), - new SignalServiceAddress(byNumber.get().getAddress().getUuid().get(), null)); + new RecipientAddress(byNumber.get().getAddress().getUuid().get())); updateRecipientAddressLocked(byUuid.get().getRecipientId(), address); return new Pair<>(byUuid.get().getRecipientId(), Optional.empty()); } @@ -342,14 +363,14 @@ public class RecipientStore implements ContactsStore, ProfileStore { return new Pair<>(byUuid.get().getRecipientId(), byNumber.map(Recipient::getRecipientId)); } - private RecipientId addNewRecipientLocked(final SignalServiceAddress serviceAddress) { + private RecipientId addNewRecipientLocked(final RecipientAddress address) { final var nextRecipientId = nextIdLocked(); - storeRecipientLocked(nextRecipientId, new Recipient(nextRecipientId, serviceAddress, null, null, null, null)); + storeRecipientLocked(nextRecipientId, new Recipient(nextRecipientId, address, null, null, null, null)); return nextRecipientId; } private void updateRecipientAddressLocked( - final RecipientId recipientId, final SignalServiceAddress address + final RecipientId recipientId, final RecipientAddress address ) { final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withAddress(address).build()); @@ -380,7 +401,7 @@ public class RecipientStore implements ContactsStore, ProfileStore { saveLocked(); } - private Optional findByNameLocked(final String number) { + private Optional findByNumberLocked(final String number) { return recipients.entrySet() .stream() .filter(entry -> entry.getValue().getAddress().getNumber().isPresent() && number.equals(entry.getValue() @@ -431,8 +452,8 @@ public class RecipientStore implements ContactsStore, ProfileStore { .map(Enum::name) .collect(Collectors.toSet())); return new Storage.Recipient(pair.getKey().getId(), - recipient.getAddress().getNumber().orNull(), - recipient.getAddress().getUuid().transform(UUID::toString).orNull(), + recipient.getAddress().getNumber().orElse(null), + recipient.getAddress().getUuid().map(UUID::toString).orElse(null), recipient.getProfileKey() == null ? null : base64.encodeToString(recipient.getProfileKey().serialize()), diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java index ef0a055b..5738408d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java @@ -3,7 +3,6 @@ package org.asamk.signal.manager.storage.sessions; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; -import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.NoSessionException; @@ -23,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -150,6 +150,19 @@ public class SessionStore implements SignalServiceSessionStore { } } + @Override + public Set getAllAddressesWithActiveSessions(final List addressNames) { + final var recipientIdToNameMap = addressNames.stream() + .collect(Collectors.toMap(this::resolveRecipient, name -> name)); + synchronized (cachedSessions) { + return recipientIdToNameMap.keySet() + .stream() + .flatMap(recipientId -> getKeysLocked(recipientId).stream()) + .map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId), key.getDeviceId())) + .collect(Collectors.toSet()); + } + } + public void archiveAllSessions() { synchronized (cachedSessions) { final var keys = getKeysLocked(); @@ -198,7 +211,7 @@ public class SessionStore implements SignalServiceSessionStore { * @param identifier can be either a serialized uuid or a e164 phone number */ private RecipientId resolveRecipient(String identifier) { - return resolver.resolveRecipient(Utils.getSignalServiceAddressFromIdentifier(identifier)); + return resolver.resolveRecipient(identifier); } private Key getKey(final SignalProtocolAddress address) { diff --git a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java index a2466311..3530d6ad 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -48,11 +48,11 @@ public class Utils { byte[] ownId; byte[] theirId; - if (isUuidCapable && ownAddress.getUuid().isPresent() && theirAddress.getUuid().isPresent()) { + if (isUuidCapable) { // Version 2: UUID user version = 2; - ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); - theirId = UuidUtil.toByteArray(theirAddress.getUuid().get()); + ownId = UuidUtil.toByteArray(ownAddress.getUuid()); + theirId = UuidUtil.toByteArray(theirAddress.getUuid()); } else { // Version 1: E164 user version = 1; @@ -69,12 +69,4 @@ public class Utils { theirId, theirIdentityKey); } - - public static SignalServiceAddress getSignalServiceAddressFromIdentifier(final String identifier) { - if (UuidUtil.isUuid(identifier)) { - return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null); - } else { - return new SignalServiceAddress(null, identifier); - } - } } diff --git a/run_tests.sh b/run_tests.sh index 4ee46845..5978eed9 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -136,8 +136,8 @@ run_main -u "$NUMBER_2" listGroups -d run_main -u "$NUMBER_2" --output=json listGroups -d run_main -u "$NUMBER_1" receive run_main -u "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" -run_main -u "$NUMBER_1" block "$GROUP_ID" -run_main -u "$NUMBER_1" unblock "$GROUP_ID" +run_main -u "$NUMBER_1" --verbose block -g "$GROUP_ID" +run_main -u "$NUMBER_1" --verbose unblock -g "$GROUP_ID" ## Identities run_main -u "$NUMBER_1" listIdentities diff --git a/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java index 5ed6af00..9433d209 100644 --- a/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java @@ -45,7 +45,7 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler { e.printStackTrace(); } } else if (content != null) { - final var sender = !envelope.isUnidentifiedSender() && envelope.hasSource() + final var sender = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender(); if (content.getReceiptMessage().isPresent()) { diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 0d8f312f..96603b76 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -1,12 +1,12 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.Util; -import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.slf4j.helpers.MessageFormatter; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceContent; @@ -38,12 +38,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { @Override public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { - if (envelope.hasSource()) { + if (envelope.hasSourceUuid()) { var source = envelope.getSourceAddress(); writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice()); - if (source.getRelay().isPresent()) { - writer.println("Relayed by: {}", source.getRelay().get()); - } } else { writer.println("Envelope from: unknown source"); } @@ -56,8 +53,8 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("Got receipt."); } else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) { if (exception != null) { - if (exception instanceof ProtocolUntrustedIdentityException) { - var e = (ProtocolUntrustedIdentityException) exception; + if (exception instanceof UntrustedIdentityException) { + var e = (UntrustedIdentityException) exception; writer.println( "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender())); @@ -630,7 +627,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { private void printMention( PlainTextWriter writer, SignalServiceDataMessage.Mention mention ) { - final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)); + final var address = m.resolveSignalServiceAddress(mention.getUuid()); writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); } diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 8d651592..8c1b9fb2 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -74,9 +74,14 @@ public class JoinGroupCommand implements JsonRpcLocalCommand { } catch (GroupPatchNotAcceptedException e) { throw new UserErrorException("Failed to join group, maybe already a member"); } catch (IOException e) { - throw new IOErrorException("Failed to send message: " + e.getMessage()); + throw new IOErrorException("Failed to send message: " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")"); } catch (DBusExecutionException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } catch (GroupLinkNotActiveException e) { throw new UserErrorException("Group link is not valid: " + e.getMessage()); } diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index b39f9ec9..5e609a48 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -8,7 +8,6 @@ import org.asamk.signal.OutputWriter; import org.asamk.signal.PlainTextWriter; import org.asamk.signal.manager.Manager; -import java.util.UUID; import java.util.stream.Collectors; import static org.asamk.signal.util.Util.getLegacyIdentifier; @@ -47,7 +46,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand { final var address = m.resolveSignalServiceAddress(contactPair.first()); final var contact = contactPair.second(); return new JsonContact(address.getNumber().orNull(), - address.getUuid().transform(UUID::toString).orNull(), + address.getUuid().toString(), contact.getName(), contact.isBlocked(), contact.getMessageExpirationTime()); diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index a6a9a2f1..b53577be 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -16,7 +16,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; public class ListGroupsCommand implements JsonRpcLocalCommand { @@ -46,8 +45,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { private static Set resolveJsonMembers(Manager m, Set addresses) { return addresses.stream() .map(m::resolveSignalServiceAddress) - .map(address -> new JsonGroupMember(address.getNumber().orNull(), - address.getUuid().transform(UUID::toString).orNull())) + .map(address -> new JsonGroupMember(address.getNumber().orNull(), address.getUuid().toString())) .collect(Collectors.toSet()); } diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index c859996e..02cd1d9f 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -18,7 +18,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Base64; import java.util.List; -import java.util.UUID; import java.util.stream.Collectors; public class ListIdentitiesCommand implements JsonRpcLocalCommand { @@ -72,7 +71,7 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(address, id.getIdentityKey())); var scannableSafetyNumber = m.computeSafetyNumberForScanning(address, id.getIdentityKey()); return new JsonIdentity(address.getNumber().orNull(), - address.getUuid().transform(UUID::toString).orNull(), + address.getUuid().toString(), Hex.toString(id.getFingerprint()), safetyNumber, scannableSafetyNumber == null diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index 03bf232b..c64d19cc 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -66,7 +66,11 @@ public class QuitGroupCommand implements JsonRpcLocalCommand { m.deleteGroup(groupId); } } catch (IOException e) { - throw new IOErrorException("Failed to send message: " + e.getMessage()); + throw new IOErrorException("Failed to send message: " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")"); } catch (GroupNotFoundException e) { throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (LastGroupAdminException e) { diff --git a/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java b/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java index e482dd58..6e1e92f7 100644 --- a/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java @@ -64,7 +64,8 @@ public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException(e.getMessage()); } catch (IOException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } @@ -104,7 +105,8 @@ public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand { } catch (Signal.Error.GroupNotFound e) { throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (DBusExecutionException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index c29d0268..7ab445fc 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -85,7 +85,8 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand { m.sendEndSessionMessage(singleRecipients); return; } catch (IOException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } @@ -108,7 +109,8 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand { outputResult(outputWriter, results.getTimestamp()); ErrorUtils.handleSendMessageResults(results.getResults()); } catch (AttachmentInvalidException | IOException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException(e.getMessage()); } @@ -141,9 +143,11 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand { signal.sendEndSessionMessage(recipients); return; } catch (Signal.Error.UntrustedIdentity e) { - throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } catch (DBusExecutionException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } @@ -182,7 +186,8 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand { outputResult(outputWriter, timestamp); return; } catch (Signal.Error.UntrustedIdentity e) { - throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } catch (DBusExecutionException e) { throw new UnexpectedErrorException("Failed to send note to self message: " + e.getMessage()); } @@ -194,9 +199,11 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand { } catch (UnknownObject e) { throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); } catch (Signal.Error.UntrustedIdentity e) { - throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } catch (DBusExecutionException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index 98e5f5ec..11a16b2e 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -80,7 +80,8 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException(e.getMessage()); } catch (IOException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } @@ -127,7 +128,8 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand { } catch (Signal.Error.GroupNotFound e) { throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (DBusExecutionException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } diff --git a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java index 70e2f015..0d5772ec 100644 --- a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java @@ -7,8 +7,8 @@ import org.asamk.signal.OutputWriter; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.util.CommandUtil; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import java.io.IOException; @@ -51,7 +51,8 @@ public class SendReceiptCommand implements JsonRpcLocalCommand { throw new UserErrorException("Unknown receipt type: " + type); } } catch (IOException | UntrustedIdentityException e) { - throw new UserErrorException("Failed to send message: " + e.getMessage()); + throw new UserErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendTypingCommand.java b/src/main/java/org/asamk/signal/commands/SendTypingCommand.java index ace4da85..3a965e47 100644 --- a/src/main/java/org/asamk/signal/commands/SendTypingCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendTypingCommand.java @@ -8,13 +8,13 @@ import org.asamk.signal.OutputWriter; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.util.CommandUtil; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import java.io.IOException; import java.util.HashSet; @@ -59,7 +59,8 @@ public class SendTypingCommand implements JsonRpcLocalCommand { try { m.sendTypingMessage(action, recipientIdentifiers); } catch (IOException | UntrustedIdentityException e) { - throw new UserErrorException("Failed to send message: " + e.getMessage()); + throw new UserErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException("Failed to send to group: " + e.getMessage()); } diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index a8d556f3..6df70ac2 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -174,7 +174,8 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException(e.getMessage()); } catch (IOException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } @@ -210,7 +211,8 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand { } catch (Signal.Error.AttachmentInvalid e) { throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage()); } catch (DBusExecutionException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")"); } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 6a7cc764..0bb0c435 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -21,6 +21,7 @@ import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.messages.SendMessageResult; +import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.File; @@ -244,6 +245,8 @@ public class DbusSignalImpl implements Signal { m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name); } catch (NotMasterDeviceException e) { throw new Error.Failure("This command doesn't work on linked devices."); + } catch (UnregisteredUserException e) { + throw new Error.Failure("Contact is not registered."); } } diff --git a/src/main/java/org/asamk/signal/json/JsonMention.java b/src/main/java/org/asamk/signal/json/JsonMention.java index f0c66d00..b24768b7 100644 --- a/src/main/java/org/asamk/signal/json/JsonMention.java +++ b/src/main/java/org/asamk/signal/json/JsonMention.java @@ -4,9 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.util.UUID; import static org.asamk.signal.util.Util.getLegacyIdentifier; @@ -29,10 +26,10 @@ public class JsonMention { final int length; JsonMention(SignalServiceDataMessage.Mention mention, Manager m) { - final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)); + final var address = m.resolveSignalServiceAddress(mention.getUuid()); this.name = getLegacyIdentifier(address); this.number = address.getNumber().orNull(); - this.uuid = address.getUuid().transform(UUID::toString).orNull(); + this.uuid = address.getUuid().toString(); this.start = mention.getStart(); this.length = mention.getLength(); } diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 814952aa..7b884b0e 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -5,14 +5,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.asamk.Signal; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.api.RecipientIdentifier; -import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.util.List; -import java.util.UUID; import static org.asamk.signal.util.Util.getLegacyIdentifier; @@ -34,10 +33,6 @@ public class JsonMessageEnvelope { @JsonProperty final Integer sourceDevice; - @JsonProperty - @JsonInclude(JsonInclude.Include.NON_NULL) - final String relay; - @JsonProperty final long timestamp; @@ -64,34 +59,30 @@ public class JsonMessageEnvelope { public JsonMessageEnvelope( SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception, Manager m ) { - if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { - var source = envelope.getSourceAddress(); + if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { + var source = m.resolveSignalServiceAddress(envelope.getSourceAddress()); this.source = getLegacyIdentifier(source); this.sourceNumber = source.getNumber().orNull(); - this.sourceUuid = source.getUuid().transform(UUID::toString).orNull(); + this.sourceUuid = source.getUuid().toString(); this.sourceDevice = envelope.getSourceDevice(); - this.relay = source.getRelay().orNull(); } else if (envelope.isUnidentifiedSender() && content != null) { - final var source = content.getSender(); + final var source = m.resolveSignalServiceAddress(content.getSender()); this.source = getLegacyIdentifier(source); this.sourceNumber = source.getNumber().orNull(); - this.sourceUuid = source.getUuid().transform(UUID::toString).orNull(); + this.sourceUuid = source.getUuid().toString(); this.sourceDevice = content.getSenderDevice(); - this.relay = null; - } else if (exception instanceof ProtocolUntrustedIdentityException) { - var e = (ProtocolUntrustedIdentityException) exception; + } else if (exception instanceof UntrustedIdentityException) { + var e = (UntrustedIdentityException) exception; final var source = m.resolveSignalServiceAddress(e.getSender()); this.source = getLegacyIdentifier(source); this.sourceNumber = source.getNumber().orNull(); - this.sourceUuid = source.getUuid().transform(UUID::toString).orNull(); + this.sourceUuid = source.getUuid().toString(); this.sourceDevice = e.getSenderDevice(); - this.relay = null; } else { this.source = null; this.sourceNumber = null; this.sourceUuid = null; this.sourceDevice = null; - this.relay = null; } String name; try { @@ -129,7 +120,6 @@ public class JsonMessageEnvelope { sourceUuid = null; sourceName = null; sourceDevice = null; - relay = null; timestamp = messageReceived.getTimestamp(); receiptMessage = null; dataMessage = new JsonDataMessage(messageReceived); @@ -144,7 +134,6 @@ public class JsonMessageEnvelope { sourceUuid = null; sourceName = null; sourceDevice = null; - relay = null; timestamp = receiptReceived.getTimestamp(); receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); dataMessage = null; @@ -159,7 +148,6 @@ public class JsonMessageEnvelope { sourceUuid = null; sourceName = null; sourceDevice = null; - relay = null; timestamp = messageReceived.getTimestamp(); receiptMessage = null; dataMessage = null; diff --git a/src/main/java/org/asamk/signal/json/JsonQuote.java b/src/main/java/org/asamk/signal/json/JsonQuote.java index ecd31c1a..73af895a 100644 --- a/src/main/java/org/asamk/signal/json/JsonQuote.java +++ b/src/main/java/org/asamk/signal/json/JsonQuote.java @@ -8,7 +8,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import java.util.ArrayList; import java.util.List; -import java.util.UUID; import java.util.stream.Collectors; import static org.asamk.signal.util.Util.getLegacyIdentifier; @@ -43,7 +42,7 @@ public class JsonQuote { final var address = m.resolveSignalServiceAddress(quote.getAuthor()); this.author = getLegacyIdentifier(address); this.authorNumber = address.getNumber().orNull(); - this.authorUuid = address.getUuid().transform(UUID::toString).orNull(); + this.authorUuid = address.getUuid().toString(); this.text = quote.getText(); if (quote.getMentions() != null && quote.getMentions().size() > 0) { diff --git a/src/main/java/org/asamk/signal/json/JsonReaction.java b/src/main/java/org/asamk/signal/json/JsonReaction.java index ecea15fe..cc80ee84 100644 --- a/src/main/java/org/asamk/signal/json/JsonReaction.java +++ b/src/main/java/org/asamk/signal/json/JsonReaction.java @@ -5,8 +5,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction; -import java.util.UUID; - import static org.asamk.signal.util.Util.getLegacyIdentifier; public class JsonReaction { @@ -35,7 +33,7 @@ public class JsonReaction { final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor()); this.targetAuthor = getLegacyIdentifier(address); this.targetAuthorNumber = address.getNumber().orNull(); - this.targetAuthorUuid = address.getUuid().transform(UUID::toString).orNull(); + this.targetAuthorUuid = address.getUuid().toString(); this.targetSentTimestamp = reaction.getTargetSentTimestamp(); this.isRemove = reaction.isRemove(); } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java index 28c9d936..e2c92bac 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java @@ -6,8 +6,6 @@ import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import java.util.UUID; - import static org.asamk.signal.util.Util.getLegacyIdentifier; class JsonSyncDataMessage extends JsonDataMessage { @@ -29,7 +27,7 @@ class JsonSyncDataMessage extends JsonDataMessage { final var address = transcriptMessage.getDestination().get(); this.destination = getLegacyIdentifier(address); this.destinationNumber = address.getNumber().orNull(); - this.destinationUuid = address.getUuid().transform(UUID::toString).orNull(); + this.destinationUuid = address.getUuid().toString(); } else { this.destination = null; this.destinationNumber = null; diff --git a/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java index df307b45..042ed7e4 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java @@ -4,8 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import java.util.UUID; - import static org.asamk.signal.util.Util.getLegacyIdentifier; class JsonSyncReadMessage { @@ -27,7 +25,7 @@ class JsonSyncReadMessage { final var sender = readMessage.getSender(); this.sender = getLegacyIdentifier(sender); this.senderNumber = sender.getNumber().orNull(); - this.senderUuid = sender.getUuid().transform(UUID::toString).orNull(); + this.senderUuid = sender.getUuid().toString(); this.timestamp = readMessage.getTimestamp(); } } diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 01a79dd1..b4954bf3 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -60,7 +60,7 @@ public class Util { } public static String getLegacyIdentifier(final SignalServiceAddress address) { - return address.getNumber().or(() -> address.getUuid().get().toString()); + return address.getNumber().or(() -> address.getUuid().toString()); } public static ObjectMapper createJsonObjectMapper() { -- 2.50.1