From 496cd5e6219b54e01d0a3b175cb77457001ce7dc Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 13:48:34 +0200 Subject: [PATCH 001/833] Fix repackage if building with multiple java versions --- .github/workflows/repackage-native-libs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/repackage-native-libs.yml b/.github/workflows/repackage-native-libs.yml index 9a87f723..fc5fa780 100644 --- a/.github/workflows/repackage-native-libs.yml +++ b/.github/workflows/repackage-native-libs.yml @@ -31,7 +31,7 @@ jobs: run: | #echo ${GITHUB_REF#refs/tag/} tree . - mv ./*/*.tar.gz . + mv ./$(ls */ -d | tail -n1)/*.tar.gz . ver=$(ls ./*.tar.gz | xargs basename | sed -E 's/signal-cli-(.*).tar.gz/\1/') echo $ver echo "::set-output name=signal_cli_version::${ver}" From 5f941004f5006b286690df2a93bc47ace3b59270 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 20 May 2022 11:46:03 +0200 Subject: [PATCH 002/833] Extend listContacts command with profiles and filtering --- graalvm-config-dir/reflect-config.json | 25 ++++++ graalvm-config-dir/resource-config.json | 3 + .../org/asamk/signal/manager/Manager.java | 10 ++- .../org/asamk/signal/manager/ManagerImpl.java | 53 +++++++------ .../signal/manager/helper/ProfileHelper.java | 16 +++- .../manager/helper/RecipientHelper.java | 4 +- .../signal/manager/helper/SendHelper.java | 2 +- .../storage/recipients/RecipientStore.java | 18 +++++ .../signal/commands/ListContactsCommand.java | 79 ++++++++++++++++--- .../asamk/signal/dbus/DbusManagerImpl.java | 31 ++++++-- .../org/asamk/signal/dbus/DbusSignalImpl.java | 38 ++------- 11 files changed, 196 insertions(+), 83 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index f9fe367f..69b19703 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -527,6 +527,20 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.commands.ListContactsCommand$JsonContact$JsonProfile", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"about","parameterTypes":[] }, + {"name":"aboutEmoji","parameterTypes":[] }, + {"name":"familyName","parameterTypes":[] }, + {"name":"givenName","parameterTypes":[] }, + {"name":"lastUpdateTimestamp","parameterTypes":[] }, + {"name":"paymentAddress","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.commands.ListDevicesCommand$JsonDevice", "allDeclaredFields":true, @@ -1797,6 +1811,17 @@ {"name":"userId_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.GroupChanges", + "fields":[{"name":"groupChanges_"}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChanges$GroupChangeState", + "fields":[ + {"name":"groupChange_"}, + {"name":"groupState_"} + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupInviteLink", "fields":[ diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index 08a9dcc5..542c8f31 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -73,6 +73,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DE\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DK\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_EC\\E" }, 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 8f49126f..cc3ce0ce 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -28,9 +28,8 @@ import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; -import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.Closeable; @@ -215,7 +214,12 @@ public interface Manager extends Closeable { void sendContacts() throws IOException; - List> getContacts(); + List getRecipients( + boolean onlyContacts, + Optional blocked, + Collection address, + Optional name + ); String getContactOrProfileName(RecipientIdentifier.Single recipient); diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index b3e6a0ae..f2d080fd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -51,9 +51,8 @@ import org.asamk.signal.manager.helper.Context; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.identities.IdentityInfo; -import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; -import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; @@ -84,6 +83,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -101,7 +101,6 @@ class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); private SignalAccount account; - private AccountFileUpdater accountFileUpdater; private final SignalDependencies dependencies; private final Context context; @@ -123,7 +122,6 @@ class ManagerImpl implements Manager { String userAgent ) { this.account = account; - this.accountFileUpdater = accountFileUpdater; final var sessionLock = new SignalSessionLock() { private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); @@ -337,7 +335,7 @@ class ManagerImpl implements Manager { } @Override - public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { return context.getProfileHelper().getRecipientProfile(context.getRecipientHelper().resolveRecipient(recipient)); } @@ -495,7 +493,7 @@ class ManagerImpl implements Manager { @Override public SendMessageResults sendReadReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException { + ) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, @@ -507,7 +505,7 @@ class ManagerImpl implements Manager { @Override public SendMessageResults sendViewedReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException { + ) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, messageIds, @@ -520,7 +518,7 @@ class ManagerImpl implements Manager { final RecipientIdentifier.Single sender, final long timestamp, final SignalServiceReceiptMessage receiptMessage - ) throws IOException { + ) { try { final var result = context.getSendHelper() .sendReceiptMessage(receiptMessage, context.getRecipientHelper().resolveRecipient(sender)); @@ -592,7 +590,7 @@ class ManagerImpl implements Manager { } } - private ArrayList resolveMentions(final List mentionList) throws IOException, UnregisteredRecipientException { + private ArrayList resolveMentions(final List mentionList) throws UnregisteredRecipientException { final var mentions = new ArrayList(); for (final var m : mentionList) { final var recipientId = context.getRecipientHelper().resolveRecipient(m.recipient()); @@ -676,7 +674,7 @@ class ManagerImpl implements Manager { @Override public void setContactName( RecipientIdentifier.Single recipient, String name - ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { + ) throws NotMasterDeviceException, UnregisteredRecipientException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } @@ -932,7 +930,7 @@ class ManagerImpl implements Manager { final RecipientId recipientId; try { recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { return false; } return context.getContactHelper().isContactBlocked(recipientId); @@ -944,12 +942,22 @@ class ManagerImpl implements Manager { } @Override - public List> getContacts() { - return account.getContactStore() - .getContacts() - .stream() - .map(p -> new Pair<>(account.getRecipientStore().resolveRecipientAddress(p.first()), p.second())) - .toList(); + public List getRecipients( + boolean onlyContacts, + Optional blocked, + Collection recipients, + Optional name + ) { + final var recipientIds = recipients.stream().map(a -> { + try { + return context.getRecipientHelper().resolveRecipient(a); + } catch (UnregisteredRecipientException e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toSet()); + // refresh profiles of explicitly given recipients + context.getProfileHelper().refreshRecipientProfiles(recipientIds); + return account.getRecipientStore().getRecipients(onlyContacts, blocked, recipientIds, name); } @Override @@ -957,7 +965,7 @@ class ManagerImpl implements Manager { final RecipientId recipientId; try { recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { return null; } @@ -1007,7 +1015,7 @@ class ManagerImpl implements Manager { try { identity = account.getIdentityKeyStore() .getIdentity(context.getRecipientHelper().resolveRecipient(recipient)); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { identity = null; } return identity == null ? List.of() : List.of(toIdentity(identity)); @@ -1044,12 +1052,7 @@ class ManagerImpl implements Manager { private boolean trustIdentity( RecipientIdentifier.Single recipient, Function trustMethod ) throws UnregisteredRecipientException { - RecipientId recipientId; - try { - recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException e) { - return false; - } + final var recipientId = context.getRecipientHelper().resolveRecipient(recipient); final var updated = trustMethod.apply(recipientId); if (updated && this.isReceiving()) { context.getReceiveHelper().setNeedsToRetryFailedMessages(true); 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 4bc00317..8a64c8b7 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 @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.util.Base64; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Locale; @@ -94,10 +95,18 @@ public final class ProfileHelper { return getRecipientProfile(recipientId, false); } + public List getRecipientProfiles(Collection recipientIds) { + return getRecipientProfiles(recipientIds, false); + } + public void refreshRecipientProfile(RecipientId recipientId) { getRecipientProfile(recipientId, true); } + public void refreshRecipientProfiles(Collection recipientIds) { + getRecipientProfiles(recipientIds, true); + } + public List getRecipientProfileKeyCredential(List recipientIds) { try { account.getRecipientStore().setBulkUpdating(true); @@ -216,11 +225,12 @@ public final class ProfileHelper { return getRecipientProfile(account.getSelfRecipientId()); } - public List getRecipientProfile(List recipientIds) { + private List getRecipientProfiles(Collection recipientIds, boolean force) { + final var profileStore = account.getProfileStore(); try { account.getRecipientStore().setBulkUpdating(true); final var profileFetches = Flowable.fromIterable(recipientIds) - .filter(recipientId -> isProfileRefreshRequired(account.getProfileStore().getProfile(recipientId))) + .filter(recipientId -> force || isProfileRefreshRequired(profileStore.getProfile(recipientId))) .map(recipientId -> retrieveProfile(recipientId, SignalServiceProfile.RequestType.PROFILE).onErrorComplete()); Maybe.merge(profileFetches, 10).blockingSubscribe(); @@ -228,7 +238,7 @@ public final class ProfileHelper { account.getRecipientStore().setBulkUpdating(false); } - return recipientIds.stream().map(r -> account.getProfileStore().getProfile(r)).toList(); + return recipientIds.stream().map(profileStore::getProfile).toList(); } private Profile getRecipientProfile(RecipientId recipientId, boolean force) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index 15508cd4..c253d602 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -69,7 +69,7 @@ public class RecipientHelper { return account.getRecipientStore().resolveRecipient(address); } - public Set resolveRecipients(Collection recipients) throws IOException, UnregisteredRecipientException { + public Set resolveRecipients(Collection recipients) throws UnregisteredRecipientException { final var recipientIds = new HashSet(recipients.size()); for (var number : recipients) { final var recipientId = resolveRecipient(number); @@ -78,7 +78,7 @@ public class RecipientHelper { return recipientIds; } - public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { return account.getRecipientStore().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); } else { 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 a2e2379b..8b2a054e 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 @@ -475,7 +475,7 @@ public class SendHelper { final var senderKeyTargets = new HashSet(); final var recipientList = new ArrayList<>(recipientIds); - final var profiles = context.getProfileHelper().getRecipientProfile(recipientList).iterator(); + final var profiles = context.getProfileHelper().getRecipientProfiles(recipientList).iterator(); for (final var recipientId : recipientList) { final var profile = profiles.next(); if (profile == null || !profile.getCapabilities().contains(Profile.Capability.senderKey)) { 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 299a3980..850b6270 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 @@ -277,6 +277,24 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile .toList(); } + public List getRecipients( + boolean onlyContacts, Optional blocked, Set recipientIds, Optional name + ) { + return recipients.values() + .stream() + .filter(r -> !onlyContacts || r.getContact() != null) + .filter(r -> blocked.isEmpty() || ( + blocked.get() == ( + r.getContact() != null && r.getContact().isBlocked() + ) + )) + .filter(r -> recipientIds.isEmpty() || (recipientIds.contains(r.getRecipientId()))) + .filter(r -> name.isEmpty() + || (r.getContact() != null && name.get().equals(r.getContact().getName())) + || (r.getProfile() != null && name.get().equals(r.getProfile().getDisplayName()))) + .toList(); + } + @Override public void deleteContact(RecipientId recipientId) { synchronized (recipients) { diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index b83440ec..0bfc7ab9 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -1,13 +1,20 @@ package org.asamk.signal.commands; +import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.storage.recipients.Contact; +import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.output.JsonWriter; import org.asamk.signal.output.OutputWriter; import org.asamk.signal.output.PlainTextWriter; +import org.asamk.signal.util.CommandUtil; +import java.util.Base64; +import java.util.Optional; import java.util.UUID; public class ListContactsCommand implements JsonRpcLocalCommand { @@ -19,19 +26,39 @@ public class ListContactsCommand implements JsonRpcLocalCommand { @Override public void attachToSubparser(final Subparser subparser) { - subparser.help("Show a list of known contacts with names."); + subparser.help("Show a list of known contacts with names and profiles."); + subparser.addArgument("recipient").help("Specify one ore more phone numbers to show.").nargs("*"); + subparser.addArgument("-a", "--all-recipients") + .action(Arguments.storeTrue()) + .help("Include all known recipients, not only contacts."); + subparser.addArgument("--blocked") + .type(Boolean.class) + .help("Specify if only blocked or unblocked contacts should be shown (default: all contacts)"); + subparser.addArgument("--name").help("Find contacts with the given contact or profile name."); } @Override - public void handleCommand(final Namespace ns, final Manager m, final OutputWriter outputWriter) { - var contacts = m.getContacts(); + public void handleCommand( + final Namespace ns, final Manager m, final OutputWriter outputWriter + ) throws CommandException { + final var allRecipients = Boolean.TRUE.equals(ns.getBoolean("all-recipients")); + final var blocked = ns.getBoolean("blocked"); + final var recipientStrings = ns.getList("recipient"); + final var recipientIdentifiers = CommandUtil.getSingleRecipientIdentifiers(recipientStrings, m.getSelfNumber()); + final var name = ns.getString("name"); + final var recipients = m.getRecipients(!allRecipients, + Optional.ofNullable(blocked), + recipientIdentifiers, + Optional.ofNullable(name)); if (outputWriter instanceof PlainTextWriter writer) { - for (var c : contacts) { - final var contact = c.second(); - writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}", - c.first().getLegacyIdentifier(), + for (var r : recipients) { + final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact(); + final var profile = r.getProfile() == null ? Profile.newBuilder().build() : r.getProfile(); + writer.println("Number: {} Name: {} Profile name: {} Blocked: {} Message expiration: {}", + r.getAddress().getLegacyIdentifier(), contact.getName(), + profile.getDisplayName(), contact.isBlocked(), contact.getMessageExpirationTime() == 0 ? "disabled" @@ -39,19 +66,47 @@ public class ListContactsCommand implements JsonRpcLocalCommand { } } else { final var writer = (JsonWriter) outputWriter; - final var jsonContacts = contacts.stream().map(contactPair -> { - final var address = contactPair.first(); - final var contact = contactPair.second(); + final var jsonContacts = recipients.stream().map(r -> { + final var address = r.getAddress(); + final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact(); return new JsonContact(address.number().orElse(null), address.uuid().map(UUID::toString).orElse(null), contact.getName(), contact.isBlocked(), - contact.getMessageExpirationTime()); + contact.getMessageExpirationTime(), + r.getProfile() == null + ? null + : new JsonContact.JsonProfile(r.getProfile().getLastUpdateTimestamp(), + r.getProfile().getGivenName(), + r.getProfile().getFamilyName(), + r.getProfile().getAbout(), + r.getProfile().getAboutEmoji(), + r.getProfile().getPaymentAddress() == null + ? null + : Base64.getEncoder() + .encodeToString(r.getProfile().getPaymentAddress()))); }).toList(); writer.write(jsonContacts); } } - private record JsonContact(String number, String uuid, String name, boolean isBlocked, int messageExpirationTime) {} + private record JsonContact( + String number, + String uuid, + String name, + boolean isBlocked, + int messageExpirationTime, + JsonProfile profile + ) { + + private record JsonProfile( + long lastUpdateTimestamp, + String givenName, + String familyName, + String about, + String aboutEmoji, + String paymentAddress + ) {} + } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 5b33b039..11800be2 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -32,6 +32,7 @@ import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.freedesktop.dbus.DBusMap; import org.freedesktop.dbus.DBusPath; @@ -547,14 +548,32 @@ public class DbusManagerImpl implements Manager { } @Override - public List> getContacts() { - return signal.listNumbers().stream().map(n -> { - final var contactName = signal.getContactName(n); - if (contactName.length() == 0) { + public List getRecipients( + final boolean onlyContacts, + final Optional blocked, + final Collection addresses, + final Optional name + ) { + final var numbers = addresses.stream() + .filter(s -> s instanceof RecipientIdentifier.Number) + .map(s -> ((RecipientIdentifier.Number) s).number()) + .collect(Collectors.toSet()); + return signal.listNumbers().stream().filter(n -> addresses.isEmpty() || numbers.contains(n)).map(n -> { + final var contactBlocked = signal.isContactBlocked(n); + if (blocked.isPresent() && blocked.get() != contactBlocked) { return null; } - return new Pair<>(new RecipientAddress(null, n), - new Contact(contactName, null, 0, signal.isContactBlocked(n), false, false)); + final var contactName = signal.getContactName(n); + if (onlyContacts && contactName.length() == 0) { + return null; + } + if (name.isPresent() && !name.get().equals(contactName)) { + return null; + } + return Recipient.newBuilder() + .withAddress(new RecipientAddress(null, n)) + .withContact(new Contact(contactName, null, 0, contactBlocked, false, false)) + .build(); }).filter(Objects::nonNull).toList(); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index b4485e32..f4a9cda8 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -4,14 +4,12 @@ import org.asamk.Signal; import org.asamk.signal.BaseConfig; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.api.AttachmentInvalidException; -import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidNumberException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.NotMasterDeviceException; -import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResults; @@ -28,7 +26,6 @@ import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.util.SendMessageResultUtils; import org.freedesktop.dbus.DBusPath; @@ -55,7 +52,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; public class DbusSignalImpl implements Signal { @@ -719,9 +715,9 @@ public class DbusSignalImpl implements Signal { // all numbers the system knows @Override public List listNumbers() { - return Stream.concat(m.getIdentities().stream().map(Identity::recipient), - m.getContacts().stream().map(Pair::first)) - .map(a -> a.number().orElse(null)) + return m.getRecipients(false, Optional.empty(), Set.of(), Optional.empty()) + .stream() + .map(r -> r.getAddress().number().orElse(null)) .filter(Objects::nonNull) .distinct() .toList(); @@ -729,30 +725,10 @@ public class DbusSignalImpl implements Signal { @Override public List getContactNumber(final String name) { - // Contact names have precedence. - var numbers = new ArrayList(); - var contacts = m.getContacts(); - for (var c : contacts) { - if (name.equals(c.second().getName())) { - numbers.add(c.first().getLegacyIdentifier()); - } - } - // Try profiles if no contact name was found - for (var identity : m.getIdentities()) { - final var address = identity.recipient(); - var number = address.number().orElse(null); - if (number != null) { - Profile profile = null; - try { - profile = m.getRecipientProfile(RecipientIdentifier.Single.fromAddress(address)); - } catch (IOException | UnregisteredRecipientException ignored) { - } - if (profile != null && profile.getDisplayName().equals(name)) { - numbers.add(number); - } - } - } - return numbers; + return m.getRecipients(false, Optional.empty(), Set.of(), Optional.of(name)) + .stream() + .map(r -> r.getAddress().getLegacyIdentifier()) + .toList(); } @Override From 2ecddba37509c1328980a2d59d1e96ca2cabab53 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 20 May 2022 11:57:18 +0200 Subject: [PATCH 003/833] Update json-rpc client --- client/Cargo.lock | 220 +++++++++++++++++++++++++----------------- client/src/cli.rs | 22 ++++- client/src/jsonrpc.rs | 22 ++++- client/src/main.rs | 21 +++- 4 files changed, 189 insertions(+), 96 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index 77afe1dd..855d74bc 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.54" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "atty" @@ -63,16 +63,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.0" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", "lazy_static", - "os_str_bytes", "strsim", "termcolor", "textwrap", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.0" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72" +checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" dependencies = [ "heck", "proc-macro-error", @@ -91,6 +91,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -221,7 +230,7 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -260,9 +269,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", "hashbrown", @@ -279,9 +288,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jsonrpc-client-transports" @@ -407,63 +416,45 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", ] [[package]] @@ -477,13 +468,16 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.0.0" +name = "once_cell" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] +checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca" + +[[package]] +name = "os_str_bytes" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "parity-tokio-ipc" @@ -526,9 +520,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -577,18 +571,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -636,18 +630,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -671,9 +665,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "scopeguard" @@ -683,24 +677,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -709,9 +703,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ "itoa", "ryu", @@ -737,9 +731,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" @@ -765,41 +759,42 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "tokio" -version = "1.17.0" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" dependencies = [ "bytes", "libc", "memchr", "mio", "num_cpus", + "once_cell", "pin-project-lite", "socket2", "tokio-macros", @@ -830,9 +825,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -844,9 +839,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] @@ -861,10 +856,10 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "version_check" @@ -878,6 +873,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -908,3 +909,46 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/client/src/cli.rs b/client/src/cli.rs index a8cee165..01b59fed 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -49,6 +49,10 @@ pub enum CliCommands { #[clap(short = 'g', long)] group_id: Vec, }, + DeleteLocalAccountData { + #[clap(long = "ignore-registered")] + ignore_registered: Option, + }, GetUserStatus { recipient: Vec, }, @@ -61,11 +65,21 @@ pub enum CliCommands { name: String, }, ListAccounts, - ListContacts, + ListContacts { + recipient: Vec, + #[clap(short = 'a', long = "all-recipients")] + all_recipients: bool, + #[clap(long, parse(try_from_str))] + blocked: Option, + #[clap(long)] + name: Option, + }, ListDevices, ListGroups { #[clap(short = 'd', long)] detailed: bool, + #[clap(short = 'g', long = "group-id")] + group_id: Vec, }, ListIdentities { #[clap(short = 'n', long)] @@ -225,13 +239,13 @@ pub enum CliCommands { #[clap(long = "read-receipts", parse(try_from_str))] read_receipts: Option, - #[clap(long = "unidentified-delivery-indicators")] + #[clap(long = "unidentified-delivery-indicators", parse(try_from_str))] unidentified_delivery_indicators: Option, - #[clap(long = "typing-indicators")] + #[clap(long = "typing-indicators", parse(try_from_str))] typing_indicators: Option, - #[clap(long = "link-previews")] + #[clap(long = "link-previews", parse(try_from_str))] link_previews: Option, }, UpdateContact { diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index d4ed4084..f91be25e 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -20,6 +20,13 @@ pub trait Rpc { #[allow(non_snake_case)] groupIds: Vec, ) -> Result; + #[rpc(name = "deleteLocalAccountData", params = "named")] + fn delete_local_account_data( + &self, + account: Option, + #[allow(non_snake_case)] ignoreRegistered: Option, + ) -> Result; + #[rpc(name = "getUserStatus", params = "named")] fn get_user_status(&self, account: Option, recipients: Vec) -> Result; @@ -37,13 +44,24 @@ pub trait Rpc { fn list_accounts(&self) -> Result; #[rpc(name = "listContacts", params = "named")] - fn list_contacts(&self, account: Option) -> Result; + fn list_contacts( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] allRecipients: bool, + blocked: Option, + name: Option, + ) -> Result; #[rpc(name = "listDevices", params = "named")] fn list_devices(&self, account: Option) -> Result; #[rpc(name = "listGroups", params = "named")] - fn list_groups(&self, account: Option) -> Result; + fn list_groups( + &self, + account: Option, + #[allow(non_snake_case)] groupIds: Vec, + ) -> Result; #[rpc(name = "listIdentities", params = "named")] fn list_identities(&self, account: Option, number: Option) -> Result; diff --git a/client/src/main.rs b/client/src/main.rs index 6266d224..622be806 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -41,6 +41,11 @@ async fn main() -> Result<(), anyhow::Error> { recipient, group_id, } => client.block(cli.account, recipient, group_id).await, + cli::CliCommands::DeleteLocalAccountData { ignore_registered } => { + client + .delete_local_account_data(cli.account, ignore_registered) + .await + } cli::CliCommands::GetUserStatus { recipient } => { client.get_user_status(cli.account, recipient).await } @@ -55,9 +60,21 @@ async fn main() -> Result<(), anyhow::Error> { client.finish_link(url, name).await } cli::CliCommands::ListAccounts => client.list_accounts().await, - cli::CliCommands::ListContacts => client.list_contacts(cli.account).await, + cli::CliCommands::ListContacts { + recipient, + all_recipients, + blocked, + name, + } => { + client + .list_contacts(cli.account, recipient, all_recipients, blocked, name) + .await + } cli::CliCommands::ListDevices => client.list_devices(cli.account).await, - cli::CliCommands::ListGroups { detailed: _ } => client.list_groups(cli.account).await, + cli::CliCommands::ListGroups { + detailed: _, + group_id, + } => client.list_groups(cli.account, group_id).await, cli::CliCommands::ListIdentities { number } => { client.list_identities(cli.account, number).await } From b18991b9fb3a68b8a3f4bd16a160ead90b4990a9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 09:00:42 +0200 Subject: [PATCH 004/833] Update documentation --- graalvm-config-dir/resource-config.json | 9 +++++++++ man/signal-cli.1.adoc | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index 542c8f31..1de22f76 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -46,6 +46,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BM\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BO\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR\\E" }, @@ -67,6 +70,12 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CN\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CO\\E" + }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CR\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CZ\\E" }, diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 4fdd684a..7d1aa6f1 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -418,7 +418,20 @@ Filter the group list by one or more group IDs. === listContacts -Show a list of known contacts with names. +Show a list of known contacts with names and profiles. +When a specific recipient is given, its profile will be refreshed. + +RECIPIENT:: +Specify the recipients’ phone number. + +*-a*, *--all-recipients*:: +Include all known recipients, not only contacts. + +*--blocked*:: +Specify if only blocked or unblocked contacts should be shown (default: all contacts) + +*--name*:: +Find contacts with the given contact or profile name. === listIdentities From 7587a603872a337dd6be706854a0658ea131dbbe Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 20:54:15 +0200 Subject: [PATCH 005/833] Implement sendPayment notification command --- .../org/asamk/signal/manager/Manager.java | 4 ++ .../org/asamk/signal/manager/ManagerImpl.java | 14 +++++ man/signal-cli.1.adoc | 13 +++++ .../org/asamk/signal/commands/Commands.java | 1 + .../SendPaymentNotificationCommand.java | 51 +++++++++++++++++++ .../asamk/signal/dbus/DbusManagerImpl.java | 7 +++ 6 files changed, 90 insertions(+) create mode 100644 src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index cc3ce0ce..e568816f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -141,6 +141,10 @@ public interface Manager extends Closeable { Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException; + SendMessageResults sendPaymentNotificationMessage( + byte[] receipt, String note, RecipientIdentifier.Single recipient + ) throws IOException; + SendMessageResults sendEndSessionMessage(Set recipients) throws IOException; void deleteRecipient(RecipientIdentifier.Single recipient); diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index f2d080fd..ed7ae86b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -638,6 +638,20 @@ class ManagerImpl implements Manager { return sendMessage(messageBuilder, recipients); } + @Override + public SendMessageResults sendPaymentNotificationMessage( + byte[] receipt, String note, RecipientIdentifier.Single recipient + ) throws IOException { + final var paymentNotification = new SignalServiceDataMessage.PaymentNotification(receipt, note); + final var payment = new SignalServiceDataMessage.Payment(paymentNotification); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withPayment(payment); + try { + return sendMessage(messageBuilder, Set.of(recipient)); + } catch (NotAGroupMemberException | GroupNotFoundException | GroupSendingNotAllowedException e) { + throw new AssertionError(e); + } + } + @Override public SendMessageResults sendEndSessionMessage(Set recipients) throws IOException { var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 7d1aa6f1..1cebd044 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -256,6 +256,19 @@ Specify the mentions of the original message (same format as `--mention`). *-e*, *--end-session*:: Clear session state and send end session message. +=== sendPaymentNotification + +Send a payment notification. + +RECIPIENT:: +Specify the recipient’s phone number. + +*--receipt* RECEIPT:: +The base64 encoded receipt blob. + +*--note* NOTE:: +Specify a note for the payment notification. + === sendReaction Send reaction to a previously received or sent message. diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 15ab1724..1c4251da 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -34,6 +34,7 @@ public class Commands { addCommand(new RemoteDeleteCommand()); addCommand(new SendCommand()); addCommand(new SendContactsCommand()); + addCommand(new SendPaymentNotificationCommand()); addCommand(new SendReactionCommand()); addCommand(new SendReceiptCommand()); addCommand(new SendSyncRequestCommand()); diff --git a/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java b/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java new file mode 100644 index 00000000..77181e3e --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java @@ -0,0 +1,51 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.manager.Manager; +import org.asamk.signal.output.OutputWriter; +import org.asamk.signal.util.CommandUtil; + +import java.io.IOException; +import java.util.Base64; + +import static org.asamk.signal.util.SendMessageResultUtils.outputResult; + +public class SendPaymentNotificationCommand implements JsonRpcLocalCommand { + + @Override + public String getName() { + return "sendPaymentNotification"; + } + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.help("Send a payment notification."); + subparser.addArgument("recipient").help("Specify the recipient's phone number."); + subparser.addArgument("--receipt").required(true).help("The base64 encoded receipt blob."); + subparser.addArgument("--note").help("Specify a note for the payment notification."); + } + + @Override + public void handleCommand( + final Namespace ns, final Manager m, final OutputWriter outputWriter + ) throws CommandException { + final var recipientString = ns.getString("recipient"); + final var recipientIdentifier = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); + + final var receiptString = ns.getString("receipt"); + final var receipt = Base64.getDecoder().decode(receiptString); + final var note = ns.getString("note"); + + try { + final var results = m.sendPaymentNotificationMessage(receipt, note, recipientIdentifier); + outputResult(outputWriter, results); + } catch (IOException e) { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")", e); + } + } +} diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 11800be2..6bb93677 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -388,6 +388,13 @@ public class DbusManagerImpl implements Manager { groupId)); } + @Override + public SendMessageResults sendPaymentNotificationMessage( + final byte[] receipt, final String note, final RecipientIdentifier.Single recipient + ) throws IOException { + throw new UnsupportedOperationException(); + } + @Override public SendMessageResults sendEndSessionMessage(final Set recipients) throws IOException { signal.sendEndSessionMessage(recipients.stream().map(RecipientIdentifier.Single::getIdentifier).toList()); From 3666531f8bfe179bdaf5eaa20ffc2ed16e0fcf9b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 09:29:58 +0200 Subject: [PATCH 006/833] Refactor manager update profile method --- graalvm-config-dir/reflect-config.json | 8 ++ .../org/asamk/signal/manager/Manager.java | 12 +- .../org/asamk/signal/manager/ManagerImpl.java | 14 ++- .../manager/RegistrationManagerImpl.java | 3 +- .../signal/manager/api/UpdateProfile.java | 108 ++++++++++++++++++ .../signal/commands/UpdateProfileCommand.java | 15 ++- .../asamk/signal/dbus/DbusManagerImpl.java | 21 ++-- .../org/asamk/signal/dbus/DbusSignalImpl.java | 14 ++- 8 files changed, 160 insertions(+), 35 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 69b19703..f9deabc6 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2754,6 +2754,14 @@ {"name":"stickerId_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$StoryContext", + "fields":[ + {"name":"authorUuid_"}, + {"name":"bitField0_"}, + {"name":"sentTimestamp_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Envelope", "fields":[ 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 e568816f..4444e57a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -21,6 +21,7 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -67,15 +68,10 @@ public interface Manager extends Closeable { void updateConfiguration(Configuration configuration) throws IOException, NotMasterDeviceException; /** - * @param givenName if null, the previous givenName will be kept - * @param familyName if null, the previous familyName will be kept - * @param about if null, the previous about text will be kept - * @param aboutEmoji if null, the previous about emoji will be kept - * @param avatar if avatar is null the image from the local avatar store is used (if present), + * Update the user's profile. + * If a field is null, the previous value will be kept. */ - void setProfile( - String givenName, String familyName, String about, String aboutEmoji, Optional avatar - ) throws IOException; + void updateProfile(UpdateProfile updateProfile) throws IOException; void unregister() throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index ed7ae86b..3ca3c109 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -38,6 +38,7 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; @@ -261,10 +262,15 @@ class ManagerImpl implements Manager { } @Override - public void setProfile( - String givenName, final String familyName, String about, String aboutEmoji, Optional avatar - ) throws IOException { - context.getProfileHelper().setProfile(givenName, familyName, about, aboutEmoji, avatar); + public void updateProfile(UpdateProfile updateProfile) throws IOException { + context.getProfileHelper() + .setProfile(updateProfile.getGivenName(), + updateProfile.getFamilyName(), + updateProfile.getAbout(), + updateProfile.getAboutEmoji(), + updateProfile.isDeleteAvatar() + ? Optional.empty() + : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar())); context.getSyncHelper().sendSyncFetchProfileMessage(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 3529154a..509b8923 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -19,6 +19,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.PinLockedException; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.helper.AccountFileUpdater; @@ -139,7 +140,7 @@ class RegistrationManagerImpl implements RegistrationManager { } // Set an initial empty profile so user can be added to groups try { - m.setProfile(null, null, null, null, null); + m.updateProfile(UpdateProfile.newBuilder().build()); } catch (NoClassDefFoundError e) { logger.warn("Failed to set default profile: {}", e.getMessage()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java new file mode 100644 index 00000000..2b47f7d5 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java @@ -0,0 +1,108 @@ +package org.asamk.signal.manager.api; + +import java.io.File; + +public class UpdateProfile { + + private final String givenName; + private final String familyName; + private final String about; + private final String aboutEmoji; + private final File avatar; + private final boolean deleteAvatar; + + private UpdateProfile(final Builder builder) { + givenName = builder.givenName; + familyName = builder.familyName; + about = builder.about; + aboutEmoji = builder.aboutEmoji; + avatar = builder.avatar; + deleteAvatar = builder.deleteAvatar; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static Builder newBuilder(final UpdateProfile copy) { + Builder builder = new Builder(); + builder.givenName = copy.getGivenName(); + builder.familyName = copy.getFamilyName(); + builder.about = copy.getAbout(); + builder.aboutEmoji = copy.getAboutEmoji(); + builder.avatar = copy.getAvatar(); + builder.deleteAvatar = copy.isDeleteAvatar(); + return builder; + } + + public String getGivenName() { + return givenName; + } + + public String getFamilyName() { + return familyName; + } + + public String getAbout() { + return about; + } + + public String getAboutEmoji() { + return aboutEmoji; + } + + public File getAvatar() { + return avatar; + } + + public boolean isDeleteAvatar() { + return deleteAvatar; + } + + public static final class Builder { + + private String givenName; + private String familyName; + private String about; + private String aboutEmoji; + private File avatar; + private boolean deleteAvatar; + + private Builder() { + } + + public Builder withGivenName(final String val) { + givenName = val; + return this; + } + + public Builder withFamilyName(final String val) { + familyName = val; + return this; + } + + public Builder withAbout(final String val) { + about = val; + return this; + } + + public Builder withAboutEmoji(final String val) { + aboutEmoji = val; + return this; + } + + public Builder withAvatar(final File val) { + avatar = val; + return this; + } + + public Builder withDeleteAvatar(final boolean val) { + deleteAvatar = val; + return this; + } + + public UpdateProfile build() { + return new UpdateProfile(this); + } + } +} diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index e60edf23..1edb6df9 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -7,11 +7,11 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.output.OutputWriter; import java.io.File; import java.io.IOException; -import java.util.Optional; public class UpdateProfileCommand implements JsonRpcLocalCommand { @@ -44,12 +44,17 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { var avatarPath = ns.getString("avatar"); boolean removeAvatar = Boolean.TRUE.equals(ns.getBoolean("remove-avatar")); - Optional avatarFile = removeAvatar - ? Optional.empty() - : avatarPath == null ? null : Optional.of(new File(avatarPath)); + File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); try { - m.setProfile(givenName, familyName, about, aboutEmoji, avatarFile); + m.updateProfile(UpdateProfile.newBuilder() + .withGivenName(givenName) + .withFamilyName(familyName) + .withAbout(about) + .withAboutEmoji(aboutEmoji) + .withAvatar(avatarFile) + .withDeleteAvatar(removeAvatar) + .build()); } catch (IOException e) { throw new IOErrorException("Update profile error: " + e.getMessage(), e); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 6bb93677..320a8533 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -22,6 +22,7 @@ import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -138,19 +139,13 @@ public class DbusManagerImpl implements Manager { } @Override - public void setProfile( - final String givenName, - final String familyName, - final String about, - final String aboutEmoji, - final Optional avatar - ) throws IOException { - signal.updateProfile(emptyIfNull(givenName), - emptyIfNull(familyName), - emptyIfNull(about), - emptyIfNull(aboutEmoji), - avatar == null ? "" : avatar.map(File::getPath).orElse(""), - avatar != null && avatar.isEmpty()); + public void updateProfile(UpdateProfile updateProfile) throws IOException { + signal.updateProfile(emptyIfNull(updateProfile.getGivenName()), + emptyIfNull(updateProfile.getFamilyName()), + emptyIfNull(updateProfile.getAbout()), + emptyIfNull(updateProfile.getAboutEmoji()), + updateProfile.getAvatar() == null ? "" : updateProfile.getAvatar().getPath(), + updateProfile.isDeleteAvatar()); } @Override diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index f4a9cda8..ec7174b2 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -17,6 +17,7 @@ import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -662,10 +663,15 @@ public class DbusSignalImpl implements Signal { about = nullIfEmpty(about); aboutEmoji = nullIfEmpty(aboutEmoji); avatarPath = nullIfEmpty(avatarPath); - Optional avatarFile = removeAvatar - ? Optional.empty() - : avatarPath == null ? null : Optional.of(new File(avatarPath)); - m.setProfile(givenName, familyName, about, aboutEmoji, avatarFile); + File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); + m.updateProfile(UpdateProfile.newBuilder() + .withGivenName(givenName) + .withFamilyName(familyName) + .withAbout(about) + .withAboutEmoji(aboutEmoji) + .withAvatar(avatarFile) + .withDeleteAvatar(removeAvatar) + .build()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } From bf75d9b4e0385d8b6b86faef8860caa06368c447 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 10:08:40 +0200 Subject: [PATCH 007/833] Decrypt and verify the profile payment address --- graalvm-config-dir/reflect-config.json | 17 +++++- .../signal/manager/helper/ProfileHelper.java | 14 ++--- .../manager/storage/recipients/Profile.java | 20 +++---- .../storage/recipients/RecipientStore.java | 10 ++-- .../signal/manager/util/PaymentUtils.java | 52 +++++++++++++++++++ .../signal/manager/util/ProfileUtils.java | 39 +++++++++++++- .../signal/commands/ListContactsCommand.java | 13 ++--- 7 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index f9deabc6..6d5df0d0 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -538,7 +538,7 @@ {"name":"familyName","parameterTypes":[] }, {"name":"givenName","parameterTypes":[] }, {"name":"lastUpdateTimestamp","parameterTypes":[] }, - {"name":"paymentAddress","parameterTypes":[] } + {"name":"mobileCoinAddress","parameterTypes":[] } ] }, { @@ -2846,6 +2846,21 @@ {"name":"padding_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$PaymentAddress", + "fields":[ + {"name":"addressCase_"}, + {"name":"address_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$PaymentAddress$MobileCoinAddress", + "fields":[ + {"name":"address_"}, + {"name":"bitField0_"}, + {"name":"signature_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Preview", "fields":[ 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 8a64c8b7..1a876d7e 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 @@ -1,7 +1,5 @@ package org.asamk.signal.manager.helper; -import com.google.protobuf.InvalidProtocolBufferException; - import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -13,6 +11,7 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.KeyUtils; +import org.asamk.signal.manager.util.PaymentUtils; import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.protocol.IdentityKey; @@ -29,7 +28,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.services.ProfileService; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.io.File; import java.io.IOException; @@ -185,13 +183,9 @@ public final class ProfileHelper { final var avatarUploadParams = streamDetails != null ? AvatarUploadParams.forAvatar(streamDetails) : avatar == null ? AvatarUploadParams.unchanged(true) : AvatarUploadParams.unchanged(false); - final var paymentsAddress = Optional.ofNullable(newProfile.getPaymentAddress()).map(data -> { - try { - return SignalServiceProtos.PaymentAddress.parseFrom(data); - } catch (InvalidProtocolBufferException e) { - return null; - } - }); + final var paymentsAddress = Optional.ofNullable(newProfile.getMobileCoinAddress()) + .map(address -> PaymentUtils.signPaymentsAddress(address, + account.getAciIdentityKeyPair().getPrivateKey())); logger.debug("Uploading new profile"); final var avatarPath = dependencies.getAccountManager() .setVersionedProfile(account.getAci(), diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java index 3f14f645..909337b7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java @@ -20,7 +20,7 @@ public class Profile { private final String avatarUrlPath; - private final byte[] paymentAddress; + private final byte[] mobileCoinAddress; private final UnidentifiedAccessMode unidentifiedAccessMode; @@ -33,7 +33,7 @@ public class Profile { final String about, final String aboutEmoji, final String avatarUrlPath, - final byte[] paymentAddress, + final byte[] mobileCoinAddress, final UnidentifiedAccessMode unidentifiedAccessMode, final Set capabilities ) { @@ -43,7 +43,7 @@ public class Profile { this.about = about; this.aboutEmoji = aboutEmoji; this.avatarUrlPath = avatarUrlPath; - this.paymentAddress = paymentAddress; + this.mobileCoinAddress = mobileCoinAddress; this.unidentifiedAccessMode = unidentifiedAccessMode; this.capabilities = capabilities; } @@ -55,7 +55,7 @@ public class Profile { about = builder.about; aboutEmoji = builder.aboutEmoji; avatarUrlPath = builder.avatarUrlPath; - paymentAddress = builder.paymentAddress; + mobileCoinAddress = builder.mobileCoinAddress; unidentifiedAccessMode = builder.unidentifiedAccessMode; capabilities = builder.capabilities; } @@ -72,7 +72,7 @@ public class Profile { builder.about = copy.getAbout(); builder.aboutEmoji = copy.getAboutEmoji(); builder.avatarUrlPath = copy.getAvatarUrlPath(); - builder.paymentAddress = copy.getPaymentAddress(); + builder.mobileCoinAddress = copy.getMobileCoinAddress(); builder.unidentifiedAccessMode = copy.getUnidentifiedAccessMode(); builder.capabilities = copy.getCapabilities(); return builder; @@ -124,8 +124,8 @@ public class Profile { return avatarUrlPath; } - public byte[] getPaymentAddress() { - return paymentAddress; + public byte[] getMobileCoinAddress() { + return mobileCoinAddress; } public UnidentifiedAccessMode getUnidentifiedAccessMode() { @@ -200,7 +200,7 @@ public class Profile { private String about; private String aboutEmoji; private String avatarUrlPath; - private byte[] paymentAddress; + private byte[] mobileCoinAddress; private UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN; private Set capabilities = Collections.emptySet(); private long lastUpdateTimestamp = 0; @@ -252,8 +252,8 @@ public class Profile { return this; } - public Builder withPaymentAddress(final byte[] val) { - paymentAddress = val; + public Builder withMobileCoinAddress(final byte[] val) { + mobileCoinAddress = val; return this; } } 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 850b6270..0297f6d7 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 @@ -105,9 +105,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile r.profile.about, r.profile.aboutEmoji, r.profile.avatarUrlPath, - r.profile.paymentAddress == null + r.profile.mobileCoinAddress == null ? null - : Base64.getDecoder().decode(r.profile.paymentAddress), + : Base64.getDecoder().decode(r.profile.mobileCoinAddress), Profile.UnidentifiedAccessMode.valueOfOrUnknown(r.profile.unidentifiedAccessMode), r.profile.capabilities.stream() .map(Profile.Capability::valueOfOrNull) @@ -592,9 +592,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile recipientProfile.getAbout(), recipientProfile.getAboutEmoji(), recipientProfile.getAvatarUrlPath(), - recipientProfile.getPaymentAddress() == null + recipientProfile.getMobileCoinAddress() == null ? null - : base64.encodeToString(recipientProfile.getPaymentAddress()), + : base64.encodeToString(recipientProfile.getMobileCoinAddress()), recipientProfile.getUnidentifiedAccessMode().name(), recipientProfile.getCapabilities().stream().map(Enum::name).collect(Collectors.toSet())); return new Storage.Recipient(pair.getKey().id(), @@ -651,7 +651,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile String about, String aboutEmoji, String avatarUrlPath, - String paymentAddress, + String mobileCoinAddress, String unidentifiedAccessMode, Set capabilities ) {} diff --git a/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java new file mode 100644 index 00000000..6c34d9c9 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java @@ -0,0 +1,52 @@ +package org.asamk.signal.manager.util; + +import com.google.protobuf.ByteString; + +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.ecc.ECPrivateKey; +import org.signal.libsignal.protocol.ecc.ECPublicKey; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; + +public class PaymentUtils { + + private PaymentUtils() { + } + + /** + * Signs the supplied address bytes with the {@link IdentityKeyPair}'s private key and returns a proto that includes it, and it's signature. + */ + public static SignalServiceProtos.PaymentAddress signPaymentsAddress( + byte[] publicAddressBytes, ECPrivateKey privateKey + ) { + byte[] signature = privateKey.calculateSignature(publicAddressBytes); + + return SignalServiceProtos.PaymentAddress.newBuilder() + .setMobileCoinAddress(SignalServiceProtos.PaymentAddress.MobileCoinAddress.newBuilder() + .setAddress(ByteString.copyFrom(publicAddressBytes)) + .setSignature(ByteString.copyFrom(signature))) + .build(); + } + + /** + * Verifies that the payments address is signed with the supplied {@link IdentityKey}. + *

+ * Returns the validated bytes if so, otherwise returns null. + */ + public static byte[] verifyPaymentsAddress( + SignalServiceProtos.PaymentAddress paymentAddress, ECPublicKey publicKey + ) { + if (!paymentAddress.hasMobileCoinAddress()) { + return null; + } + + byte[] bytes = paymentAddress.getMobileCoinAddress().getAddress().toByteArray(); + byte[] signature = paymentAddress.getMobileCoinAddress().getSignature().toByteArray(); + + if (signature.length != 64 || !publicKey.verifySignature(bytes, signature)) { + return null; + } + + return bytes; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java index 53a9cdb6..e202f70a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -1,14 +1,21 @@ package org.asamk.signal.manager.util; +import com.google.protobuf.InvalidProtocolBufferException; + import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.storage.recipients.Profile; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; +import java.io.IOException; import java.util.Base64; import java.util.HashSet; @@ -20,6 +27,12 @@ public class ProfileUtils { final ProfileKey profileKey, final SignalServiceProfile encryptedProfile ) { var profileCipher = new ProfileCipher(profileKey); + IdentityKey identityKey = null; + try { + identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey()), 0); + } catch (InvalidKeyException ignored) { + } + try { var name = decrypt(encryptedProfile.getName(), profileCipher); var about = trimZeros(decrypt(encryptedProfile.getAbout(), profileCipher)); @@ -32,7 +45,11 @@ public class ProfileUtils { about, aboutEmoji, encryptedProfile.getAvatar(), - encryptedProfile.getPaymentAddress(), + identityKey == null || encryptedProfile.getPaymentAddress() == null + ? null + : decryptAndVerifyMobileCoinAddress(encryptedProfile.getPaymentAddress(), + profileCipher, + identityKey.getPublicKey()), getUnidentifiedAccessMode(encryptedProfile, profileCipher), getCapabilities(encryptedProfile)); } catch (InvalidCiphertextException e) { @@ -88,6 +105,26 @@ public class ProfileUtils { } } + private static byte[] decryptAndVerifyMobileCoinAddress( + final byte[] encryptedPaymentAddress, final ProfileCipher profileCipher, final ECPublicKey publicKey + ) throws InvalidCiphertextException { + byte[] decrypted; + try { + decrypted = profileCipher.decryptWithLength(encryptedPaymentAddress); + } catch (IOException e) { + return null; + } + + SignalServiceProtos.PaymentAddress paymentAddress; + try { + paymentAddress = SignalServiceProtos.PaymentAddress.parseFrom(decrypted); + } catch (InvalidProtocolBufferException e) { + return null; + } + + return PaymentUtils.verifyPaymentsAddress(paymentAddress, publicKey); + } + private static Pair splitName(String name) { if (name == null) { return new Pair<>(null, null); diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 0bfc7ab9..8fc6f2d1 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -81,10 +81,10 @@ public class ListContactsCommand implements JsonRpcLocalCommand { r.getProfile().getFamilyName(), r.getProfile().getAbout(), r.getProfile().getAboutEmoji(), - r.getProfile().getPaymentAddress() == null + r.getProfile().getMobileCoinAddress() == null ? null : Base64.getEncoder() - .encodeToString(r.getProfile().getPaymentAddress()))); + .encodeToString(r.getProfile().getMobileCoinAddress()))); }).toList(); writer.write(jsonContacts); @@ -92,12 +92,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand { } private record JsonContact( - String number, - String uuid, - String name, - boolean isBlocked, - int messageExpirationTime, - JsonProfile profile + String number, String uuid, String name, boolean isBlocked, int messageExpirationTime, JsonProfile profile ) { private record JsonProfile( @@ -106,7 +101,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand { String familyName, String about, String aboutEmoji, - String paymentAddress + String mobileCoinAddress ) {} } } From 34c0968f5e4db6b01b9c5cfd0d9556632e0667ba Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 10:42:56 +0200 Subject: [PATCH 008/833] Add mobile-coin-address to updateProfile command --- .../org/asamk/signal/manager/ManagerImpl.java | 3 ++- .../asamk/signal/manager/api/UpdateProfile.java | 13 +++++++++++++ .../signal/manager/helper/ProfileHelper.java | 17 +++++++++++++---- .../signal/manager/helper/StorageHelper.java | 1 + man/signal-cli.1.adoc | 3 +++ .../signal/commands/UpdateProfileCommand.java | 9 ++++++++- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 3ca3c109..96196f8f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -270,7 +270,8 @@ class ManagerImpl implements Manager { updateProfile.getAboutEmoji(), updateProfile.isDeleteAvatar() ? Optional.empty() - : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar())); + : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar()), + updateProfile.getMobileCoinAddress()); context.getSyncHelper().sendSyncFetchProfileMessage(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java index 2b47f7d5..d5de308e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java @@ -10,6 +10,7 @@ public class UpdateProfile { private final String aboutEmoji; private final File avatar; private final boolean deleteAvatar; + private final byte[] mobileCoinAddress; private UpdateProfile(final Builder builder) { givenName = builder.givenName; @@ -18,6 +19,7 @@ public class UpdateProfile { aboutEmoji = builder.aboutEmoji; avatar = builder.avatar; deleteAvatar = builder.deleteAvatar; + mobileCoinAddress = builder.mobileCoinAddress; } public static Builder newBuilder() { @@ -32,6 +34,7 @@ public class UpdateProfile { builder.aboutEmoji = copy.getAboutEmoji(); builder.avatar = copy.getAvatar(); builder.deleteAvatar = copy.isDeleteAvatar(); + builder.mobileCoinAddress = copy.getMobileCoinAddress(); return builder; } @@ -59,6 +62,10 @@ public class UpdateProfile { return deleteAvatar; } + public byte[] getMobileCoinAddress() { + return mobileCoinAddress; + } + public static final class Builder { private String givenName; @@ -67,6 +74,7 @@ public class UpdateProfile { private String aboutEmoji; private File avatar; private boolean deleteAvatar; + private byte[] mobileCoinAddress; private Builder() { } @@ -101,6 +109,11 @@ public class UpdateProfile { return this; } + public Builder withMobileCoinAddress(final byte[] val) { + mobileCoinAddress = val; + return this; + } + public UpdateProfile build() { return new UpdateProfile(this); } 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 1a876d7e..cc01dc73 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 @@ -64,7 +64,7 @@ public final class ProfileHelper { var profileKey = KeyUtils.createProfileKey(); account.setProfileKey(profileKey); context.getAccountHelper().updateAccountAttributes(); - setProfile(true, true, null, null, null, null, null); + setProfile(true, true, null, null, null, null, null, null); // TODO update profile key in storage final var recipientIds = account.getRecipientStore().getRecipientIdsWithEnabledProfileSharing(); @@ -144,9 +144,14 @@ public final class ProfileHelper { * @param avatar if avatar is null the image from the local avatar store is used (if present), */ public void setProfile( - String givenName, final String familyName, String about, String aboutEmoji, Optional avatar + String givenName, + final String familyName, + String about, + String aboutEmoji, + Optional avatar, + byte[] mobileCoinAddress ) throws IOException { - setProfile(true, false, givenName, familyName, about, aboutEmoji, avatar); + setProfile(true, false, givenName, familyName, about, aboutEmoji, avatar, mobileCoinAddress); } public void setProfile( @@ -156,7 +161,8 @@ public final class ProfileHelper { final String familyName, String about, String aboutEmoji, - Optional avatar + Optional avatar, + byte[] mobileCoinAddress ) throws IOException { var profile = getSelfProfile(); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); @@ -172,6 +178,9 @@ public final class ProfileHelper { if (aboutEmoji != null) { builder.withAboutEmoji(aboutEmoji); } + if (mobileCoinAddress != null) { + builder.withMobileCoinAddress(mobileCoinAddress); + } var newProfile = builder.build(); if (uploadProfile) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 469ca02e..88fe84b9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -234,6 +234,7 @@ public class StorageHelper { accountRecord.getFamilyName().orElse(null), null, null, + null, null); } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 1cebd044..456c6f1a 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -493,6 +493,9 @@ Path to the new avatar image file. *--remove-avatar*:: Remove the avatar +*--mobile-coin-address*:: +New MobileCoin address (Base64 encoded public address) + === updateContact Update the info associated to a number on our contact list. diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 1edb6df9..d8e87430 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -12,6 +12,7 @@ import org.asamk.signal.output.OutputWriter; import java.io.File; import java.io.IOException; +import java.util.Base64; public class UpdateProfileCommand implements JsonRpcLocalCommand { @@ -27,6 +28,7 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { subparser.addArgument("--family-name").help("New profile family name (optional)"); subparser.addArgument("--about").help("New profile about text"); subparser.addArgument("--about-emoji").help("New profile about emoji"); + subparser.addArgument("--mobile-coin-address").help("New MobileCoin address (Base64 encoded public address)"); final var avatarOptions = subparser.addMutuallyExclusiveGroup(); avatarOptions.addArgument("--avatar").help("Path to new profile avatar"); @@ -41,9 +43,13 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { var familyName = ns.getString("family-name"); var about = ns.getString("about"); var aboutEmoji = ns.getString("about-emoji"); + var mobileCoinAddressString = ns.getString("mobile-coin-address"); + var mobileCoinAddress = mobileCoinAddressString == null + ? null + : Base64.getDecoder().decode(mobileCoinAddressString); + var avatarPath = ns.getString("avatar"); boolean removeAvatar = Boolean.TRUE.equals(ns.getBoolean("remove-avatar")); - File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); try { @@ -52,6 +58,7 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { .withFamilyName(familyName) .withAbout(about) .withAboutEmoji(aboutEmoji) + .withMobileCoinAddress(mobileCoinAddress) .withAvatar(avatarFile) .withDeleteAvatar(removeAvatar) .build()); From 5a63b5419fc109c8eede318dba096571161165a4 Mon Sep 17 00:00:00 2001 From: Sebastian Scheibner Date: Sat, 21 May 2022 11:47:46 +0200 Subject: [PATCH 009/833] Update README.md --- README.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e6f74c46..570451c2 100644 --- a/README.md +++ b/README.md @@ -6,26 +6,23 @@ verifying, sending and receiving messages. To be able to link to an existing Sig signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because libsignal-service-java does not yet support [provisioning as a linked device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). For -registering you need a phone number where you can receive SMS or incoming calls. signal-cli is primarily intended to be -used on servers to notify admins of important events. For this use-case, it has a dbus -interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)), that can be used to -send messages from any programming language that has dbus bindings. It also has a JSON-RPC based interface, see -the [documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service) for more information. +registering you need a phone number where you can receive SMS or incoming calls. + +signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a daemon mode with D-BUS +interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) and JSON-PRC interface ([documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service)). For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust. ## Installation -You can [build signal-cli](#building) yourself, or use +You can [build signal-cli](#building) yourself or use the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and -Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/), as well as -a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) and -an [Alpine aport](https://pkgs.alpinelinux.org/packages?name=signal-cli). +Windows. There's also a [docker image and some Linux packages](https://github.com/AsamK/signal-cli/wiki/Binary-distributions) provided by the community. System requirements: - at least Java Runtime Environment (JRE) 17 - native library: libsignal-client - The native libs are bundled for x86_64 Linux (with recent enough glibc, see #643), Windows and MacOS. For other + The native libs are bundled for x86_64 Linux (with recent enough glibc), Windows and MacOS. For other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) From cf07512d2403cd428959f96840ee923a606d78d9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 12:10:51 +0200 Subject: [PATCH 010/833] Update json-rpc client --- client/Cargo.lock | 8 ++++---- client/src/cli.rs | 12 ++++++++++++ client/src/jsonrpc.rs | 10 ++++++++++ client/src/main.rs | 11 +++++++++++ graalvm-config-dir/reflect-config.json | 24 ++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index 855d74bc..607cf0d5 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -650,9 +650,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "rustc_version" diff --git a/client/src/cli.rs b/client/src/cli.rs index 01b59fed..a98b16d3 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -164,6 +164,15 @@ pub enum CliCommands { sticker: Option, }, SendContacts, + SendPaymentNotification { + recipient: String, + + #[clap(long)] + receipt: String, + + #[clap(long)] + note: String, + }, SendReaction { recipient: Vec, @@ -319,6 +328,9 @@ pub enum CliCommands { #[clap(long = "about-emoji")] about_emoji: Option, + #[clap(long = "mobile-coin-address")] + mobile_coin_address: Option, + #[clap(long)] avatar: Option, diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index f91be25e..3c8abbbd 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -135,6 +135,15 @@ pub trait Rpc { #[rpc(name = "sendContacts", params = "named")] fn send_contacts(&self, account: Option) -> Result; + #[rpc(name = "sendPaymentNotification", params = "named")] + fn send_payment_notification( + &self, + account: Option, + recipient: String, + receipt: String, + note: String, + ) -> Result; + #[rpc(name = "sendReaction", params = "named")] fn send_reaction( &self, @@ -263,6 +272,7 @@ pub trait Rpc { #[allow(non_snake_case)] familyName: Option, about: Option, #[allow(non_snake_case)] aboutEmoji: Option, + #[allow(non_snake_case)] mobileCoinAddress: Option, avatar: Option, #[allow(non_snake_case)] removeAvatar: bool, ) -> Result; diff --git a/client/src/main.rs b/client/src/main.rs index 622be806..c0ef0de6 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -147,6 +147,15 @@ async fn main() -> Result<(), anyhow::Error> { .await } cli::CliCommands::SendContacts => client.send_contacts(cli.account).await, + cli::CliCommands::SendPaymentNotification { + recipient, + receipt, + note, + } => { + client + .send_payment_notification(cli.account, recipient, receipt, note) + .await + } cli::CliCommands::SendReaction { recipient, group_id, @@ -309,6 +318,7 @@ async fn main() -> Result<(), anyhow::Error> { family_name, about, about_emoji, + mobile_coin_address, avatar, remove_avatar, } => { @@ -319,6 +329,7 @@ async fn main() -> Result<(), anyhow::Error> { family_name, about, about_emoji, + mobile_coin_address, avatar, remove_avatar, ) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 6d5df0d0..503f17f5 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2971,6 +2971,30 @@ {"name":"type_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$OutgoingPayment", + "fields":[ + {"name":"bitField0_"}, + {"name":"note_"}, + {"name":"paymentDetailCase_"}, + {"name":"paymentDetail_"}, + {"name":"recipientUuid_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$OutgoingPayment$MobileCoin", + "fields":[ + {"name":"amountPicoMob_"}, + {"name":"bitField0_"}, + {"name":"feePicoMob_"}, + {"name":"ledgerBlockIndex_"}, + {"name":"ledgerBlockTimestamp_"}, + {"name":"outputPublicKeys_"}, + {"name":"receipt_"}, + {"name":"recipientAddress_"}, + {"name":"spentKeyImages_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$PniIdentity", "fields":[ From 995eaa6e7cb49b353e306adf08c0457e1aade618 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 22 May 2022 22:01:11 +0200 Subject: [PATCH 011/833] Print more detailed error message when registering with non-normalized number Fixes #958 --- .../asamk/signal/manager/RegistrationManager.java | 5 ++++- .../signal/manager/RegistrationManagerImpl.java | 5 ++++- .../api/NonNormalizedPhoneNumberException.java | 12 ++++++++++++ .../asamk/signal/manager/helper/AccountHelper.java | 3 ++- .../signal/manager/util/NumberVerificationUtils.java | 8 +++++++- .../org/asamk/signal/commands/RegisterCommand.java | 3 +++ .../org/asamk/signal/dbus/DbusSignalControlImpl.java | 3 +++ 7 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 507690f4..a92792c7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -2,6 +2,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import java.io.Closeable; @@ -9,7 +10,9 @@ import java.io.IOException; public interface RegistrationManager extends Closeable { - void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException; + void register( + boolean voiceVerification, String captcha + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException; void verifyAccount( String verificationCode, String pin diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 509b8923..3ce77557 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -18,6 +18,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.config.ServiceConfig; @@ -95,7 +96,9 @@ class RegistrationManagerImpl implements RegistrationManager { } @Override - public void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException { + public void register( + boolean voiceVerification, String captcha + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { if (account.isRegistered() && account.getServiceEnvironment() != null && account.getServiceEnvironment() != serviceEnvironmentConfig.getType()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java b/lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java new file mode 100644 index 00000000..ae00f66b --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java @@ -0,0 +1,12 @@ +package org.asamk.signal.manager.api; + +public class NonNormalizedPhoneNumberException extends Exception { + + public NonNormalizedPhoneNumberException(final String message) { + super(message); + } + + public NonNormalizedPhoneNumberException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index 26247b09..b75cce52 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -5,6 +5,7 @@ import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; @@ -95,7 +96,7 @@ public class AccountHelper { public void startChangeNumber( String newNumber, String captcha, boolean voiceVerification - ) throws IOException, CaptchaRequiredException { + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { final var accountManager = dependencies.createUnauthenticatedAccountManager(newNumber, account.getPassword()); NumberVerificationUtils.requestVerificationCode(accountManager, captcha, voiceVerification); } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java index 2f8981e3..9dcee1fb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java @@ -2,6 +2,7 @@ package org.asamk.signal.manager.util; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.helper.PinHelper; @@ -20,7 +21,7 @@ public class NumberVerificationUtils { public static void requestVerificationCode( SignalServiceAccountManager accountManager, String captcha, boolean voiceVerification - ) throws IOException, CaptchaRequiredException { + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { captcha = captcha == null ? null : captcha.replace("signalcaptcha://", ""); final ServiceResponse response; @@ -39,6 +40,11 @@ public class NumberVerificationUtils { handleResponseException(response); } catch (org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException e) { throw new CaptchaRequiredException(e.getMessage(), e); + } catch (org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException e) { + throw new NonNormalizedPhoneNumberException("Phone number is not normalized (" + + e.getMessage() + + "). Expected normalized: " + + e.getNormalizedNumber(), e); } } diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index d0bc6f1b..b2d3581e 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -12,6 +12,7 @@ import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.api.CaptchaRequiredException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.output.JsonWriter; import java.io.IOException; @@ -76,6 +77,8 @@ public class RegisterCommand implements RegistrationCommand, JsonRpcRegistration message = "Invalid captcha given."; } throw new UserErrorException(message); + } catch (NonNormalizedPhoneNumberException e) { + throw new UserErrorException("Failed to register: " + e.getMessage(), e); } catch (IOException e) { throw new IOErrorException("Request verify error: " + e.getMessage(), e); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index e034d677..59afebf8 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -9,6 +9,7 @@ import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.freedesktop.dbus.DBusPath; @@ -61,6 +62,8 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { } catch (CaptchaRequiredException e) { String message = captcha == null ? "Captcha required for verification." : "Invalid captcha given."; throw new SignalControl.Error.RequiresCaptcha(message); + } catch (NonNormalizedPhoneNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); } catch (OverlappingFileLockException e) { throw new SignalControl.Error.Failure("Account is already in use"); } catch (IOException e) { From 3abb641c7c554bf8a719b0a47a494aee1f9d8eba Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 May 2022 13:18:33 +0200 Subject: [PATCH 012/833] Reduce direct use of recipient store --- .../org/asamk/signal/manager/ManagerImpl.java | 22 +++---- .../signal/manager/helper/AccountHelper.java | 2 +- .../signal/manager/helper/GroupHelper.java | 20 +++--- .../helper/IncomingMessageHandler.java | 20 +++--- .../signal/manager/helper/ProfileHelper.java | 2 +- .../signal/manager/helper/ReceiveHelper.java | 6 +- .../manager/helper/RecipientHelper.java | 14 ++--- .../signal/manager/helper/SendHelper.java | 8 +-- .../signal/manager/helper/StorageHelper.java | 2 +- .../signal/manager/helper/SyncHelper.java | 4 +- .../signal/manager/storage/SignalAccount.java | 61 ++++++++++++------- .../storage/recipients/RecipientStore.java | 4 +- .../recipients/RecipientTrustedResolver.java | 10 +++ 13 files changed, 101 insertions(+), 74 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 96196f8f..19ff8b54 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -168,9 +168,9 @@ class ManagerImpl implements Manager { logger.trace("Archiving old sessions for {}", recipientId); account.getSessionStore().archiveSessions(recipientId); account.getSenderKeyStore().deleteSharedWith(recipientId); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) @@ -215,7 +215,8 @@ class ManagerImpl implements Manager { final var aci = registeredUsers.get(number); final var profile = aci == null ? null - : context.getProfileHelper().getRecipientProfile(account.getRecipientStore().resolveRecipient(aci)); + : context.getProfileHelper() + .getRecipientProfile(account.getRecipientResolver().resolveRecipient(aci)); return new UserStatus(number.isEmpty() ? null : number, aci == null ? null : aci.uuid(), profile != null @@ -356,9 +357,7 @@ class ManagerImpl implements Manager { return null; } - return Group.from(groupInfo, - account.getRecipientStore()::resolveRecipientAddress, - account.getSelfRecipientId()); + return Group.from(groupInfo, account.getRecipientAddressResolver(), account.getSelfRecipientId()); } @Override @@ -459,9 +458,7 @@ class ManagerImpl implements Manager { } private SendMessageResult toSendMessageResult(final org.whispersystems.signalservice.api.messages.SendMessageResult result) { - return SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress); + return SendMessageResult.from(result, account.getRecipientResolver(), account.getRecipientAddressResolver()); } private SendMessageResults sendTypingMessage( @@ -683,13 +680,13 @@ class ManagerImpl implements Manager { @Override public void deleteRecipient(final RecipientIdentifier.Single recipient) { - account.removeRecipient(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); + account.removeRecipient(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override public void deleteContact(final RecipientIdentifier.Single recipient) { account.getContactStore() - .deleteContact(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); + .deleteContact(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override @@ -1018,7 +1015,8 @@ class ManagerImpl implements Manager { return null; } - final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId()); + final var address = account.getRecipientAddressResolver() + .resolveRecipientAddress(identityInfo.getRecipientId()); final var scannableFingerprint = context.getIdentityHelper() .computeSafetyNumberForScanning(identityInfo.getRecipientId(), identityInfo.getIdentityKey()); return new Identity(address, diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index b75cce52..224ac4da 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -86,7 +86,7 @@ public class AccountHelper { account.setNumber(number); account.setAci(aci); account.setPni(pni); - account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); + account.getRecipientTrustedResolver().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); // TODO check and update remote storage context.getUnidentifiedAccessHelper().rotateSenderCertificates(); dependencies.resetAfterAddressChange(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 9346372c..f4e92320 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -153,7 +153,7 @@ public class GroupHelper { downloadGroupAvatar(groupId, groupSecretParams, avatar); } } - groupInfoV2.setGroup(group, account.getRecipientStore()); + groupInfoV2.setGroup(group, account.getRecipientResolver()); account.getGroupStore().updateGroup(groupInfoV2); } @@ -183,7 +183,7 @@ public class GroupHelper { final var gv2 = gv2Pair.first(); final var decryptedGroup = gv2Pair.second(); - gv2.setGroup(decryptedGroup, account.getRecipientStore()); + gv2.setGroup(decryptedGroup, account.getRecipientResolver()); if (avatarFile != null) { context.getAvatarStore() .storeGroupAvatar(gv2.getGroupId(), @@ -398,7 +398,7 @@ public class GroupHelper { downloadGroupAvatar(groupInfoV2.getGroupId(), groupSecretParams, avatar); } } - groupInfoV2.setGroup(decryptedGroup, account.getRecipientStore()); + groupInfoV2.setGroup(decryptedGroup, account.getRecipientResolver()); account.getGroupStore().updateGroup(group); } } @@ -441,7 +441,7 @@ public class GroupHelper { private void storeProfileKeysFromMembers(final DecryptedGroup group) { for (var member : group.getMembersList()) { final var serviceId = ServiceId.fromByteString(member.getUuid()); - final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); final var profileStore = account.getProfileStore(); if (profileStore.getProfileKey(recipientId) != null) { // We already have a profile key, not updating it from a non-authoritative source @@ -461,7 +461,7 @@ public class GroupHelper { if (profileKeyFromChange != null) { final var serviceId = profileKeyFromChange.first(); final var profileKey = profileKeyFromChange.second(); - final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); account.getProfileStore().storeProfileKey(recipientId, profileKey); } } @@ -487,7 +487,7 @@ public class GroupHelper { .forEach(p -> { final var serviceId = p.first(); final var profileKey = p.second(); - final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); newProfileKeys.put(recipientId, profileKey); }); if (!page.getPagingData().hasMorePages()) { @@ -729,7 +729,7 @@ public class GroupHelper { throw new LastGroupAdminException(groupInfoV2.getGroupId(), groupInfoV2.getTitle()); } final var groupGroupChangePair = context.getGroupV2Helper().leaveGroup(groupInfoV2, newAdmins); - groupInfoV2.setGroup(groupGroupChangePair.first(), account.getRecipientStore()); + groupInfoV2.setGroup(groupGroupChangePair.first(), account.getRecipientResolver()); account.getGroupStore().updateGroup(groupInfoV2); var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray()); @@ -773,7 +773,7 @@ public class GroupHelper { ) throws IOException { final var selfRecipientId = account.getSelfRecipientId(); final var members = group.getMembersIncludingPendingWithout(selfRecipientId); - group.setGroup(newDecryptedGroup, account.getRecipientStore()); + group.setGroup(newDecryptedGroup, account.getRecipientResolver()); members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId)); account.getGroupStore().updateGroup(group); @@ -792,8 +792,8 @@ public class GroupHelper { return new SendGroupMessageResults(timestamp, results.stream() .map(sendMessageResult -> SendMessageResult.from(sendMessageResult, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress)) + account.getRecipientResolver(), + account.getRecipientAddressResolver())) .toList()); } } 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 5e310a48..b28c21e6 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 @@ -89,8 +89,8 @@ public final class IncomingMessageHandler { try { content = dependencies.getCipher().decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { - final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); - final var exception = new UntrustedIdentityException(account.getRecipientStore() + final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender()); + final var exception = new UntrustedIdentityException(account.getRecipientAddressResolver() .resolveRecipientAddress(recipientId), e.getSenderDevice()); return new Pair<>(List.of(), exception); } catch (Exception e) { @@ -112,7 +112,7 @@ public final class IncomingMessageHandler { 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()); + account.getRecipientTrustedResolver().resolveRecipientTrusted(envelope.getSourceAddress()); } SignalServiceContent content = null; Exception exception = null; @@ -120,14 +120,14 @@ public final class IncomingMessageHandler { try { content = dependencies.getCipher().decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { - final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); + final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender()); actions.add(new RetrieveProfileAction(recipientId)); - exception = new UntrustedIdentityException(account.getRecipientStore() + exception = new UntrustedIdentityException(account.getRecipientAddressResolver() .resolveRecipientAddress(recipientId), e.getSenderDevice()); } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) { logger.debug("Failed to decrypt incoming message", e); - final var sender = account.getRecipientStore().resolveRecipient(e.getSender()); + final var sender = account.getRecipientResolver().resolveRecipient(e.getSender()); if (context.getContactHelper().isContactBlocked(sender)) { logger.debug("Received invalid message from blocked contact, ignoring."); } else { @@ -169,7 +169,7 @@ public final class IncomingMessageHandler { 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()); + account.getRecipientTrustedResolver().resolveRecipientTrusted(content.getSender()); } if (envelope.isReceipt()) { final var senderPair = getSender(envelope, content); @@ -195,8 +195,8 @@ public final class IncomingMessageHandler { } handler.handleMessage(MessageEnvelope.from(envelope, content, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress, + account.getRecipientResolver(), + account.getRecipientAddressResolver(), context.getAttachmentHelper()::getAttachmentFile, exception), exception); return actions; @@ -391,7 +391,7 @@ public final class IncomingMessageHandler { if (syncMessage.getVerified().isPresent()) { final var verifiedMessage = syncMessage.getVerified().get(); account.getIdentityKeyStore() - .setIdentityTrustLevel(account.getRecipientStore() + .setIdentityTrustLevel(account.getRecipientTrustedResolver() .resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); 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 cc01dc73..38788278 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 @@ -289,7 +289,7 @@ public final class ProfileHelper { var profile = account.getProfileStore().getProfile(recipientId); if (profile == null || !Objects.equals(avatarPath, profile.getAvatarUrlPath())) { logger.trace("Downloading profile avatar for {}", recipientId); - downloadProfileAvatar(account.getRecipientStore().resolveRecipientAddress(recipientId), + downloadProfileAvatar(account.getRecipientAddressResolver().resolveRecipientAddress(recipientId), avatarPath, profileKey); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java index 7bc2c224..7083caae 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java @@ -141,7 +141,7 @@ public class ReceiveHelper { isWaitingForMessage = true; var result = signalWebSocket.readOrEmpty(timeout.toMillis(), envelope1 -> { isWaitingForMessage = false; - final var recipientId = envelope1.hasSourceUuid() ? account.getRecipientStore() + final var recipientId = envelope1.hasSourceUuid() ? account.getRecipientResolver() .resolveRecipient(envelope1.getSourceAddress()) : null; logger.trace("Storing new message from {}", recipientId); // store message on disk, before acknowledging receipt to the server @@ -211,7 +211,7 @@ public class ReceiveHelper { if (exception instanceof UntrustedIdentityException) { logger.debug("Keeping message with untrusted identity in message cache"); final var address = ((UntrustedIdentityException) exception).getSender(); - final var recipientId = account.getRecipientStore().resolveRecipient(address); + final var recipientId = account.getRecipientResolver().resolveRecipient(address); if (!envelope.hasSourceUuid()) { try { cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); @@ -260,7 +260,7 @@ public class ReceiveHelper { } if (!envelope.hasSourceUuid()) { final var identifier = ((UntrustedIdentityException) exception).getSender(); - final var recipientId = account.getRecipientStore().resolveRecipient(identifier); + final var recipientId = account.getRecipientResolver().resolveRecipient(identifier); try { account.getMessageCache().replaceSender(cachedMessage, recipientId); } catch (IOException ioException) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index c253d602..2f254b5d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -40,7 +40,7 @@ public class RecipientHelper { } public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { - final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); + final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); if (address.number().isEmpty() || address.uuid().isPresent()) { return address.toSignalServiceAddress(); } @@ -60,13 +60,13 @@ public class RecipientHelper { // Return SignalServiceAddress with unknown UUID return address.toSignalServiceAddress(); } - return account.getRecipientStore() - .resolveRecipientAddress(account.getRecipientStore().resolveRecipient(aci)) + return account.getRecipientAddressResolver() + .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(aci)) .toSignalServiceAddress(); } public RecipientId resolveRecipient(final SignalServiceAddress address) { - return account.getRecipientStore().resolveRecipient(address); + return account.getRecipientResolver().resolveRecipient(address); } public Set resolveRecipients(Collection recipients) throws UnregisteredRecipientException { @@ -80,7 +80,7 @@ public class RecipientHelper { public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { - return account.getRecipientStore().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); + return account.getRecipientResolver().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); } else { final var number = ((RecipientIdentifier.Number) recipient).number(); return account.getRecipientStore().resolveRecipient(number, () -> { @@ -100,7 +100,7 @@ public class RecipientHelper { } final var number = address.getNumber().get(); final var uuid = getRegisteredUser(number); - return account.getRecipientStore().resolveRecipientTrusted(new SignalServiceAddress(uuid, number)); + return account.getRecipientTrustedResolver().resolveRecipientTrusted(new SignalServiceAddress(uuid, number)); } public Map getRegisteredUsers(final Set numbers) throws IOException { @@ -116,7 +116,7 @@ public class RecipientHelper { } // Store numbers as recipients, so we have the number/uuid association - registeredUsers.forEach((number, aci) -> account.getRecipientStore() + registeredUsers.forEach((number, aci) -> account.getRecipientTrustedResolver() .resolveRecipientTrusted(new SignalServiceAddress(aci, number))); return registeredUsers; 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 8b2a054e..85d5cc32 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 @@ -656,12 +656,12 @@ public class SendHelper { private void handleSendMessageResult(final SendMessageResult r) { if (r.isSuccess() && !r.getSuccess().isUnidentified()) { final var recipientId = context.getRecipientHelper().resolveRecipient(r.getAddress()); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null && ( profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.ENABLED || profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED )) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) @@ -670,12 +670,12 @@ public class SendHelper { } if (r.isUnregisteredFailure()) { final var recipientId = context.getRecipientHelper().resolveRecipient(r.getAddress()); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null && ( profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.ENABLED || profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED )) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 88fe84b9..b1a653fd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -86,7 +86,7 @@ public class StorageHelper { final var contactRecord = record.getContact().get(); final var address = contactRecord.getAddress(); - final var recipientId = account.getRecipientStore().resolveRecipient(address); + final var recipientId = account.getRecipientResolver().resolveRecipient(address); final var contact = account.getContactStore().getContact(recipientId); final var blocked = contact != null && contact.isBlocked(); final var profileShared = contact != null && contact.isProfileSharingEnabled(); 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 86fb0e66..d98f9eef 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 @@ -257,7 +257,7 @@ public class SyncHelper { if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) { account.setProfileKey(c.getProfileKey().get()); } - final var recipientId = account.getRecipientStore().resolveRecipientTrusted(c.getAddress()); + final var recipientId = account.getRecipientTrustedResolver().resolveRecipientTrusted(c.getAddress()); var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); if (c.getName().isPresent()) { @@ -272,7 +272,7 @@ public class SyncHelper { if (c.getVerified().isPresent()) { final var verifiedMessage = c.getVerified().get(); account.getIdentityKeyStore() - .setIdentityTrustLevel(account.getRecipientStore() + .setIdentityTrustLevel(account.getRecipientTrustedResolver() .resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); 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 ac41d791..521e21d9 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 @@ -7,6 +7,7 @@ import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.storage.configuration.ConfigurationStore; import org.asamk.signal.manager.storage.contacts.ContactsStore; import org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore; @@ -27,7 +28,9 @@ import org.asamk.signal.manager.storage.recipients.LegacyRecipientStore; import org.asamk.signal.manager.storage.recipients.Profile; 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.storage.recipients.RecipientStore; +import org.asamk.signal.manager.storage.recipients.RecipientTrustedResolver; import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore; import org.asamk.signal.manager.storage.senderKeys.SenderKeyStore; import org.asamk.signal.manager.storage.sessions.SessionStore; @@ -201,7 +204,7 @@ public class SignalAccount implements Closeable { signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), - signalAccount.getRecipientStore(), + signalAccount.getRecipientResolver(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); @@ -260,7 +263,8 @@ public class SignalAccount implements Closeable { aciIdentityKey, pniIdentityKey, profileKey); - signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); + signalAccount.getRecipientTrustedResolver() + .resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.getSessionStore().archiveAllSessions(); signalAccount.getSenderKeyStore().deleteAll(); signalAccount.clearAllPreKeys(); @@ -321,12 +325,13 @@ public class SignalAccount implements Closeable { signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), - signalAccount.getRecipientStore(), + signalAccount.getRecipientResolver(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); - signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); + signalAccount.getRecipientTrustedResolver() + .resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.migrateLegacyConfigs(); signalAccount.clearAllPreKeys(); @@ -608,11 +613,11 @@ public class SignalAccount implements Closeable { groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStore = GroupStore.fromStorage(groupStoreStorage, getGroupCachePath(dataPath, accountPath), - getRecipientStore(), + getRecipientResolver(), this::saveGroupStore); } else { groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), - getRecipientStore(), + getRecipientResolver(), this::saveGroupStore); } @@ -650,7 +655,7 @@ public class SignalAccount implements Closeable { if (legacyRecipientStore != null) { getRecipientStore().resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); } - getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); + getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress()); migrated = true; } @@ -710,7 +715,7 @@ public class SignalAccount implements Closeable { final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class); for (var contact : contactStore.getContacts()) { final var recipientId = getRecipientStore().resolveRecipientTrusted(contact.getAddress()); - getRecipientStore().storeContact(recipientId, + getContactStore().storeContact(recipientId, new Contact(contact.name, contact.color, contact.messageExpirationTime, @@ -738,9 +743,9 @@ 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 = getRecipientStore().resolveRecipient(profileEntry.getAddress()); - getRecipientStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); - getRecipientStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); + var recipientId = getRecipientResolver().resolveRecipient(profileEntry.getAddress()); + getProfileStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); + getProfileStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); final var profile = profileEntry.getProfile(); if (profile != null) { final var capabilities = new HashSet(); @@ -765,7 +770,7 @@ public class SignalAccount implements Closeable { ? Profile.UnidentifiedAccessMode.ENABLED : Profile.UnidentifiedAccessMode.DISABLED, capabilities); - getRecipientStore().storeProfile(recipientId, newProfile); + getProfileStore().storeProfile(recipientId, newProfile); } } } @@ -784,10 +789,10 @@ public class SignalAccount implements Closeable { } try { if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) { - final var recipientId = getRecipientStore().resolveRecipient(thread.id); - var contact = getRecipientStore().getContact(recipientId); + final var recipientId = getRecipientResolver().resolveRecipient(thread.id); + var contact = getContactStore().getContact(recipientId); if (contact != null) { - getRecipientStore().storeContact(recipientId, + getContactStore().storeContact(recipientId, Contact.newBuilder(contact) .withMessageExpirationTime(thread.messageExpirationTime) .build()); @@ -1028,13 +1033,13 @@ public class SignalAccount implements Closeable { public SessionStore getSessionStore() { return getOrCreate(() -> sessionStore, - () -> sessionStore = new SessionStore(getSessionsPath(dataPath, accountPath), getRecipientStore())); + () -> sessionStore = new SessionStore(getSessionsPath(dataPath, accountPath), getRecipientResolver())); } public IdentityKeyStore getIdentityKeyStore() { return getOrCreate(() -> identityKeyStore, () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), - getRecipientStore(), + getRecipientResolver(), aciIdentityKeyPair, localRegistrationId, trustNewIdentity)); @@ -1048,6 +1053,18 @@ public class SignalAccount implements Closeable { return getRecipientStore(); } + public RecipientResolver getRecipientResolver() { + return getRecipientStore(); + } + + public RecipientTrustedResolver getRecipientTrustedResolver() { + return getRecipientStore(); + } + + public RecipientAddressResolver getRecipientAddressResolver() { + return getRecipientStore()::resolveRecipientAddress; + } + public RecipientStore getRecipientStore() { return getOrCreate(() -> recipientStore, () -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, accountPath), @@ -1067,8 +1084,8 @@ public class SignalAccount implements Closeable { return getOrCreate(() -> senderKeyStore, () -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, accountPath), getSenderKeysPath(dataPath, accountPath), - getRecipientStore()::resolveRecipientAddress, - getRecipientStore())); + getRecipientAddressResolver(), + getRecipientResolver())); } public ConfigurationStore getConfigurationStore() { @@ -1092,7 +1109,7 @@ public class SignalAccount implements Closeable { public MessageSendLogStore getMessageSendLogStore() { return getOrCreate(() -> messageSendLogStore, - () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getAccountDatabase())); + () -> messageSendLogStore = new MessageSendLogStore(getRecipientResolver(), getAccountDatabase())); } public CredentialsProvider getCredentialsProvider() { @@ -1169,7 +1186,7 @@ public class SignalAccount implements Closeable { } public RecipientId getSelfRecipientId() { - return getRecipientStore().resolveRecipient(getSelfRecipientAddress()); + return getRecipientResolver().resolveRecipient(getSelfRecipientAddress()); } public byte[] getEncryptedDeviceName() { @@ -1337,7 +1354,7 @@ public class SignalAccount implements Closeable { clearAllPreKeys(); getSessionStore().archiveAllSessions(); getSenderKeyStore().deleteAll(); - final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); + final var recipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress()); final var publicKey = getAciIdentityKeyPair().getPublicKey(); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); 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 0297f6d7..d2cc94d6 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 @@ -37,7 +37,7 @@ import java.util.UUID; import java.util.function.Supplier; import java.util.stream.Collectors; -public class RecipientStore implements RecipientResolver, ContactsStore, ProfileStore { +public class RecipientStore implements RecipientResolver, RecipientTrustedResolver, ContactsStore, ProfileStore { private final static Logger logger = LoggerFactory.getLogger(RecipientStore.class); @@ -224,6 +224,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return resolveRecipient(new RecipientAddress(address), false, false); } + @Override public RecipientId resolveSelfRecipientTrusted(RecipientAddress address) { return resolveRecipient(address, true, true); } @@ -232,6 +233,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return resolveRecipient(address, true, false); } + @Override public RecipientId resolveRecipientTrusted(SignalServiceAddress address) { return resolveRecipient(new RecipientAddress(address), true, false); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java new file mode 100644 index 00000000..0bc522e3 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java @@ -0,0 +1,10 @@ +package org.asamk.signal.manager.storage.recipients; + +import org.whispersystems.signalservice.api.push.SignalServiceAddress; + +public interface RecipientTrustedResolver { + + RecipientId resolveSelfRecipientTrusted(RecipientAddress address); + + RecipientId resolveRecipientTrusted(SignalServiceAddress address); +} From 621d822a6ce03cf52b0897935cf669d341603eca Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 May 2022 16:24:08 +0200 Subject: [PATCH 013/833] Refresh self profile before rotating profile key --- .../java/org/asamk/signal/manager/helper/ProfileHelper.java | 2 ++ 1 file changed, 2 insertions(+) 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 38788278..812e4b3c 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 @@ -61,6 +61,8 @@ public final class ProfileHelper { } public void rotateProfileKey() throws IOException { + // refresh our profile, before creating a new profile key + getSelfProfile(); var profileKey = KeyUtils.createProfileKey(); account.setProfileKey(profileKey); context.getAccountHelper().updateAccountAttributes(); From 69e952738bd6300940ac3845792f052fe57eb58d Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 May 2022 16:24:36 +0200 Subject: [PATCH 014/833] Update code style for text blocks --- .idea/codeStyles/Project.xml | 1 + .../storage/sendLog/MessageSendLogStore.java | 34 +++++++++---------- .../signal/commands/RegisterCommand.java | 8 ++--- .../signal/util/SendMessageResultUtils.java | 10 +++--- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 9cbd20b7..bb65b3e9 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -4,6 +4,7 @@

+ * The {@code dataURI} must be of the form: + *

+ * {@code + * data:[][;base64], + * } + *

+ * The {@code } is an Internet media type specification (with + * optional parameters.) The appearance of ";base64" means that the data + * is encoded as base64. Without ";base64", the data is represented using (ASCII) URL Escaped encoding. + * If {@code } is omitted, it defaults to {@link DataURI#DEFAULT_TYPE}. + * Parameter values should use the URL Escaped encoding. + * + * @param dataURI the data URI + * @return a data URI object + * @throws IllegalArgumentException if the given string is not a valid data URI + */ + public static DataURI of(final String dataURI) { + final var matcher = DATA_URI_PATTERN.matcher(dataURI); + + if (!matcher.find()) { + throw new IllegalArgumentException("The given string is not a valid data URI."); + } + + final Map parameters = new HashMap<>(); + final var params = matcher.group("parameters"); + if (params != null) { + final Matcher paramsMatcher = PARAMETER_PATTERN.matcher(params); + while (paramsMatcher.find()) { + final var key = paramsMatcher.group("key"); + final var value = URLDecoder.decode(paramsMatcher.group("value"), StandardCharsets.UTF_8); + parameters.put(key, value); + } + } + + final boolean isBase64 = matcher.group("base64") != null; + final byte[] data; + if (isBase64) { + data = Base64.getDecoder().decode(matcher.group("data").getBytes(StandardCharsets.UTF_8)); + } else { + data = URLDecoder.decode(matcher.group("data"), StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8); + } + + return new DataURI(Optional.ofNullable(matcher.group("type")).orElse(DEFAULT_TYPE), parameters, data); + } +} 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 3fc69801..e71e6412 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 @@ -1,5 +1,6 @@ package org.asamk.signal.manager.util; +import org.asamk.signal.manager.api.Pair; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.fingerprint.Fingerprint; import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator; @@ -9,6 +10,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.StreamDetails; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -17,9 +19,11 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Base64; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.BiFunction; @@ -31,10 +35,10 @@ public class Utils { private final static Logger logger = LoggerFactory.getLogger(Utils.class); - public static String getFileMimeType(File file, String defaultMimeType) throws IOException { + public static String getFileMimeType(final File file, final String defaultMimeType) throws IOException { var mime = Files.probeContentType(file.toPath()); if (mime == null) { - try (InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { + try (final InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { mime = URLConnection.guessContentTypeFromStream(bufferedStream); } } @@ -44,13 +48,31 @@ public class Utils { return mime; } - public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { - InputStream stream = new FileInputStream(file); + public static Pair> createStreamDetailsFromDataURI(final String dataURI) { + final DataURI uri = DataURI.of(dataURI); + + return new Pair<>(new StreamDetails( + new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length), + Optional.ofNullable(uri.parameter().get("filename"))); + } + + public static StreamDetails createStreamDetailsFromFile(final File file) throws IOException { + final InputStream stream = new FileInputStream(file); final var size = file.length(); final var mime = getFileMimeType(file, "application/octet-stream"); return new StreamDetails(stream, mime, size); } + public static Pair> createStreamDetails(final String value) throws IOException { + try { + return createStreamDetailsFromDataURI(value); + } catch (final IllegalArgumentException e) { + final File f = new File(value); + + return new Pair<>(createStreamDetailsFromFile(f), Optional.of(f.getName())); + } + } + public static Fingerprint computeSafetyNumber( boolean isUuidCapable, SignalServiceAddress ownAddress, diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 6258243a..d60ef3ad 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -231,6 +231,9 @@ Read the message from standard input. *-a* [ATTACHMENT [ATTACHMENT ...]], *--attachment* [ATTACHMENT [ATTACHMENT ...]]:: Add one or more files as attachment. +Can be either a file path or a data URI. Data URI encoded attachments must follow the RFC 2397. +Additionally a file name can be added: +e.g.: `data:;filename=;base64,` *--sticker* STICKER:: Send a sticker of a locally known sticker pack (syntax: stickerPackId:stickerId). diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index ae324c84..411895dd 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -55,7 +55,9 @@ public class SendCommand implements JsonRpcLocalCommand { mut.addArgument("--message-from-stdin") .action(Arguments.storeTrue()) .help("Read the message from standard input."); - subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment"); + subparser.addArgument("-a", "--attachment").nargs("*").help("Add an attachment. " + + "Can be either a file path or a data URI. Data URI encoded attachments must follow the RFC 2397. Additionally a file name can be added, e.g. " + + "data:;filename=;base64,."); subparser.addArgument("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") .action(Arguments.storeTrue()); From 3ad87e136231159f7439c7707605f3c4fe90d2f0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 4 Jun 2022 11:52:42 +0200 Subject: [PATCH 033/833] Reformat --- .../org/asamk/signal/manager/util/AttachmentUtils.java | 2 -- .../main/java/org/asamk/signal/manager/util/Utils.java | 4 +--- src/main/java/org/asamk/signal/commands/SendCommand.java | 8 +++++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java index 4da78f68..a535d82b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -5,8 +5,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; 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 e71e6412..cead93d9 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 @@ -19,7 +19,6 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.Base64; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -51,8 +50,7 @@ public class Utils { public static Pair> createStreamDetailsFromDataURI(final String dataURI) { final DataURI uri = DataURI.of(dataURI); - return new Pair<>(new StreamDetails( - new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length), + return new Pair<>(new StreamDetails(new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length), Optional.ofNullable(uri.parameter().get("filename"))); } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 411895dd..23a7fb37 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -55,9 +55,11 @@ public class SendCommand implements JsonRpcLocalCommand { mut.addArgument("--message-from-stdin") .action(Arguments.storeTrue()) .help("Read the message from standard input."); - subparser.addArgument("-a", "--attachment").nargs("*").help("Add an attachment. " - + "Can be either a file path or a data URI. Data URI encoded attachments must follow the RFC 2397. Additionally a file name can be added, e.g. " - + "data:;filename=;base64,."); + subparser.addArgument("-a", "--attachment") + .nargs("*") + .help("Add an attachment. " + + "Can be either a file path or a data URI. Data URI encoded attachments must follow the RFC 2397. Additionally a file name can be added, e.g. " + + "data:;filename=;base64,."); subparser.addArgument("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") .action(Arguments.storeTrue()); From 51c2352d676cce004844f704b4c71c3de5bde4cc Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 6 Jun 2022 17:14:17 +0200 Subject: [PATCH 034/833] Extend logging when determining default locale --- lib/src/main/java/org/asamk/signal/manager/util/Utils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 cead93d9..cb0cf95f 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 @@ -107,15 +107,18 @@ public class Utils { public static Locale getDefaultLocale(Locale fallback) { final var locale = Locale.getDefault(); if (locale == null) { + logger.debug("No default locale found, using fallback: {}", fallback); return fallback; } + final var localeString = locale.getLanguage() + "-" + locale.getCountry(); try { - Locale.LanguageRange.parse(locale.getLanguage() + "-" + locale.getCountry()); + Locale.LanguageRange.parse(localeString); } catch (IllegalArgumentException e) { - logger.debug("Invalid locale, ignoring: {}", locale); + logger.debug("Invalid locale '{}', using fallback: {}", locale, fallback); return fallback; } + logger.debug("Using default locale: {} ({})", locale, localeString); return locale; } From 5b5a1718e9c54f6e12eac47662257faa9337f366 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 11:53:53 +0200 Subject: [PATCH 035/833] Update register error message --- graalvm-config-dir/reflect-config.json | 7 +++++++ .../java/org/asamk/signal/commands/RegisterCommand.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 62612d44..2c2c94f9 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2135,6 +2135,13 @@ "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArraySerializer", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException$JsonResponse", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.api.storage.StorageAuthResponse", "allDeclaredFields":true, diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index 4415bb18..84287877 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -80,7 +80,7 @@ public class RegisterCommand implements RegistrationCommand, JsonRpcRegistration } catch (NonNormalizedPhoneNumberException e) { throw new UserErrorException("Failed to register: " + e.getMessage(), e); } catch (IOException e) { - throw new IOErrorException("Request verify error: " + e.getMessage(), e); + throw new IOErrorException("Failed to register: " + e.getMessage(), e); } } From 5afe9c5337e16e89c2ce7bd4aa1781a9ab78704b Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 14:25:28 +0200 Subject: [PATCH 036/833] Rename ThreadInfo to LegacyThreadInfo --- .../storage/threads/LegacyJsonThreadStore.java | 12 ++++++------ .../{ThreadInfo.java => LegacyThreadInfo.java} | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) rename lib/src/main/java/org/asamk/signal/manager/storage/threads/{ThreadInfo.java => LegacyThreadInfo.java} (86%) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java index 81810713..7e5fd242 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java @@ -25,9 +25,9 @@ public class LegacyJsonThreadStore { @JsonProperty("threads") @JsonSerialize(using = MapToListSerializer.class) @JsonDeserialize(using = ThreadsDeserializer.class) - private Map threads = new HashMap<>(); + private Map threads = new HashMap<>(); - public List getThreads() { + public List getThreads() { return new ArrayList<>(threads.values()); } @@ -41,16 +41,16 @@ public class LegacyJsonThreadStore { } } - private static class ThreadsDeserializer extends JsonDeserializer> { + private static class ThreadsDeserializer extends JsonDeserializer> { @Override - public Map deserialize( + public Map deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { - var threads = new HashMap(); + var threads = new HashMap(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); for (var n : node) { - var t = jsonProcessor.treeToValue(n, ThreadInfo.class); + var t = jsonProcessor.treeToValue(n, LegacyThreadInfo.class); threads.put(t.id, t); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyThreadInfo.java similarity index 86% rename from lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyThreadInfo.java index b81a0051..7a55c665 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyThreadInfo.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.threads; import com.fasterxml.jackson.annotation.JsonProperty; -public class ThreadInfo { +public class LegacyThreadInfo { @JsonProperty public String id; From 1757e939e1f60e77744501d0bd6cd7dd762b5dfc Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 15:26:42 +0200 Subject: [PATCH 037/833] Reformat sql statements in MessageSendLogStore --- .../storage/sendLog/MessageSendLogStore.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java index 79fc28f0..c893f09c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java @@ -93,13 +93,16 @@ public class MessageSendLogStore implements AutoCloseable { public List findMessages( final RecipientId recipientId, final int deviceId, final long timestamp, final boolean isSenderKey ) { + final var sql = """ + SELECT group_id, content, content_hint + FROM %s l + INNER JOIN %s lc ON l.content_id = lc._id + WHERE l.recipient_id = ? AND l.device_id = ? AND lc.timestamp = ? + """.formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { deleteOutdatedEntries(connection); - try (final var statement = connection.prepareStatement( - "SELECT group_id, content, content_hint FROM %s l INNER JOIN %s lc ON l.content_id = lc._id WHERE l.recipient_id = ? AND l.device_id = ? AND lc.timestamp = ?".formatted( - TABLE_MESSAGE_SEND_LOG, - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, recipientId.id()); statement.setInt(2, deviceId); statement.setLong(3, timestamp); @@ -185,10 +188,12 @@ public class MessageSendLogStore implements AutoCloseable { } public void deleteEntryForGroup(long sentTimestamp, GroupId groupId) { + final var sql = """ + DELETE FROM %s AS lc + WHERE lc.timestamp = ? AND lc.group_id = ? + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { - try (final var statement = connection.prepareStatement( - "DELETE FROM %s AS lc WHERE lc.timestamp = ? AND lc.group_id = ?".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, sentTimestamp); statement.setBytes(2, groupId.serialize()); statement.executeUpdate(); @@ -199,12 +204,13 @@ public class MessageSendLogStore implements AutoCloseable { } public void deleteEntryForRecipientNonGroup(long sentTimestamp, RecipientId recipientId) { + final var sql = """ + DELETE FROM %s AS lc + WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.recipient_id = ?) + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, TABLE_MESSAGE_SEND_LOG); try (final var connection = database.getConnection()) { connection.setAutoCommit(false); - try (final var statement = connection.prepareStatement( - "DELETE FROM %s AS lc WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.recipient_id = ?)".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT, - TABLE_MESSAGE_SEND_LOG))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, sentTimestamp); statement.setLong(2, recipientId.id()); statement.executeUpdate(); @@ -222,12 +228,13 @@ public class MessageSendLogStore implements AutoCloseable { } public void deleteEntriesForRecipient(List sentTimestamps, RecipientId recipientId, int deviceId) { + final var sql = """ + DELETE FROM %s AS l + WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.recipient_id = ? AND l.device_id = ? + """.formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { connection.setAutoCommit(false); - try (final var statement = connection.prepareStatement( - "DELETE FROM %s AS l WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.recipient_id = ? AND l.device_id = ?".formatted( - TABLE_MESSAGE_SEND_LOG, - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { for (final var sentTimestamp : sentTimestamps) { statement.setLong(1, sentTimestamp); statement.setLong(2, recipientId.id()); @@ -269,12 +276,14 @@ public class MessageSendLogStore implements AutoCloseable { ) { byte[] groupId = getGroupId(content); + final var sql = """ + INSERT INTO %s (timestamp, group_id, content, content_hint) + VALUES (?,?,?,?) + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { connection.setAutoCommit(false); final long contentId; - try (final var statement = connection.prepareStatement( - "INSERT INTO %s (timestamp, group_id, content, content_hint) VALUES (?,?,?,?)".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, sentTimestamp); statement.setBytes(2, groupId); statement.setBytes(3, content.toByteArray()); @@ -334,8 +343,11 @@ public class MessageSendLogStore implements AutoCloseable { private void insertRecipientsForExistingContent( final long contentId, final List recipientDevices, final Connection connection ) throws SQLException { - try (final var statement = connection.prepareStatement( - "INSERT INTO %s (recipient_id, device_id, content_id) VALUES (?,?,?)".formatted(TABLE_MESSAGE_SEND_LOG))) { + final var sql = """ + INSERT INTO %s (recipient_id, device_id, content_id) + VALUES (?,?,?) + """.formatted(TABLE_MESSAGE_SEND_LOG); + try (final var statement = connection.prepareStatement(sql)) { for (final var recipientDevice : recipientDevices) { for (final var deviceId : recipientDevice.deviceIds()) { statement.setLong(1, recipientDevice.recipientId().id()); @@ -348,8 +360,11 @@ public class MessageSendLogStore implements AutoCloseable { } private void deleteOutdatedEntries(final Connection connection) throws SQLException { - try (final var statement = connection.prepareStatement("DELETE FROM %s WHERE timestamp < ?".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + final var sql = """ + DELETE FROM %s + WHERE timestamp < ? + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT); + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, System.currentTimeMillis() - LOG_DURATION.toMillis()); final var rowCount = statement.executeUpdate(); if (rowCount > 0) { @@ -361,9 +376,11 @@ public class MessageSendLogStore implements AutoCloseable { } private void deleteOrphanedLogContents(final Connection connection) throws SQLException { - try (final var statement = connection.prepareStatement( - "DELETE FROM %s WHERE _id NOT IN (SELECT content_id FROM %s)".formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, - TABLE_MESSAGE_SEND_LOG))) { + final var sql = """ + DELETE FROM %s + WHERE _id NOT IN (SELECT content_id FROM %s) + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, TABLE_MESSAGE_SEND_LOG); + try (final var statement = connection.prepareStatement(sql)) { statement.executeUpdate(); } } From 8236696492cb29930c421e642d89d4bd48b7c3e5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 20:34:41 +0200 Subject: [PATCH 038/833] Fix typo --- .../manager/storage/senderKeys/SenderKeyRecordStore.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java index 0d9f6288..83302faf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java @@ -6,6 +6,7 @@ import org.asamk.signal.manager.util.IOUtils; import org.signal.libsignal.protocol.InvalidMessageException; import org.signal.libsignal.protocol.SignalProtocolAddress; import org.signal.libsignal.protocol.groups.state.SenderKeyRecord; +import org.signal.libsignal.protocol.groups.state.SenderKeyStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +24,7 @@ import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class SenderKeyRecordStore implements org.signal.libsignal.protocol.groups.state.SenderKeyStore { +public class SenderKeyRecordStore implements SenderKeyStore { private final static Logger logger = LoggerFactory.getLogger(SenderKeyRecordStore.class); @@ -138,7 +139,7 @@ public class SenderKeyRecordStore implements org.signal.libsignal.protocol.group } /** - * @param identifier can be either a serialized uuid or a e164 phone number + * @param identifier can be either a serialized uuid or an e164 phone number */ private RecipientId resolveRecipient(String identifier) { return resolver.resolveRecipient(identifier); From 936a68433def7a74d11a5fb17e0fac073dae77b9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 15:21:24 +0200 Subject: [PATCH 039/833] Add additional logging for payment address parsing --- .../java/org/asamk/signal/manager/util/PaymentUtils.java | 6 ++++++ .../java/org/asamk/signal/manager/util/ProfileUtils.java | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java index 6c34d9c9..1471d8fe 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java @@ -6,10 +6,14 @@ import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.ecc.ECPrivateKey; import org.signal.libsignal.protocol.ecc.ECPublicKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; public class PaymentUtils { + private final static Logger logger = LoggerFactory.getLogger(PaymentUtils.class); + private PaymentUtils() { } @@ -37,6 +41,7 @@ public class PaymentUtils { SignalServiceProtos.PaymentAddress paymentAddress, ECPublicKey publicKey ) { if (!paymentAddress.hasMobileCoinAddress()) { + logger.debug("Got payment address without mobile coin address, ignoring."); return null; } @@ -44,6 +49,7 @@ public class PaymentUtils { byte[] signature = paymentAddress.getMobileCoinAddress().getSignature().toByteArray(); if (signature.length != 64 || !publicKey.verifySignature(bytes, signature)) { + logger.debug("Got mobile coin address with invalid signature, ignoring."); return null; } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java index e202f70a..5d61cab3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -30,7 +30,8 @@ public class ProfileUtils { IdentityKey identityKey = null; try { identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey()), 0); - } catch (InvalidKeyException ignored) { + } catch (InvalidKeyException e) { + logger.debug("Failed to decode identity key in profile, can't verify payment address", e); } try { @@ -112,6 +113,7 @@ public class ProfileUtils { try { decrypted = profileCipher.decryptWithLength(encryptedPaymentAddress); } catch (IOException e) { + logger.debug("Failed to decrypt payment address", e); return null; } @@ -119,6 +121,7 @@ public class ProfileUtils { try { paymentAddress = SignalServiceProtos.PaymentAddress.parseFrom(decrypted); } catch (InvalidProtocolBufferException e) { + logger.debug("Failed to parse payment address", e); return null; } From 0e65e67077f36932fe9ed1471d41502ca6e1ffe3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 17:26:22 +0200 Subject: [PATCH 040/833] Log more detailed provisioning errors --- .../org/asamk/signal/manager/ProvisioningManagerImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 0c617646..e405f54c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -161,7 +161,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { try { m.refreshPreKeys(); } catch (Exception e) { - logger.error("Failed to refresh pre keys."); + logger.error("Failed to refresh pre keys.", e); } logger.debug("Requesting sync data"); @@ -169,7 +169,8 @@ class ProvisioningManagerImpl implements ProvisioningManager { m.requestAllSyncData(); } catch (Exception e) { logger.error( - "Failed to request sync messages from linked device, data can be requested again with `sendSyncRequest`."); + "Failed to request sync messages from linked device, data can be requested again with `sendSyncRequest`.", + e); } if (newManagerListener != null) { From 7bf06aef5ecea8856aeebc5c9d3a67d11cb73b92 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 17:26:37 +0200 Subject: [PATCH 041/833] Store self profile key in profile store after linking --- .../asamk/signal/manager/storage/SignalAccount.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 f66d3e4a..6d636359 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 @@ -313,6 +313,11 @@ public class SignalAccount implements Closeable { final var pair = openFileChannel(fileName, true); var signalAccount = new SignalAccount(pair.first(), pair.second()); + signalAccount.dataPath = dataPath; + signalAccount.accountPath = accountPath; + signalAccount.serviceEnvironment = serviceEnvironment; + signalAccount.localRegistrationId = registrationId; + signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.setProvisioningData(number, aci, pni, @@ -323,11 +328,6 @@ public class SignalAccount implements Closeable { pniIdentityKey, profileKey); - signalAccount.dataPath = dataPath; - signalAccount.accountPath = accountPath; - signalAccount.serviceEnvironment = serviceEnvironment; - signalAccount.localRegistrationId = registrationId; - signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), signalAccount.getRecipientResolver(), signalAccount::saveGroupStore); @@ -360,6 +360,7 @@ public class SignalAccount implements Closeable { this.pni = pni; this.password = password; this.profileKey = profileKey; + getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey()); this.encryptedDeviceName = encryptedDeviceName; this.deviceId = deviceId; this.aciIdentityKeyPair = aciIdentity; From c8cd36bde884fe03004dfa05357eac34394b4b01 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 17:50:20 +0200 Subject: [PATCH 042/833] Unsubscribe receive if jsonRpcSender channel is closed --- .../java/org/asamk/signal/manager/ManagerImpl.java | 3 ++- .../signal/jsonrpc/SignalJsonRpcDispatcherHandler.java | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index dc7e743e..fcc50e09 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -860,7 +860,8 @@ class ManagerImpl implements Manager { logger.debug("Starting receiving messages"); context.getReceiveHelper().receiveMessagesContinuously((envelope, e) -> { synchronized (messageHandlers) { - Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { + final var handlers = Stream.concat(messageHandlers.stream(), weakHandlers.stream()).toList(); + handlers.forEach(h -> { try { h.handleMessage(envelope, e); } catch (Throwable ex) { diff --git a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java index 086681f7..181233bc 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java +++ b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java @@ -30,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.channels.ClosedChannelException; import java.nio.channels.OverlappingFileLockException; import java.util.HashMap; import java.util.List; @@ -101,7 +102,14 @@ public class SignalJsonRpcDispatcherHandler { final var receiveMessageHandler = new JsonReceiveMessageHandler(m, s -> { final ContainerNode params = objectMapper.valueToTree(s); ((ObjectNode) params).set("subscription", IntNode.valueOf(subscriptionId)); - jsonRpcSender.sendRequest(JsonRpcRequest.forNotification("receive", params, null)); + final var jsonRpcRequest = JsonRpcRequest.forNotification("receive", params, null); + try { + jsonRpcSender.sendRequest(jsonRpcRequest); + } catch (AssertionError e) { + if (e.getCause() instanceof ClosedChannelException) { + unsubscribeReceive(subscriptionId); + } + } }); m.addReceiveHandler(receiveMessageHandler); return new Pair<>(m, (Manager.ReceiveMessageHandler) receiveMessageHandler); From 26620f3137bc7a3aa5a5e05510cfdac6c6fb3a32 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 9 Jun 2022 15:37:21 +0200 Subject: [PATCH 043/833] Refactor identity key store --- .../org/asamk/signal/manager/ManagerImpl.java | 2 +- .../signal/manager/helper/IdentityHelper.java | 5 +- .../signal/manager/helper/ProfileHelper.java | 3 +- .../signal/manager/helper/SendHelper.java | 2 +- .../signal/manager/helper/StorageHelper.java | 3 +- .../signal/manager/helper/SyncHelper.java | 2 +- .../signal/manager/storage/SignalAccount.java | 17 +++-- .../storage/identities/IdentityKeyStore.java | 60 ++++------------- .../identities/SignalIdentityKeyStore.java | 66 +++++++++++++++++++ 9 files changed, 99 insertions(+), 61 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index fcc50e09..201f0931 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -1049,7 +1049,7 @@ class ManagerImpl implements Manager { IdentityInfo identity; try { identity = account.getIdentityKeyStore() - .getIdentity(context.getRecipientHelper().resolveRecipient(recipient)); + .getIdentityInfo(context.getRecipientHelper().resolveRecipient(recipient)); } catch (UnregisteredRecipientException e) { identity = null; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java index ee74338e..ce397353 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java @@ -16,7 +16,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; import java.util.Arrays; -import java.util.Date; import java.util.function.Function; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; @@ -85,7 +84,7 @@ public class IdentityHelper { private boolean trustIdentity( RecipientId recipientId, Function verifier, TrustLevel trustLevel ) { - var identity = account.getIdentityKeyStore().getIdentity(recipientId); + var identity = account.getIdentityKeyStore().getIdentityInfo(recipientId); if (identity == null) { return false; } @@ -110,7 +109,7 @@ public class IdentityHelper { ) { final var identityKey = identityFailure.getIdentityKey(); if (identityKey != null) { - account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); + account.getIdentityKeyStore().saveIdentity(recipientId, identityKey); } else { // Retrieve profile to get the current identity key from the server context.getProfileHelper().refreshRecipientProfile(recipientId); 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 812e4b3c..a3f3b480 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 @@ -35,7 +35,6 @@ import java.io.OutputStream; import java.nio.file.Files; import java.util.Base64; import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -354,7 +353,7 @@ public final class ProfileHelper { try { logger.trace("Storing identity"); final var identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey())); - account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); + account.getIdentityKeyStore().saveIdentity(recipientId, identityKey); } catch (InvalidKeyException ignored) { logger.warn("Got invalid identity key in profile for {}", context.getRecipientHelper().resolveSignalServiceAddress(recipientId).getIdentifier()); 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 758fe1b9..e0f2f161 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 @@ -477,7 +477,7 @@ public class SendHelper { continue; } - final var identity = account.getIdentityKeyStore().getIdentity(recipientId); + final var identity = account.getIdentityKeyStore().getIdentityInfo(recipientId); if (identity == null || !identity.getTrustLevel().isTrusted()) { continue; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 281901c1..84d98db2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -127,7 +126,7 @@ public class StorageHelper { try { logger.trace("Storing identity key {}", recipientId); final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get()); - account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); + account.getIdentityKeyStore().saveIdentity(recipientId, identityKey); final var trustLevel = TrustLevel.fromIdentityState(contactRecord.getIdentityState()); if (trustLevel != null) { 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 4e77d738..3ad1fda8 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 @@ -128,7 +128,7 @@ public class SyncHelper { final var contact = contactPair.second(); final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); - var currentIdentity = account.getIdentityKeyStore().getIdentity(recipientId); + var currentIdentity = account.getIdentityKeyStore().getIdentityInfo(recipientId); VerifiedMessage verifiedMessage = null; if (currentIdentity != null) { verifiedMessage = new VerifiedMessage(address, 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 6d636359..a66af784 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 @@ -15,6 +15,7 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.groups.GroupStore; import org.asamk.signal.manager.storage.identities.IdentityKeyStore; +import org.asamk.signal.manager.storage.identities.SignalIdentityKeyStore; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.storage.messageCache.MessageCache; import org.asamk.signal.manager.storage.prekeys.PreKeyStore; @@ -81,7 +82,6 @@ import java.security.SecureRandom; import java.sql.SQLException; import java.util.Base64; import java.util.Comparator; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -137,6 +137,7 @@ public class SignalAccount implements Closeable { private SignedPreKeyStore pniSignedPreKeyStore; private SessionStore sessionStore; private IdentityKeyStore identityKeyStore; + private SignalIdentityKeyStore aciIdentityKeyStore; private SenderKeyStore senderKeyStore; private GroupStore groupStore; private GroupStore.Storage groupStoreStorage; @@ -1016,7 +1017,7 @@ public class SignalAccount implements Closeable { () -> signalProtocolStore = new SignalProtocolStore(getAciPreKeyStore(), getAciSignedPreKeyStore(), getSessionStore(), - getIdentityKeyStore(), + getAciIdentityKeyStore(), getSenderKeyStore(), this::isMultiDevice)); } @@ -1050,11 +1051,17 @@ public class SignalAccount implements Closeable { return getOrCreate(() -> identityKeyStore, () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), getRecipientResolver(), - aciIdentityKeyPair, - localRegistrationId, trustNewIdentity)); } + public SignalIdentityKeyStore getAciIdentityKeyStore() { + return getOrCreate(() -> aciIdentityKeyStore, + () -> aciIdentityKeyStore = new SignalIdentityKeyStore(getRecipientResolver(), + () -> aciIdentityKeyPair, + localRegistrationId, + getIdentityKeyStore())); + } + public GroupStore getGroupStore() { return groupStore; } @@ -1390,7 +1397,7 @@ public class SignalAccount implements Closeable { getSenderKeyStore().deleteAll(); final var recipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress()); final var publicKey = getAciIdentityKeyPair().getPublicKey(); - getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); + getIdentityKeyStore().saveIdentity(recipientId, publicKey); getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); } 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 d9c553b7..4d08f89f 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 @@ -7,9 +7,8 @@ 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.signal.libsignal.protocol.IdentityKey; -import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.state.IdentityKeyStore.Direction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +31,7 @@ import java.util.regex.Pattern; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.subjects.Subject; -public class IdentityKeyStore implements org.signal.libsignal.protocol.state.IdentityKeyStore { +public class IdentityKeyStore { private final static Logger logger = LoggerFactory.getLogger(IdentityKeyStore.class); private final ObjectMapper objectMapper = org.asamk.signal.manager.storage.Utils.createStorageObjectMapper(); @@ -42,24 +41,16 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide private final File identitiesPath; private final RecipientResolver resolver; - private final IdentityKeyPair identityKeyPair; - private final int localRegistrationId; private final TrustNewIdentity trustNewIdentity; private final PublishSubject identityChanges = PublishSubject.create(); private boolean isRetryingDecryption = false; public IdentityKeyStore( - final File identitiesPath, - final RecipientResolver resolver, - final IdentityKeyPair identityKeyPair, - final int localRegistrationId, - final TrustNewIdentity trustNewIdentity + final File identitiesPath, final RecipientResolver resolver, final TrustNewIdentity trustNewIdentity ) { this.identitiesPath = identitiesPath; this.resolver = resolver; - this.identityKeyPair = identityKeyPair; - this.localRegistrationId = localRegistrationId; this.trustNewIdentity = trustNewIdentity; } @@ -67,21 +58,8 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide return identityChanges; } - @Override - public IdentityKeyPair getIdentityKeyPair() { - return identityKeyPair; - } - - @Override - public int getLocalRegistrationId() { - return localRegistrationId; - } - - @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { - final var recipientId = resolveRecipient(address.getName()); - - return saveIdentity(recipientId, identityKey, new Date()); + public boolean saveIdentity(final RecipientId recipientId, final IdentityKey identityKey) { + return saveIdentity(recipientId, identityKey, null); } public boolean saveIdentity(final RecipientId recipientId, final IdentityKey identityKey, Date added) { @@ -100,7 +78,10 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide trustNewIdentity == TrustNewIdentity.ON_FIRST_USE && identityInfo == null ) ? TrustLevel.TRUSTED_UNVERIFIED : TrustLevel.UNTRUSTED; logger.debug("Storing new identity for recipient {} with trust {}", recipientId, trustLevel); - final var newIdentityInfo = new IdentityInfo(recipientId, identityKey, trustLevel, added); + final var newIdentityInfo = new IdentityInfo(recipientId, + identityKey, + trustLevel, + added == null ? new Date() : added); storeIdentityLocked(recipientId, newIdentityInfo); identityChanges.onNext(recipientId); return true; @@ -137,26 +118,23 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide } } - @Override - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + public boolean isTrustedIdentity(RecipientId recipientId, IdentityKey identityKey, Direction direction) { if (trustNewIdentity == TrustNewIdentity.ALWAYS) { return true; } - var recipientId = resolveRecipient(address.getName()); - synchronized (cachedIdentities) { // TODO implement possibility for different handling of incoming/outgoing trust decisions var identityInfo = loadIdentityLocked(recipientId); if (identityInfo == null) { logger.debug("Initial identity found for {}, saving.", recipientId); - saveIdentity(address, identityKey); + saveIdentity(recipientId, identityKey); identityInfo = loadIdentityLocked(recipientId); } else if (!identityInfo.getIdentityKey().equals(identityKey)) { // Identity found, but different if (direction == Direction.SENDING) { logger.debug("Changed identity found for {}, saving.", recipientId); - saveIdentity(address, identityKey); + saveIdentity(recipientId, identityKey); identityInfo = loadIdentityLocked(recipientId); } else { logger.trace("Trusting identity for {} for {}: {}", recipientId, direction, false); @@ -170,17 +148,14 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide } } - @Override - public IdentityKey getIdentity(SignalProtocolAddress address) { - var recipientId = resolveRecipient(address.getName()); - + public IdentityKey getIdentity(RecipientId recipientId) { synchronized (cachedIdentities) { var identity = loadIdentityLocked(recipientId); return identity == null ? null : identity.getIdentityKey(); } } - public IdentityInfo getIdentity(RecipientId recipientId) { + public IdentityInfo getIdentityInfo(RecipientId recipientId) { synchronized (cachedIdentities) { return loadIdentityLocked(recipientId); } @@ -214,13 +189,6 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide } } - /** - * @param identifier can be either a serialized uuid or a e164 phone number - */ - private RecipientId resolveRecipient(String identifier) { - return resolver.resolveRecipient(identifier); - } - private File getIdentityFile(final RecipientId recipientId) { try { IOUtils.createPrivateDirectories(identitiesPath); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java new file mode 100644 index 00000000..06050875 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java @@ -0,0 +1,66 @@ +package org.asamk.signal.manager.storage.identities; + +import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.SignalProtocolAddress; + +import java.util.function.Supplier; + +public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.state.IdentityKeyStore { + + private final RecipientResolver resolver; + private final Supplier identityKeyPairSupplier; + private final int localRegistrationId; + private final IdentityKeyStore identityKeyStore; + + public SignalIdentityKeyStore( + final RecipientResolver resolver, + final Supplier identityKeyPairSupplier, + final int localRegistrationId, + final IdentityKeyStore identityKeyStore + ) { + this.resolver = resolver; + this.identityKeyPairSupplier = identityKeyPairSupplier; + this.localRegistrationId = localRegistrationId; + this.identityKeyStore = identityKeyStore; + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return identityKeyPairSupplier.get(); + } + + @Override + public int getLocalRegistrationId() { + return localRegistrationId; + } + + @Override + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + final var recipientId = resolveRecipient(address.getName()); + + return identityKeyStore.saveIdentity(recipientId, identityKey); + } + + @Override + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + var recipientId = resolveRecipient(address.getName()); + + return identityKeyStore.isTrustedIdentity(recipientId, identityKey, direction); + } + + @Override + public IdentityKey getIdentity(SignalProtocolAddress address) { + var recipientId = resolveRecipient(address.getName()); + return identityKeyStore.getIdentity(recipientId); + } + + /** + * @param identifier can be either a serialized uuid or an e164 phone number + */ + private RecipientId resolveRecipient(String identifier) { + return resolver.resolveRecipient(identifier); + } +} From 7bb690e58e56ab81ad85f811e438f8024c6cc495 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 12:34:30 +0200 Subject: [PATCH 044/833] Update libsignal-service-java --- lib/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index cad9ca8b..567afd19 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_49") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_50") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") From e9e66e1005ce39684d9d128933bb7edaf99d8de1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 10 Jun 2022 10:34:37 +0200 Subject: [PATCH 045/833] Handle groups sync message again --- .../helper/IncomingMessageHandler.java | 8 +++- .../signal/manager/helper/SyncHelper.java | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) 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 b6e3d8cb..4940e67c 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 @@ -367,7 +367,13 @@ public final class IncomingMessageHandler { } } if (syncMessage.getGroups().isPresent()) { - logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring."); + try { + final var groupsMessage = syncMessage.getGroups().get(); + context.getAttachmentHelper() + .retrieveAttachment(groupsMessage, context.getSyncHelper()::handleSyncDeviceGroups); + } catch (Exception e) { + logger.warn("Failed to handle received sync groups, ignoring: {}", e.getMessage()); + } } if (syncMessage.getBlockedList().isPresent()) { final var blockedListMessage = syncMessage.getBlockedList().get(); 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 3ad1fda8..9a5b5a7d 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 @@ -3,6 +3,7 @@ package org.asamk.signal.manager.helper; import com.google.protobuf.ByteString; import org.asamk.signal.manager.api.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; @@ -21,6 +22,7 @@ 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.KeysMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; @@ -36,7 +38,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; public class SyncHelper { @@ -237,6 +241,48 @@ public class SyncHelper { context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forPniIdentity(pniIdentity)); } + 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(account.getRecipientResolver()::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()) { + context.getGroupHelper().downloadGroupAvatar(syncGroup.getGroupId(), g.getAvatar().get()); + } + syncGroup.archived = g.isArchived(); + account.getGroupStore().updateGroup(syncGroup); + } + } + } + public void handleSyncDeviceContacts(final InputStream input) throws IOException { final var s = new DeviceContactsInputStream(input); DeviceContact c; From 0b63e78d2b80cd96962152393c0130046d490b54 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 18:17:15 +0200 Subject: [PATCH 046/833] Add logging to test script --- run_tests.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/run_tests.sh b/run_tests.sh index 94eb98d9..69a7d382 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -20,8 +20,8 @@ if [ "$NATIVE" -eq 1 ]; then SIGNAL_CLI="$PWD/build/native/nativeCompile/signal-cli" elif [ "$JSON_RPC" -eq 1 ]; then (cd client && cargo build) - "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_MAIN" --service-environment="staging" daemon --socket --receive-mode=manual& - "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_LINK" --service-environment="staging" daemon --tcp --receive-mode=manual& + "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_MAIN" --service-environment="staging" --log-file="$PATH_MAIN/log" daemon --socket --receive-mode=manual& + "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_LINK" --service-environment="staging" --log-file="$PATH_LINK/log" daemon --tcp --receive-mode=manual& sleep 5 SIGNAL_CLI="$PWD/client/target/debug/signal-cli-client" else @@ -41,7 +41,7 @@ run() { if [ "$JSON_RPC" -eq 1 ]; then "$SIGNAL_CLI" $@ else - "$SIGNAL_CLI" --service-environment="staging" $@ + "$SIGNAL_CLI" --service-environment="staging" --verbose --verbose $@ fi set +x } @@ -51,7 +51,7 @@ run_main() { if [ "$JSON_RPC" -eq 1 ]; then run --json-rpc-socket="$XDG_RUNTIME_DIR/signal-cli/socket" $@ else - run --config="$PATH_MAIN" $@ + run --config="$PATH_MAIN" --log-file="$PATH_MAIN/log" $@ fi unset SIGNAL_CLI_AGENT_ID } @@ -61,7 +61,7 @@ run_linked() { if [ "$JSON_RPC" -eq 1 ]; then run --json-rpc-tcp="127.0.0.1:7583" $@ else - run --config="$PATH_LINK" $@ + run --config="$PATH_LINK" --log-file="$PATH_LINK/log" $@ fi unset SIGNAL_CLI_AGENT_ID } @@ -177,14 +177,14 @@ run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -r "$NUMBER_2" run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" run_main -a "$NUMBER_1" listGroups -d run_main -a "$NUMBER_1" --output=json listGroups -d -run_main -a "$NUMBER_2" --verbose receive +run_main -a "$NUMBER_2" receive run_main -a "$NUMBER_2" quitGroup -g "$GROUP_ID" run_main -a "$NUMBER_2" listGroups -d run_main -a "$NUMBER_2" --output=json listGroups -d run_main -a "$NUMBER_1" receive run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" -run_main -a "$NUMBER_1" --verbose block -g "$GROUP_ID" -run_main -a "$NUMBER_1" --verbose unblock -g "$GROUP_ID" +run_main -a "$NUMBER_1" block -g "$GROUP_ID" +run_main -a "$NUMBER_1" unblock -g "$GROUP_ID" ## Configuration run_main -a "$NUMBER_1" updateConfiguration --read-receipts=true From c487929bcd505a76b4ee445a0765079184865e8b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 18:36:53 +0200 Subject: [PATCH 047/833] Make version command work on the command line --- .../asamk/signal/commands/VersionCommand.java | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/VersionCommand.java b/src/main/java/org/asamk/signal/commands/VersionCommand.java index 95b41598..52099ff3 100644 --- a/src/main/java/org/asamk/signal/commands/VersionCommand.java +++ b/src/main/java/org/asamk/signal/commands/VersionCommand.java @@ -1,14 +1,23 @@ package org.asamk.signal.commands; +import com.fasterxml.jackson.core.type.TypeReference; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + import org.asamk.signal.BaseConfig; +import org.asamk.signal.OutputType; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.output.JsonWriter; +import org.asamk.signal.output.OutputWriter; +import org.asamk.signal.output.PlainTextWriter; +import java.util.List; import java.util.Map; -public class VersionCommand implements JsonRpcSingleCommand, JsonRpcMultiCommand { +public class VersionCommand implements JsonRpcLocalCommand, JsonRpcMultiLocalCommand { @Override public String getName() { @@ -16,21 +25,41 @@ public class VersionCommand implements JsonRpcSingleCommand, JsonRpcMultiC } @Override - public void handleCommand( - final Void request, final Manager m, final JsonWriter jsonWriter - ) throws CommandException { - outputVersion(jsonWriter); + public List getSupportedOutputTypes() { + return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + + @Override + public void attachToSubparser(final Subparser subparser) { } @Override public void handleCommand( - final Void request, final MultiAccountManager c, final JsonWriter jsonWriter + final Namespace ns, final Manager m, final OutputWriter outputWriter ) throws CommandException { - outputVersion(jsonWriter); + outputVersion(outputWriter); } - private void outputVersion(final JsonWriter jsonWriter) { - jsonWriter.write(Map.of("version", - BaseConfig.PROJECT_VERSION == null ? "unknown" : BaseConfig.PROJECT_VERSION)); + @Override + public void handleCommand( + final Namespace ns, final MultiAccountManager c, final OutputWriter outputWriter + ) throws CommandException { + outputVersion(outputWriter); + } + + @Override + public TypeReference> getRequestType() { + return new TypeReference<>() {}; + } + + private void outputVersion(final OutputWriter outputWriter) { + final var projectName = BaseConfig.PROJECT_NAME == null ? "signal-cli" : BaseConfig.PROJECT_NAME; + final var version = BaseConfig.PROJECT_VERSION == null ? "unknown" : BaseConfig.PROJECT_VERSION; + + if (outputWriter instanceof JsonWriter jsonWriter) { + jsonWriter.write(Map.of("version", version)); + } else if (outputWriter instanceof PlainTextWriter plainTextWriter) { + plainTextWriter.println("{} {}", projectName, version); + } } } From aaa6412469c0af93a226ba805e6dbe74b4ea9773 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 22:45:51 +0200 Subject: [PATCH 048/833] Allow registering new accounts on both live and staging environments in the same config directory --- graalvm-config-dir/reflect-config.json | 8 +- .../signal/manager/SignalAccountFiles.java | 17 ++- .../storage/accounts/AccountsStorage.java | 4 +- .../storage/accounts/AccountsStore.java | 132 ++++++++++++++---- src/main/java/org/asamk/signal/App.java | 10 +- 5 files changed, 136 insertions(+), 35 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 2c2c94f9..54ed49f6 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -914,8 +914,9 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"","parameterTypes":["java.util.List"] }, - {"name":"accounts","parameterTypes":[] } + {"name":"","parameterTypes":["java.util.List","java.lang.Integer"] }, + {"name":"accounts","parameterTypes":[] }, + {"name":"version","parameterTypes":[] } ] }, { @@ -924,7 +925,8 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, + {"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, + {"name":"environment","parameterTypes":[] }, {"name":"number","parameterTypes":[] }, {"name":"path","parameterTypes":[] }, {"name":"uuid","parameterTypes":[] } diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java index 3821a306..f397b534 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -41,14 +41,24 @@ public class SignalAccountFiles { this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(this.serviceEnvironment, userAgent); this.userAgent = userAgent; this.trustNewIdentity = trustNewIdentity; - this.accountsStore = new AccountsStore(pathConfig.dataPath()); + this.accountsStore = new AccountsStore(pathConfig.dataPath(), serviceEnvironment, accountPath -> { + if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { + return null; + } + + try { + return SignalAccount.load(pathConfig.dataPath(), accountPath, false, trustNewIdentity); + } catch (Exception e) { + return null; + } + }); } - public Set getAllLocalAccountNumbers() { + public Set getAllLocalAccountNumbers() throws IOException { return accountsStore.getAllNumbers(); } - public MultiAccountManager initMultiAccountManager() { + public MultiAccountManager initMultiAccountManager() throws IOException { final var managers = accountsStore.getAllAccounts().parallelStream().map(a -> { try { return initManager(a.number(), a.path()); @@ -108,6 +118,7 @@ public class SignalAccountFiles { if (account.getServiceEnvironment() == null) { account.setServiceEnvironment(serviceEnvironment); + accountsStore.updateAccount(accountPath, account.getNumber(), account.getAci()); } return manager; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java index 7507c339..40107986 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.accounts; import java.util.List; -public record AccountsStorage(List accounts) { +public record AccountsStorage(List accounts, Integer version) { - public record Account(String path, String number, String uuid) {} + public record Account(String path, String environment, String number, String uuid) {} } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java index ea2f0a1b..d708a41c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java @@ -3,6 +3,8 @@ package org.asamk.signal.manager.storage.accounts; import com.fasterxml.jackson.databind.ObjectMapper; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; @@ -29,39 +31,52 @@ import java.util.stream.Stream; public class AccountsStore { + private static final int MINIMUM_STORAGE_VERSION = 1; + private static final int CURRENT_STORAGE_VERSION = 2; private final static Logger logger = LoggerFactory.getLogger(AccountsStore.class); private final ObjectMapper objectMapper = Utils.createStorageObjectMapper(); private final File dataPath; + private final String serviceEnvironment; + private final AccountLoader accountLoader; - public AccountsStore(final File dataPath) throws IOException { + public AccountsStore( + final File dataPath, final ServiceEnvironment serviceEnvironment, final AccountLoader accountLoader + ) throws IOException { this.dataPath = dataPath; + this.serviceEnvironment = getServiceEnvironmentString(serviceEnvironment); + this.accountLoader = accountLoader; if (!getAccountsFile().exists()) { createInitialAccounts(); } } - public synchronized Set getAllNumbers() { + public synchronized Set getAllNumbers() throws IOException { return readAccounts().stream() .map(AccountsStorage.Account::number) .filter(Objects::nonNull) .collect(Collectors.toSet()); } - public synchronized Set getAllAccounts() { - return readAccounts().stream().filter(a -> a.number() != null).collect(Collectors.toSet()); + public synchronized Set getAllAccounts() throws IOException { + return readAccounts().stream() + .filter(a -> a.environment() == null || serviceEnvironment.equals(a.environment())) + .filter(a -> a.number() != null) + .collect(Collectors.toSet()); } - public synchronized String getPathByNumber(String number) { + public synchronized String getPathByNumber(String number) throws IOException { return readAccounts().stream() + .filter(a -> a.environment() == null || serviceEnvironment.equals(a.environment())) .filter(a -> number.equals(a.number())) .map(AccountsStorage.Account::path) .findFirst() .orElse(null); } - public synchronized String getPathByAci(ACI aci) { + public synchronized String getPathByAci(ACI aci) throws IOException { return readAccounts().stream() + .filter(a -> a.environment() == null || serviceEnvironment.equals(a.environment())) .filter(a -> aci.toString().equals(a.uuid())) .map(AccountsStorage.Account::path) .findFirst() @@ -70,15 +85,22 @@ public class AccountsStore { public synchronized void updateAccount(String path, String number, ACI aci) { updateAccounts(accounts -> accounts.stream().map(a -> { + if (a.environment() != null && !serviceEnvironment.equals(a.environment())) { + return a; + } + if (path.equals(a.path())) { - return new AccountsStorage.Account(a.path(), number, aci == null ? null : aci.toString()); + return new AccountsStorage.Account(a.path(), + serviceEnvironment, + number, + aci == null ? null : aci.toString()); } if (number != null && number.equals(a.number())) { - return new AccountsStorage.Account(a.path(), null, a.uuid()); + return new AccountsStorage.Account(a.path(), a.environment(), null, a.uuid()); } if (aci != null && aci.toString().equals(a.toString())) { - return new AccountsStorage.Account(a.path(), a.number(), null); + return new AccountsStorage.Account(a.path(), a.environment(), a.number(), null); } return a; @@ -87,14 +109,21 @@ public class AccountsStore { public synchronized String addAccount(String number, ACI aci) { final var accountPath = generateNewAccountPath(); - final var account = new AccountsStorage.Account(accountPath, number, aci == null ? null : aci.toString()); + final var account = new AccountsStorage.Account(accountPath, + serviceEnvironment, + number, + aci == null ? null : aci.toString()); updateAccounts(accounts -> { final var existingAccounts = accounts.stream().map(a -> { - if (number != null && number.equals(a.number())) { - return new AccountsStorage.Account(a.path(), null, a.uuid()); + if (a.environment() != null && !serviceEnvironment.equals(a.environment())) { + return a; } - if (aci != null && aci.toString().equals(a.toString())) { - return new AccountsStorage.Account(a.path(), a.number(), null); + + if (number != null && number.equals(a.number())) { + return new AccountsStorage.Account(a.path(), a.environment(), null, a.uuid()); + } + if (aci != null && aci.toString().equals(a.uuid())) { + return new AccountsStorage.Account(a.path(), a.environment(), a.number(), null); } return a; @@ -105,7 +134,9 @@ public class AccountsStore { } public void removeAccount(final String accountPath) { - updateAccounts(accounts -> accounts.stream().filter(a -> !a.path().equals(accountPath)).toList()); + updateAccounts(accounts -> accounts.stream().filter(a -> !( + (a.environment() == null || serviceEnvironment.equals(a.environment())) && a.path().equals(accountPath) + )).toList()); } private String generateNewAccountPath() { @@ -123,8 +154,8 @@ public class AccountsStore { private void createInitialAccounts() throws IOException { final var legacyAccountPaths = getLegacyAccountPaths(); final var accountsStorage = new AccountsStorage(legacyAccountPaths.stream() - .map(number -> new AccountsStorage.Account(number, number, null)) - .toList()); + .map(number -> new AccountsStorage.Account(number, null, number, null)) + .toList(), CURRENT_STORAGE_VERSION); IOUtils.createPrivateDirectories(dataPath); var fileName = getAccountsFile(); @@ -152,15 +183,52 @@ public class AccountsStore { .collect(Collectors.toSet()); } - private List readAccounts() { - try { - final var pair = openFileChannel(getAccountsFile()); - try (final var fileChannel = pair.first(); final var lock = pair.second()) { - return readAccountsLocked(fileChannel).accounts(); + private List readAccounts() throws IOException { + final var pair = openFileChannel(getAccountsFile()); + try (final var fileChannel = pair.first(); final var lock = pair.second()) { + final var storage = readAccountsLocked(fileChannel); + + var accountsVersion = storage.version() == null ? 1 : storage.version(); + if (accountsVersion > CURRENT_STORAGE_VERSION) { + throw new IOException("Accounts file was created by a more recent version: " + accountsVersion); + } else if (accountsVersion < MINIMUM_STORAGE_VERSION) { + throw new IOException("Accounts file was created by a no longer supported older version: " + + accountsVersion); + } else if (accountsVersion < CURRENT_STORAGE_VERSION) { + return upgradeAccountsFile(fileChannel, storage, accountsVersion).accounts(); } - } catch (IOException e) { - logger.error("Failed to read accounts list", e); - return List.of(); + return storage.accounts(); + } + } + + private AccountsStorage upgradeAccountsFile( + final FileChannel fileChannel, final AccountsStorage storage, final int accountsVersion + ) { + try { + List newAccounts = storage.accounts(); + if (accountsVersion < 2) { + // add environment field + newAccounts = newAccounts.stream().map(a -> { + if (a.environment() != null) { + return a; + } + try (final var account = accountLoader.loadAccountOrNull(a.path())) { + if (account == null || account.getServiceEnvironment() == null) { + return a; + } + return new AccountsStorage.Account(a.path(), + getServiceEnvironmentString(account.getServiceEnvironment()), + a.number(), + a.uuid()); + } + }).toList(); + } + final var newStorage = new AccountsStorage(newAccounts, CURRENT_STORAGE_VERSION); + saveAccountsLocked(fileChannel, newStorage); + return newStorage; + } catch (Exception e) { + logger.warn("Failed to upgrade accounts file", e); + return storage; } } @@ -170,7 +238,7 @@ public class AccountsStore { try (final var fileChannel = pair.first(); final var lock = pair.second()) { final var accountsStorage = readAccountsLocked(fileChannel); final var newAccountsStorage = updater.apply(accountsStorage.accounts()); - saveAccountsLocked(fileChannel, new AccountsStorage(newAccountsStorage)); + saveAccountsLocked(fileChannel, new AccountsStorage(newAccountsStorage, CURRENT_STORAGE_VERSION)); } } catch (IOException e) { logger.error("Failed to update accounts list", e); @@ -209,4 +277,16 @@ public class AccountsStore { } return new Pair<>(fileChannel, lock); } + + private String getServiceEnvironmentString(final ServiceEnvironment serviceEnvironment) { + return switch (serviceEnvironment) { + case LIVE -> "LIVE"; + case STAGING -> "STAGING"; + }; + } + + public interface AccountLoader { + + SignalAccount loadAccountOrNull(String accountPath); + } } diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 37f5feec..91af270d 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -46,6 +46,7 @@ import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; +import java.util.Set; import static net.sourceforge.argparse4j.DefaultSettings.VERSION_0_9_0_DEFAULT_SETTINGS; @@ -188,7 +189,12 @@ public class App { return; } - var accounts = signalAccountFiles.getAllLocalAccountNumbers(); + Set accounts = null; + try { + accounts = signalAccountFiles.getAllLocalAccountNumbers(); + } catch (IOException e) { + throw new IOErrorException("Failed to load local accounts file", e); + } if (accounts.size() == 0) { throw new UserErrorException("No local users found, you first need to register or link an account"); } else if (accounts.size() > 1) { @@ -299,6 +305,8 @@ public class App { ) throws CommandException { try (var multiAccountManager = signalAccountFiles.initMultiAccountManager()) { command.handleCommand(ns, multiAccountManager, outputWriter); + } catch (IOException e) { + throw new IOErrorException("Failed to load local accounts file", e); } } From 4ea3d94d0768f452899b45120df50a67fd1b8443 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 13 Jun 2022 15:35:25 +0200 Subject: [PATCH 049/833] Improve number filtering of listContacts command If the given number is not registered, don't output all recipients --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 201f0931..5548e5ac 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -989,6 +989,9 @@ class ManagerImpl implements Manager { return null; } }).filter(Objects::nonNull).collect(Collectors.toSet()); + if (!recipients.isEmpty() && recipientIds.isEmpty()) { + return List.of(); + } // refresh profiles of explicitly given recipients context.getProfileHelper().refreshRecipientProfiles(recipientIds); return account.getRecipientStore().getRecipients(onlyContacts, blocked, recipientIds, name); From adce64bc21cc57bcba2bd12a68105be5924a9f63 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 13 Jun 2022 15:46:03 +0200 Subject: [PATCH 050/833] Bump version --- CHANGELOG.md | 11 +++++++++++ build.gradle.kts | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a829372d..3d27afb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## [Unreleased] +## [0.10.8] - 2022-06-13 + +### Added +- Attachments can now be given as data: URIs with base64 data instead of just file paths (Thanks @KevinRoebert) +- `version` command can now be used on the commandline, in addition to the `--version` flag. + In the next version the current short form `-v` will change its meaning to `--verbose`! + +### Improved +- An account can now be registered on both LIVE and STAGING environment in the same config directory. +- Logging output for registering has been extended. + ## [0.10.7] - 2022-05-29 ### Added diff --git a/build.gradle.kts b/build.gradle.kts index 8bdd9ca4..cbaa36c3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.11" } -version = "0.10.7" +version = "0.10.8" java { sourceCompatibility = JavaVersion.VERSION_17 From abebffb2cd99678bd17498fea2d1f0749b602c8b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 18 Jun 2022 12:11:36 +0200 Subject: [PATCH 051/833] Improve output for profile key update messages --- .../java/org/asamk/signal/manager/api/MessageEnvelope.java | 2 ++ src/main/java/org/asamk/signal/ReceiveMessageHandler.java | 5 ++++- src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java index 67a33613..d2164e91 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java @@ -100,6 +100,7 @@ public record MessageEnvelope( boolean isExpirationUpdate, boolean isViewOnce, boolean isEndSession, + boolean isProfileKeyUpdate, boolean hasProfileKey, Optional reaction, Optional quote, @@ -126,6 +127,7 @@ public record MessageEnvelope( dataMessage.isExpirationUpdate(), dataMessage.isViewOnce(), dataMessage.isEndSession(), + dataMessage.isProfileKeyUpdate(), dataMessage.getProfileKey().isPresent(), dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)), dataMessage.getQuote().map(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)), diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 04a7be03..390087f9 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -141,9 +141,12 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (message.expiresInSeconds() > 0) { writer.println("Expires in: {} seconds", message.expiresInSeconds()); } - if (message.hasProfileKey()) { + if (message.isProfileKeyUpdate()) { writer.println("Profile key update"); } + if (message.hasProfileKey()) { + writer.println("With profile key"); + } if (message.reaction().isPresent()) { writer.println("Reaction:"); final var reaction = message.reaction().get(); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 9ffb0795..90153147 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -757,6 +757,7 @@ public class DbusManagerImpl implements Manager { false, false, false, + false, Optional.empty(), Optional.empty(), Optional.empty(), @@ -827,6 +828,7 @@ public class DbusManagerImpl implements Manager { false, false, false, + false, Optional.empty(), Optional.empty(), Optional.empty(), From 280bdbefdcb9123a9bd7ee0cf7b7b3585a972dfc Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 18 Jun 2022 12:27:34 +0200 Subject: [PATCH 052/833] Only send profile key update message from the primary device --- .../asamk/signal/manager/helper/IncomingMessageHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 4940e67c..d18cc0c0 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 @@ -255,7 +255,10 @@ public final class IncomingMessageHandler { } else { // Message wasn't sent as unidentified sender message final var contact = context.getAccount().getContactStore().getContact(sender); - if (contact != null && !contact.isBlocked() && contact.isProfileSharingEnabled()) { + if (account.isPrimaryDevice() + && contact != null + && !contact.isBlocked() + && contact.isProfileSharingEnabled()) { actions.add(UpdateAccountAttributesAction.create()); actions.add(new SendProfileKeyAction(sender)); } From 6bbbccb9ac74d577d159fdabe9b6de27a8d99822 Mon Sep 17 00:00:00 2001 From: John Rush Date: Tue, 21 Jun 2022 01:11:30 -0700 Subject: [PATCH 053/833] Update README.md (#972) Fix typo from "JSON-PRC" to "JSON-RPC". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 570451c2..50442c34 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ support [provisioning as a linked device](https://github.com/WhisperSystems/libs registering you need a phone number where you can receive SMS or incoming calls. signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a daemon mode with D-BUS -interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) and JSON-PRC interface ([documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service)). For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust. +interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) and JSON-RPC interface ([documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service)). For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust. ## Installation From c1dc44d4fd5cf1aba2a30f7c689cf9ea013e2d60 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 16:58:15 +0200 Subject: [PATCH 054/833] Separate registrationLock attribute from master key --- .../manager/RegistrationManagerImpl.java | 2 +- .../signal/manager/helper/AccountHelper.java | 13 ++++------ .../signal/manager/storage/SignalAccount.java | 24 ++++++++++++++++--- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 3ce77557..5a99b827 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -182,7 +182,7 @@ class RegistrationManagerImpl implements RegistrationManager { account.getLocalRegistrationId(), true, null, - account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), + account.getRegistrationLock(), account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), capabilities, diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index bae049c5..0c66a182 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -9,7 +9,6 @@ import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; -import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.NumberVerificationUtils; import org.signal.libsignal.protocol.InvalidKeyException; import org.slf4j.Logger; @@ -125,7 +124,7 @@ public class AccountHelper { account.getLocalRegistrationId(), true, null, - account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), + account.getRegistrationLock(), account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), ServiceConfig.capabilities, @@ -157,20 +156,18 @@ public class AccountHelper { } public void setRegistrationPin(String pin) throws IOException { - final var masterKey = account.getPinMasterKey() != null - ? account.getPinMasterKey() - : KeyUtils.createMasterKey(); + var masterKey = account.getOrCreatePinMasterKey(); context.getPinHelper().setRegistrationLockPin(pin, masterKey); - account.setRegistrationLockPin(pin, masterKey); + account.setRegistrationLockPin(pin); } public void removeRegistrationPin() throws IOException { // Remove KBS Pin context.getPinHelper().removeRegistrationLockPin(); - account.setRegistrationLockPin(null, null); + account.setRegistrationLockPin(null); } public void unregister() throws IOException { @@ -189,7 +186,7 @@ public class AccountHelper { } catch (IOException e) { logger.warn("Failed to remove registration lock pin"); } - account.setRegistrationLockPin(null, null); + account.setRegistrationLockPin(null); dependencies.getAccountManager().deleteAccount(); 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 a66af784..7268693a 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 @@ -1253,13 +1253,31 @@ public class SignalAccount implements Closeable { save(); } - public void setRegistrationLockPin(final String registrationLockPin, final MasterKey pinMasterKey) { + public void setRegistrationLockPin(final String registrationLockPin) { this.registrationLockPin = registrationLockPin; - this.pinMasterKey = pinMasterKey; save(); } - public MasterKey getPinMasterKey() { + public String getRegistrationLock() { + final var masterKey = getPinBackedMasterKey(); + if (masterKey == null) { + return null; + } + return masterKey.deriveRegistrationLock(); + } + + public MasterKey getPinBackedMasterKey() { + if (registrationLockPin == null) { + return null; + } + return pinMasterKey; + } + + public MasterKey getOrCreatePinMasterKey() { + if (pinMasterKey == null) { + pinMasterKey = KeyUtils.createMasterKey(); + save(); + } return pinMasterKey; } From a4db5d616a184b4ef442a37005a0738a276909f5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 17:17:25 +0200 Subject: [PATCH 055/833] Create master key before storage sync if it doesn't exist yet --- .../main/java/org/asamk/signal/manager/ManagerImpl.java | 4 +--- .../manager/actions/RetrieveStorageDataAction.java | 6 +----- .../org/asamk/signal/manager/helper/StorageHelper.java | 9 ++++++++- .../org/asamk/signal/manager/storage/SignalAccount.java | 7 +++++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 5548e5ac..6f178e4e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -830,9 +830,7 @@ class ManagerImpl implements Manager { } void retrieveRemoteStorage() throws IOException { - if (account.getStorageKey() != null) { - context.getStorageHelper().readDataFromStorage(); - } + context.getStorageHelper().readDataFromStorage(); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java index 28b304ab..8b296006 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java @@ -15,10 +15,6 @@ public class RetrieveStorageDataAction implements HandleAction { @Override public void execute(Context context) throws Throwable { - if (context.getAccount().getStorageKey() != null) { - context.getStorageHelper().readDataFromStorage(); - } else if (!context.getAccount().isPrimaryDevice()) { - context.getSyncHelper().requestSyncKeys(); - } + context.getStorageHelper().readDataFromStorage(); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 84d98db2..f849389d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -43,11 +43,18 @@ public class StorageHelper { } public void readDataFromStorage() throws IOException { + final var storageKey = account.getOrCreateStorageKey(); + if (storageKey == null) { + logger.debug("Storage key unknown, requesting from primary device."); + context.getSyncHelper().requestSyncKeys(); + return; + } + logger.debug("Reading data from remote storage"); Optional manifest; try { manifest = dependencies.getAccountManager() - .getStorageManifestIfDifferentVersion(account.getStorageKey(), account.getStorageManifestVersion()); + .getStorageManifestIfDifferentVersion(storageKey, account.getStorageManifestVersion()); } catch (InvalidKeyException e) { logger.warn("Manifest couldn't be decrypted, ignoring."); return; 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 7268693a..72b759b6 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 @@ -1288,6 +1288,13 @@ public class SignalAccount implements Closeable { return storageKey; } + public StorageKey getOrCreateStorageKey() { + if (isPrimaryDevice()) { + return getOrCreatePinMasterKey().deriveStorageServiceKey(); + } + return storageKey; + } + public void setStorageKey(final StorageKey storageKey) { if (storageKey.equals(this.storageKey)) { return; From 9553b1ef00992430477717a5ceb8fd7aea920661 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 17:18:16 +0200 Subject: [PATCH 056/833] Check account identifiers on the server before updating the account attributes --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 6f178e4e..cdb6f640 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -231,6 +231,7 @@ class ManagerImpl implements Manager { if (deviceName != null) { context.getAccountHelper().setDeviceName(deviceName); } + context.getAccountHelper().checkWhoAmiI(); context.getAccountHelper().updateAccountAttributes(); } From c586f58286fb3128d67c420be45b78c1905ed014 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 20:34:38 +0200 Subject: [PATCH 057/833] Reset cached storage manifest after provisioning/registering --- .../signal/manager/storage/SignalAccount.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 72b759b6..5e3e7d04 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 @@ -371,6 +371,7 @@ public class SignalAccount implements Closeable { this.lastReceiveTimestamp = 0; this.pinMasterKey = null; this.storageManifestVersion = -1; + this.setStorageManifest(null); this.storageKey = null; } @@ -1329,9 +1330,19 @@ public class SignalAccount implements Closeable { } public void setStorageManifest(SignalStorageManifest manifest) { - final var manifestBytes = manifest.serialize(); - final var storageManifestFile = getStorageManifestFile(dataPath, accountPath); + if (manifest == null) { + if (storageManifestFile.exists()) { + try { + Files.delete(storageManifestFile.toPath()); + } catch (IOException e) { + logger.error("Failed to delete local storage manifest.", e); + } + } + return; + } + + final var manifestBytes = manifest.serialize(); try (var outputStream = new FileOutputStream(storageManifestFile)) { outputStream.write(manifestBytes); } catch (IOException e) { @@ -1406,6 +1417,7 @@ public class SignalAccount implements Closeable { public void finishRegistration(final ACI aci, final PNI pni, final MasterKey masterKey, final String pin) { this.pinMasterKey = masterKey; this.storageManifestVersion = -1; + this.setStorageManifest(null); this.storageKey = null; this.encryptedDeviceName = null; this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; From dca1d479e84f7b4c8b3a91ef517438d8483a32a7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 27 Jun 2022 15:39:33 +0200 Subject: [PATCH 058/833] Update graalvm buildtools --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index cbaa36c3..878ea08e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { application eclipse `check-lib-versions` - id("org.graalvm.buildtools.native") version "0.9.11" + id("org.graalvm.buildtools.native") version "0.9.12" } version = "0.10.8" From 9f7979314f2e3d5338b704f43e81da5a408d97b9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 27 Jun 2022 16:57:40 +0200 Subject: [PATCH 059/833] Prevent duplicate family name when handling contact sync message --- .../main/java/org/asamk/signal/manager/helper/SyncHelper.java | 1 + 1 file changed, 1 insertion(+) 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 9a5b5a7d..d192ae8f 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 @@ -308,6 +308,7 @@ public class SyncHelper { final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); if (c.getName().isPresent()) { builder.withGivenName(c.getName().get()); + builder.withFamilyName(null); } if (c.getColor().isPresent()) { builder.withColor(c.getColor().get()); From 1c7d1861d6b004c47b5a3264fbfec071f25a9e7b Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jul 2022 20:07:28 +0200 Subject: [PATCH 060/833] Update graalvm buildtools --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 878ea08e..bd257e5a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { application eclipse `check-lib-versions` - id("org.graalvm.buildtools.native") version "0.9.12" + id("org.graalvm.buildtools.native") version "0.9.13" } version = "0.10.8" From 9da2a0040358c57c27fbbf3c98d416b243070832 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jul 2022 20:13:36 +0200 Subject: [PATCH 061/833] Fix issue with loading legacy profile store Fixes #981 --- .../manager/storage/profiles/LegacyProfileStore.java | 2 +- .../{SignalProfile.java => LegacySignalProfile.java} | 7 +++++-- .../manager/storage/profiles/LegacySignalProfileEntry.java | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) rename lib/src/main/java/org/asamk/signal/manager/storage/profiles/{SignalProfile.java => LegacySignalProfile.java} (95%) 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 b3aa4f8c..be275116 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 @@ -60,7 +60,7 @@ public class LegacyProfileStore { } } var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); - var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class); + var profile = jsonProcessor.treeToValue(entry.get("profile"), LegacySignalProfile.class); profileEntries.add(new LegacySignalProfileEntry(address, profileKey, lastUpdateTimestamp, diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfile.java similarity index 95% rename from lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java rename to lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfile.java index ed5f0160..e3e18b1e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfile.java @@ -3,7 +3,7 @@ package org.asamk.signal.manager.storage.profiles; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -public class SignalProfile { +public class LegacySignalProfile { @JsonProperty @JsonIgnore @@ -27,7 +27,7 @@ public class SignalProfile { @JsonProperty private final Capabilities capabilities; - public SignalProfile( + public LegacySignalProfile( @JsonProperty("name") final String name, @JsonProperty("about") final String about, @JsonProperty("aboutEmoji") final String aboutEmoji, @@ -88,6 +88,9 @@ public class SignalProfile { @JsonIgnore public boolean uuid; + @JsonIgnore + public boolean gv2; + @JsonProperty public boolean storage; 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 7571146e..609d2f54 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 @@ -12,7 +12,7 @@ public class LegacySignalProfileEntry { private final long lastUpdateTimestamp; - private final SignalProfile profile; + private final LegacySignalProfile profile; private final ProfileKeyCredential profileKeyCredential; @@ -20,7 +20,7 @@ public class LegacySignalProfileEntry { final RecipientAddress address, final ProfileKey profileKey, final long lastUpdateTimestamp, - final SignalProfile profile, + final LegacySignalProfile profile, final ProfileKeyCredential profileKeyCredential ) { this.address = address; @@ -42,7 +42,7 @@ public class LegacySignalProfileEntry { return lastUpdateTimestamp; } - public SignalProfile getProfile() { + public LegacySignalProfile getProfile() { return profile; } From f4346e3f0ad1507bc9ba350289b32b388f2b2814 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 7 Jul 2022 18:46:29 +0200 Subject: [PATCH 062/833] Update account attributes before checking whoAmI --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index cdb6f640..23423955 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -231,8 +231,8 @@ class ManagerImpl implements Manager { if (deviceName != null) { context.getAccountHelper().setDeviceName(deviceName); } - context.getAccountHelper().checkWhoAmiI(); context.getAccountHelper().updateAccountAttributes(); + context.getAccountHelper().checkWhoAmiI(); } @Override From dcde9fbe8ea884e7ae61b3c64a0cff28c09050ea Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 7 Jul 2022 18:22:09 +0200 Subject: [PATCH 063/833] Use toList() Stream method --- .../java/org/asamk/signal/manager/helper/SendHelper.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 e0f2f161..523df2c9 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 @@ -51,7 +51,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; public class SendHelper { @@ -275,7 +274,7 @@ public class SendHelper { .getDevices() .stream() .map(device -> new SignalProtocolAddress(address.getIdentifier(), device)) - .collect(Collectors.toList()); + .toList(); account.getSenderKeyStore().markSenderKeySharedWith(group.getDistributionId(), addresses); } @@ -536,14 +535,14 @@ public class SendHelper { List addresses = recipientIdList.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) - .collect(Collectors.toList()); + .toList(); List unidentifiedAccesses = context.getUnidentifiedAccessHelper() .getAccessFor(recipientIdList) .stream() .map(Optional::get) .map(UnidentifiedAccessPair::getTargetUnidentifiedAccess) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); try { List results = sender.send(distributionId, From 457b093dce9a63b22b773ac4502867fdc7b0ab2c Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 7 Jul 2022 18:43:49 +0200 Subject: [PATCH 064/833] Improve import of contact from storage --- .../signal/manager/helper/StorageHelper.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index f849389d..bc785099 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -6,6 +6,7 @@ import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.Contact; +import org.asamk.signal.manager.storage.recipients.Profile; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.zkgroup.InvalidInputException; @@ -100,26 +101,36 @@ public class StorageHelper { final var contactRecord = record.getContact().get(); final var address = contactRecord.getAddress(); - final var recipientId = account.getRecipientResolver().resolveRecipient(address); + final var contact = account.getContactStore().getContact(recipientId); final var blocked = contact != null && contact.isBlocked(); final var profileShared = contact != null && contact.isProfileSharingEnabled(); - final var givenName = contact == null ? null : contact.getGivenName(); - final var familyName = contact == null ? null : contact.getFamilyName(); - if ((contactRecord.getGivenName().isPresent() && !contactRecord.getGivenName().get().equals(givenName)) || ( - contactRecord.getFamilyName().isPresent() && !contactRecord.getFamilyName().get().equals(familyName) - ) || blocked != contactRecord.isBlocked() || profileShared != contactRecord.isProfileSharingEnabled()) { + final var archived = contact != null && contact.isArchived(); + if (blocked != contactRecord.isBlocked() + || profileShared != contactRecord.isProfileSharingEnabled() + || archived != contactRecord.isArchived()) { logger.debug("Storing new or updated contact {}", recipientId); final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked()) - .withGivenName(contactRecord.getGivenName().orElse(null)) - .withFamilyName(contactRecord.getFamilyName().orElse(null)) .withProfileSharingEnabled(contactRecord.isProfileSharingEnabled()) + .withArchived(contactRecord.isArchived()) .build(); account.getContactStore().storeContact(recipientId, newContact); } + final var profile = account.getProfileStore().getProfile(recipientId); + final var givenName = profile == null ? null : profile.getGivenName(); + final var familyName = profile == null ? null : profile.getFamilyName(); + if ((contactRecord.getGivenName().isPresent() && !contactRecord.getGivenName().get().equals(givenName)) || ( + contactRecord.getFamilyName().isPresent() && !contactRecord.getFamilyName().get().equals(familyName) + )) { + final var profileBuilder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); + final var newProfile = profileBuilder.withGivenName(contactRecord.getGivenName().orElse(null)) + .withFamilyName(contactRecord.getFamilyName().orElse(null)) + .build(); + account.getProfileStore().storeProfile(recipientId, newProfile); + } if (contactRecord.getProfileKey().isPresent()) { try { logger.trace("Storing profile key {}", recipientId); From ed7d023581ecbdf8cfebbef6a89e41921b281ee0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jul 2022 11:06:10 +0200 Subject: [PATCH 065/833] Update gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 ++++++ gradlew.bat | 14 ++++++++------ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fce..8049c684 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..a69d9cb6 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f9..53a6b238 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From de583475f923fba15cf548369808ee06fba10a2d Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jul 2022 15:17:48 +0200 Subject: [PATCH 066/833] Update libsignal-service-java --- lib/build.gradle.kts | 2 +- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 5 +++-- .../java/org/asamk/signal/manager/helper/SendHelper.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 567afd19..f475d567 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_50") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_51") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 23423955..7b09aa3e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -564,7 +564,8 @@ class ManagerImpl implements Manager { final var quote = message.quote().get(); messageBuilder.withQuote(new SignalServiceDataMessage.Quote(quote.timestamp(), context.getRecipientHelper() - .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())), + .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())) + .getServiceId(), quote.message(), List.of(), resolveMentions(quote.mentions()), @@ -651,7 +652,7 @@ class ManagerImpl implements Manager { var targetAuthorRecipientId = context.getRecipientHelper().resolveRecipient(targetAuthor); var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, - context.getRecipientHelper().resolveSignalServiceAddress(targetAuthorRecipientId), + context.getRecipientHelper().resolveSignalServiceAddress(targetAuthorRecipientId).getServiceId(), targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendMessage(messageBuilder, recipients); 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 523df2c9..f71133ae 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 @@ -633,7 +633,7 @@ public class SendHelper { message.getTimestamp(), Optional.of(message), message.getExpiresInSeconds(), - Map.of(address, true), + Map.of(address.getServiceId(), true), false, Optional.empty(), Set.of()); From 72293d8d877fcbd9412d4a748db9de2b2a950748 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jul 2022 15:25:58 +0200 Subject: [PATCH 067/833] Bump version to 0.10.9 --- CHANGELOG.md | 9 +++++++++ build.gradle.kts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d27afb3..8ff851c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.10.9] - 2022-07-16 + +### Changed +- updateAccount command checks self number and PNI after updating account attributes + +### Fixed +- Fixed small issue with syncing contacts from storage +- Fixed issue with opening older account files + ## [0.10.8] - 2022-06-13 ### Added diff --git a/build.gradle.kts b/build.gradle.kts index bd257e5a..0c5ca9b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.13" } -version = "0.10.8" +version = "0.10.9" java { sourceCompatibility = JavaVersion.VERSION_17 From 956cb97331c2dfa5371d29aa8ac6fe58c2ef0623 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 24 Jul 2022 10:01:22 +0200 Subject: [PATCH 068/833] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 43 +++++++++------ lib/build.gradle.kts | 2 +- .../signal/manager/config/LiveConfig.java | 2 +- .../signal/manager/config/StagingConfig.java | 2 +- .../signal/manager/helper/GroupV2Helper.java | 55 +++++++++++++------ .../signal/manager/helper/ProfileHelper.java | 25 +++++---- .../signal/manager/storage/SignalAccount.java | 2 +- .../storage/profiles/LegacyProfileStore.java | 15 +---- .../profiles/LegacySignalProfileEntry.java | 11 +--- .../storage/profiles/ProfileStore.java | 9 ++- .../manager/storage/recipients/Recipient.java | 26 ++++----- .../storage/recipients/RecipientStore.java | 38 +++++++------ 12 files changed, 122 insertions(+), 108 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 54ed49f6..4c41de46 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -761,7 +761,6 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"date","parameterTypes":[] }, {"name":"description","parameterTypes":[] }, {"name":"image","parameterTypes":[] }, {"name":"title","parameterTypes":[] }, @@ -1041,18 +1040,6 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, -{ - "name":"org.asamk.signal.manager.storage.profiles.SignalProfile", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allDeclaredConstructors":true -}, -{ - "name":"org.asamk.signal.manager.storage.profiles.SignalProfile$Capabilities", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allDeclaredConstructors":true -}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonIdentityKeyStore$JsonIdentityKeyStoreDeserializer", "methods":[{"name":"","parameterTypes":[] }] @@ -1634,13 +1621,13 @@ "queryAllDeclaredMethods":true }, { - "name":"org.signal.libsignal.zkgroup.profiles.ProfileKey", + "name":"org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { - "name":"org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential", + "name":"org.signal.libsignal.zkgroup.profiles.ProfileKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true @@ -1728,6 +1715,7 @@ {"name":"modifyMemberRoles_"}, {"name":"modifyTitle_"}, {"name":"promotePendingMembers_"}, + {"name":"promotePendingPniAciMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"}, {"name":"sourceUuid_"} @@ -1798,7 +1786,11 @@ }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction", - "fields":[{"name":"presentation_"}] + "fields":[ + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"userId_"} + ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberRoleAction", @@ -1817,7 +1809,20 @@ }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingMemberAction", - "fields":[{"name":"presentation_"}] + "fields":[ + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"userId_"} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingPniAciMemberProfileKeyAction", + "fields":[ + {"name":"pni_"}, + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"userId_"} + ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction", @@ -1946,6 +1951,7 @@ {"name":"newTimer_"}, {"name":"newTitle_"}, {"name":"promotePendingMembers_"}, + {"name":"promotePendingPniAciMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"} ] @@ -1967,6 +1973,7 @@ "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", "fields":[ {"name":"joinedAtRevision_"}, + {"name":"pni_"}, {"name":"profileKey_"}, {"name":"role_"}, {"name":"uuid_"} @@ -2142,7 +2149,7 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }] + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"org.whispersystems.signalservice.api.storage.StorageAuthResponse", diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index f475d567..eb68b368 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_51") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_52") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 98e1f026..b219830e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -46,7 +46,7 @@ class LiveConfig { private final static Optional proxy = Optional.empty(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() - .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXQ=="); + .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P"); static SignalServiceConfiguration createDefaultServiceConfiguration( final List interceptors diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index 8faec0eb..ca2adc92 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -46,7 +46,7 @@ class StagingConfig { private final static Optional proxy = Optional.empty(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() - .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXQ=="); + .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj"); static SignalServiceConfiguration createDefaultServiceConfiguration( final List interceptors 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 9b934580..967c70a2 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 @@ -16,7 +16,7 @@ import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.VerificationFailedException; -import org.signal.libsignal.zkgroup.auth.AuthCredentialResponse; +import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.groups.UuidCiphertext; @@ -41,6 +41,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException; import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; @@ -69,7 +70,7 @@ class GroupV2Helper { private final SignalDependencies dependencies; private final Context context; - private HashMap groupApiCredentials; + private HashMap groupApiCredentials; GroupV2Helper(final Context context) { this.dependencies = context.getDependencies(); @@ -177,7 +178,7 @@ class GroupV2Helper { String name, Set members, byte[] avatar ) { final var profileKeyCredential = context.getProfileHelper() - .getRecipientProfileKeyCredential(context.getAccount().getSelfRecipientId()); + .getExpiringProfileKeyCredential(context.getAccount().getSelfRecipientId()); if (profileKeyCredential == null) { logger.warn("Cannot create a V2 group as self does not have a versioned profile"); return null; @@ -185,7 +186,7 @@ class GroupV2Helper { final var self = new GroupCandidate(getSelfAci().uuid(), Optional.of(profileKeyCredential)); final var memberList = new ArrayList<>(members); - final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); + final var credentials = context.getProfileHelper().getExpiringProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, @@ -234,7 +235,7 @@ class GroupV2Helper { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var memberList = new ArrayList<>(newMembers); - final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); + final var credentials = context.getProfileHelper().getExpiringProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, @@ -396,7 +397,7 @@ class GroupV2Helper { logger.debug("Updating own profile key in group " + groupInfoV2.getGroupId().toBase64()); final var selfRecipientId = context.getAccount().getSelfRecipientId(); - final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + final var profileKeyCredential = context.getProfileHelper().getExpiringProfileKeyCredential(selfRecipientId); if (profileKeyCredential == null) { logger.trace("Cannot update profile key as self does not have a versioned profile"); return null; @@ -417,7 +418,7 @@ class GroupV2Helper { final var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); final var selfRecipientId = context.getAccount().getSelfRecipientId(); - final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + final var profileKeyCredential = context.getProfileHelper().getExpiringProfileKeyCredential(selfRecipientId); if (profileKeyCredential == null) { throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); } @@ -439,7 +440,7 @@ class GroupV2Helper { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var selfRecipientId = context.getAccount().getSelfRecipientId(); - final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + final var profileKeyCredential = context.getProfileHelper().getExpiringProfileKeyCredential(selfRecipientId); if (profileKeyCredential == null) { throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); } @@ -609,31 +610,49 @@ class GroupV2Helper { return null; } - private static int currentTimeDays() { - return (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()); + private static long currentDaySeconds() { + return TimeUnit.DAYS.toSeconds(TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis())); } private GroupsV2AuthorizationString getGroupAuthForToday( final GroupSecretParams groupSecretParams ) throws IOException { - final var today = currentTimeDays(); - if (groupApiCredentials == null || !groupApiCredentials.containsKey(today)) { + final var todaySeconds = currentDaySeconds(); + if (groupApiCredentials == null || !groupApiCredentials.containsKey(todaySeconds)) { // Returns credentials for the next 7 days - final var isAci = true; // TODO enable group handling with PNI - groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(today, isAci); + groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(todaySeconds); // TODO cache credentials on disk until they expire } - var authCredentialResponse = groupApiCredentials.get(today); - final var aci = getSelfAci(); try { - return dependencies.getGroupsV2Api() - .getGroupsV2AuthorizationString(aci, today, groupSecretParams, authCredentialResponse); + return getAuthorizationString(groupSecretParams, todaySeconds); + } catch (VerificationFailedException e) { + logger.debug("Group api credentials invalid, renewing and trying again."); + groupApiCredentials.clear(); + } + + groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(todaySeconds); + try { + return getAuthorizationString(groupSecretParams, todaySeconds); } catch (VerificationFailedException e) { throw new IOException(e); } } + private GroupsV2AuthorizationString getAuthorizationString( + final GroupSecretParams groupSecretParams, final long todaySeconds + ) throws VerificationFailedException { + var authCredentialResponse = groupApiCredentials.get(todaySeconds); + final var aci = getSelfAci(); + final var pni = getSelfPni(); + return dependencies.getGroupsV2Api() + .getGroupsV2AuthorizationString(aci, pni, todaySeconds, groupSecretParams, authCredentialResponse); + } + private ACI getSelfAci() { return context.getAccount().getAci(); } + + private PNI getSelfPni() { + return context.getAccount().getPni(); + } } 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 a3f3b480..f4497e5d 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 @@ -16,8 +16,8 @@ import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; @@ -28,6 +28,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.api.util.ExpiringProfileCredentialUtil; import java.io.File; import java.io.IOException; @@ -106,11 +107,12 @@ public final class ProfileHelper { getRecipientProfiles(recipientIds, true); } - public List getRecipientProfileKeyCredential(List recipientIds) { + public List getExpiringProfileKeyCredential(List recipientIds) { try { account.getRecipientStore().setBulkUpdating(true); final var profileFetches = Flowable.fromIterable(recipientIds) - .filter(recipientId -> account.getProfileStore().getProfileKeyCredential(recipientId) == null) + .filter(recipientId -> !ExpiringProfileCredentialUtil.isValid(account.getProfileStore() + .getExpiringProfileKeyCredential(recipientId))) .map(recipientId -> retrieveProfile(recipientId, SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL).onErrorComplete()); Maybe.merge(profileFetches, 10).blockingSubscribe(); @@ -118,12 +120,12 @@ public final class ProfileHelper { account.getRecipientStore().setBulkUpdating(false); } - return recipientIds.stream().map(r -> account.getProfileStore().getProfileKeyCredential(r)).toList(); + return recipientIds.stream().map(r -> account.getProfileStore().getExpiringProfileKeyCredential(r)).toList(); } - public ProfileKeyCredential getRecipientProfileKeyCredential(RecipientId recipientId) { - var profileKeyCredential = account.getProfileStore().getProfileKeyCredential(recipientId); - if (profileKeyCredential != null) { + public ExpiringProfileKeyCredential getExpiringProfileKeyCredential(RecipientId recipientId) { + var profileKeyCredential = account.getProfileStore().getExpiringProfileKeyCredential(recipientId); + if (ExpiringProfileCredentialUtil.isValid(profileKeyCredential)) { return profileKeyCredential; } @@ -134,7 +136,7 @@ public final class ProfileHelper { return null; } - return account.getProfileStore().getProfileKeyCredential(recipientId); + return account.getProfileStore().getExpiringProfileKeyCredential(recipientId); } /** @@ -327,10 +329,11 @@ public final class ProfileHelper { final var encryptedProfile = p.getProfile(); if (requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL - || account.getProfileStore().getProfileKeyCredential(recipientId) == null) { + || !ExpiringProfileCredentialUtil.isValid(account.getProfileStore() + .getExpiringProfileKeyCredential(recipientId))) { logger.trace("Storing profile credential"); - final var profileKeyCredential = p.getProfileKeyCredential().orElse(null); - account.getProfileStore().storeProfileKeyCredential(recipientId, profileKeyCredential); + final var profileKeyCredential = p.getExpiringProfileKeyCredential().orElse(null); + account.getProfileStore().storeExpiringProfileKeyCredential(recipientId, profileKeyCredential); } final var profile = account.getProfileStore().getProfile(recipientId); 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 5e3e7d04..b797290a 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 @@ -756,7 +756,7 @@ public class SignalAccount implements Closeable { final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); for (var profileEntry : legacyProfileStore.getProfileEntries()) { var recipientId = getRecipientResolver().resolveRecipient(profileEntry.getAddress()); - getProfileStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); + // Not migrating profile key credential here, it was changed to expiring profile key credentials getProfileStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); final var profile = profileEntry.getProfile(); if (profile != null) { 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 be275116..99e2980e 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 @@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; @@ -51,21 +50,9 @@ public class LegacyProfileStore { profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText())); } catch (InvalidInputException ignored) { } - ProfileKeyCredential profileKeyCredential = null; - if (entry.hasNonNull("profileKeyCredential")) { - try { - profileKeyCredential = new ProfileKeyCredential(Base64.getDecoder() - .decode(entry.get("profileKeyCredential").asText())); - } catch (Throwable ignored) { - } - } var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); var profile = jsonProcessor.treeToValue(entry.get("profile"), LegacySignalProfile.class); - profileEntries.add(new LegacySignalProfileEntry(address, - profileKey, - lastUpdateTimestamp, - profile, - profileKeyCredential)); + 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 609d2f54..3acad2b1 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 @@ -2,7 +2,6 @@ package org.asamk.signal.manager.storage.profiles; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; public class LegacySignalProfileEntry { @@ -14,20 +13,16 @@ public class LegacySignalProfileEntry { private final LegacySignalProfile profile; - private final ProfileKeyCredential profileKeyCredential; - public LegacySignalProfileEntry( final RecipientAddress address, final ProfileKey profileKey, final long lastUpdateTimestamp, - final LegacySignalProfile profile, - final ProfileKeyCredential profileKeyCredential + final LegacySignalProfile profile ) { this.address = address; this.profileKey = profileKey; this.lastUpdateTimestamp = lastUpdateTimestamp; this.profile = profile; - this.profileKeyCredential = profileKeyCredential; } public RecipientAddress getAddress() { @@ -45,8 +40,4 @@ public class LegacySignalProfileEntry { public LegacySignalProfile getProfile() { return profile; } - - public ProfileKeyCredential getProfileKeyCredential() { - return profileKeyCredential; - } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java index df65db0d..acdcbb18 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java @@ -2,8 +2,8 @@ package org.asamk.signal.manager.storage.profiles; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; public interface ProfileStore { @@ -11,7 +11,7 @@ public interface ProfileStore { ProfileKey getProfileKey(RecipientId recipientId); - ProfileKeyCredential getProfileKeyCredential(RecipientId recipientId); + ExpiringProfileKeyCredential getExpiringProfileKeyCredential(RecipientId recipientId); void storeProfile(RecipientId recipientId, Profile profile); @@ -19,5 +19,8 @@ public interface ProfileStore { void storeProfileKey(RecipientId recipientId, ProfileKey profileKey); - void storeProfileKeyCredential(RecipientId recipientId, ProfileKeyCredential profileKeyCredential); + void storeExpiringProfileKeyCredential( + RecipientId recipientId, + ExpiringProfileKeyCredential expiringProfileKeyCredential + ); } 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 d5c68315..2d3c8430 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 @@ -1,7 +1,7 @@ package org.asamk.signal.manager.storage.recipients; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import java.util.Objects; @@ -15,7 +15,7 @@ public class Recipient { private final ProfileKey profileKey; - private final ProfileKeyCredential profileKeyCredential; + private final ExpiringProfileKeyCredential expiringProfileKeyCredential; private final Profile profile; @@ -24,14 +24,14 @@ public class Recipient { final RecipientAddress address, final Contact contact, final ProfileKey profileKey, - final ProfileKeyCredential profileKeyCredential, + final ExpiringProfileKeyCredential expiringProfileKeyCredential, final Profile profile ) { this.recipientId = recipientId; this.address = address; this.contact = contact; this.profileKey = profileKey; - this.profileKeyCredential = profileKeyCredential; + this.expiringProfileKeyCredential = expiringProfileKeyCredential; this.profile = profile; } @@ -40,7 +40,7 @@ public class Recipient { address = builder.address; contact = builder.contact; profileKey = builder.profileKey; - profileKeyCredential = builder.profileKeyCredential; + expiringProfileKeyCredential = builder.expiringProfileKeyCredential1; profile = builder.profile; } @@ -54,7 +54,7 @@ public class Recipient { builder.address = copy.getAddress(); builder.contact = copy.getContact(); builder.profileKey = copy.getProfileKey(); - builder.profileKeyCredential = copy.getProfileKeyCredential(); + builder.expiringProfileKeyCredential1 = copy.getExpiringProfileKeyCredential(); builder.profile = copy.getProfile(); return builder; } @@ -75,8 +75,8 @@ public class Recipient { return profileKey; } - public ProfileKeyCredential getProfileKeyCredential() { - return profileKeyCredential; + public ExpiringProfileKeyCredential getExpiringProfileKeyCredential() { + return expiringProfileKeyCredential; } public Profile getProfile() { @@ -92,13 +92,13 @@ public class Recipient { && Objects.equals(address, recipient.address) && Objects.equals(contact, recipient.contact) && Objects.equals(profileKey, recipient.profileKey) - && Objects.equals(profileKeyCredential, recipient.profileKeyCredential) + && Objects.equals(expiringProfileKeyCredential, recipient.expiringProfileKeyCredential) && Objects.equals(profile, recipient.profile); } @Override public int hashCode() { - return Objects.hash(recipientId, address, contact, profileKey, profileKeyCredential, profile); + return Objects.hash(recipientId, address, contact, profileKey, expiringProfileKeyCredential, profile); } public static final class Builder { @@ -107,7 +107,7 @@ public class Recipient { private RecipientAddress address; private Contact contact; private ProfileKey profileKey; - private ProfileKeyCredential profileKeyCredential; + private ExpiringProfileKeyCredential expiringProfileKeyCredential1; private Profile profile; private Builder() { @@ -133,8 +133,8 @@ public class Recipient { return this; } - public Builder withProfileKeyCredential(final ProfileKeyCredential val) { - profileKeyCredential = val; + public Builder withExpiringProfileKeyCredential(final ExpiringProfileKeyCredential val) { + expiringProfileKeyCredential1 = val; return this; } 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 cc9db862..7244a96c 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 @@ -8,8 +8,8 @@ import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.contacts.ContactsStore; import org.asamk.signal.manager.storage.profiles.ProfileStore; import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ACI; @@ -89,11 +89,11 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv } } - ProfileKeyCredential profileKeyCredential = null; - if (r.profileKeyCredential != null) { + ExpiringProfileKeyCredential expiringProfileKeyCredential = null; + if (r.expiringProfileKeyCredential != null) { try { - profileKeyCredential = new ProfileKeyCredential(Base64.getDecoder() - .decode(r.profileKeyCredential)); + expiringProfileKeyCredential = new ExpiringProfileKeyCredential(Base64.getDecoder() + .decode(r.expiringProfileKeyCredential)); } catch (Throwable ignored) { } } @@ -116,7 +116,7 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv .collect(Collectors.toSet())); } - return new Recipient(recipientId, address, contact, profileKey, profileKeyCredential, profile); + return new Recipient(recipientId, address, contact, profileKey, expiringProfileKeyCredential, profile); }).collect(Collectors.toMap(Recipient::getRecipientId, r -> r)); recipientStore.addRecipients(recipients); @@ -333,9 +333,9 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv } @Override - public ProfileKeyCredential getProfileKeyCredential(final RecipientId recipientId) { + public ExpiringProfileKeyCredential getExpiringProfileKeyCredential(final RecipientId recipientId) { final var recipient = getRecipient(recipientId); - return recipient == null ? null : recipient.getProfileKeyCredential(); + return recipient == null ? null : recipient.getExpiringProfileKeyCredential(); } @Override @@ -371,7 +371,7 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv final var builder = Recipient.newBuilder(recipient) .withProfileKey(profileKey) - .withProfileKeyCredential(null); + .withExpiringProfileKeyCredential(null); if (resetProfile) { builder.withProfile(recipient.getProfile() == null ? null @@ -383,11 +383,15 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv } @Override - public void storeProfileKeyCredential(RecipientId recipientId, final ProfileKeyCredential profileKeyCredential) { + public void storeExpiringProfileKeyCredential( + RecipientId recipientId, final ExpiringProfileKeyCredential expiringProfileKeyCredential + ) { synchronized (recipients) { final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, - Recipient.newBuilder(recipient).withProfileKeyCredential(profileKeyCredential).build()); + Recipient.newBuilder(recipient) + .withExpiringProfileKeyCredential(expiringProfileKeyCredential) + .build()); } } @@ -535,9 +539,9 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv recipient.getProfileKey() != null ? recipient.getProfileKey() : toBeMergedRecipient.getProfileKey(), - recipient.getProfileKeyCredential() != null - ? recipient.getProfileKeyCredential() - : toBeMergedRecipient.getProfileKeyCredential(), + recipient.getExpiringProfileKeyCredential() != null + ? recipient.getExpiringProfileKeyCredential() + : toBeMergedRecipient.getExpiringProfileKeyCredential(), recipient.getProfile() != null ? recipient.getProfile() : toBeMergedRecipient.getProfile())); recipients.remove(toBeMergedRecipientId); recipientsMerged.put(toBeMergedRecipientId.id(), recipientId.id()); @@ -607,9 +611,9 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv recipient.getProfileKey() == null ? null : base64.encodeToString(recipient.getProfileKey().serialize()), - recipient.getProfileKeyCredential() == null + recipient.getExpiringProfileKeyCredential() == null ? null - : base64.encodeToString(recipient.getProfileKeyCredential().serialize()), + : base64.encodeToString(recipient.getExpiringProfileKeyCredential().serialize()), contact, profile); }).toList(), lastId); @@ -634,7 +638,7 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv String number, String uuid, String profileKey, - String profileKeyCredential, + String expiringProfileKeyCredential, Storage.Recipient.Contact contact, Storage.Recipient.Profile profile ) { From 1cc21834e2429a5efa27b1766dcff9d6b3151ddf Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 27 Jul 2022 18:20:07 +0200 Subject: [PATCH 069/833] Workaround possible GraalVM issue Fixes #987 --- src/main/java/org/asamk/signal/util/IOUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 9f220946..2d4ffd8e 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -125,7 +125,7 @@ public class IOUtils { UnixDomainPrincipal principal = null; try { principal = channel.getOption(ExtendedSocketOptions.SO_PEERCRED); - } catch (UnsupportedOperationException ignored) { + } catch (UnsupportedOperationException | NoClassDefFoundError ignored) { } return principal; } From 7a427372872580e6d4ce740e668101dbc2a1f536 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 27 Jul 2022 20:58:58 +0200 Subject: [PATCH 070/833] Workaround possible GraalVM issue --- src/main/java/org/asamk/signal/util/IOUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 2d4ffd8e..9e0d6c3e 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -121,13 +121,13 @@ public class IOUtils { return socketAddress; } - public static UnixDomainPrincipal getUnixDomainPrincipal(final SocketChannel channel) throws IOException { + public static String getUnixDomainPrincipal(final SocketChannel channel) throws IOException { UnixDomainPrincipal principal = null; try { principal = channel.getOption(ExtendedSocketOptions.SO_PEERCRED); } catch (UnsupportedOperationException | NoClassDefFoundError ignored) { } - return principal; + return principal == null ? null : principal.toString(); } public static ServerSocketChannel bindSocket(final SocketAddress address) throws IOErrorException { From d69b9ea394dc4ac8298139480700611316bdb444 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 28 Jul 2022 16:57:49 +0200 Subject: [PATCH 071/833] Update KBS enclave Fixes #985 --- .../java/org/asamk/signal/manager/config/LiveConfig.java | 6 +++--- .../java/org/asamk/signal/manager/config/StagingConfig.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index b219830e..41eab6cf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -28,10 +28,10 @@ class LiveConfig { .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; - private final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"; + private final static String KEY_BACKUP_ENCLAVE_NAME = "0cedba03535b41b67729ce9924185f831d7767928a1d1689acb689bc079c375f"; private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( - "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"); - private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + "187d2739d22be65e74b65f0055e74d31310e4267e5fac2b1246cc8beba81af39"); + private final static String KEY_BACKUP_MRENCLAVE = "ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba"; private final static String URL = "https://chat.signal.org"; private final static String CDN_URL = "https://cdn.signal.org"; diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index ca2adc92..771e9348 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -28,10 +28,10 @@ class StagingConfig { .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; - private final static String KEY_BACKUP_ENCLAVE_NAME = "823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9"; + private final static String KEY_BACKUP_ENCLAVE_NAME = "dd6f66d397d9e8cf6ec6db238e59a7be078dd50e9715427b9c89b409ffe53f99"; private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( - "16b94ac6d2b7f7b9d72928f36d798dbb35ed32e7bb14c42b4301ad0344b46f29"); - private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + "4200003414528c151e2dccafbc87aa6d3d66a5eb8f8c05979a6e97cb33cd493a"); + private final static String KEY_BACKUP_MRENCLAVE = "ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba"; private final static String URL = "https://chat.staging.signal.org"; private final static String CDN_URL = "https://cdn-staging.signal.org"; From d8a5244a8861cde08c954e128e3267e705e6ae5f Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 30 Jul 2022 14:56:17 +0200 Subject: [PATCH 072/833] Bump version to 0.10.10 --- CHANGELOG.md | 5 +++++ build.gradle.kts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff851c8..5d70bb27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +## [0.10.10] - 2022-07-30 +### Fixed +- Fix setPin/removePin commands which broke due to server side changes +- Workaround GraalVM 22.2.0 issue with daemon connection + ## [0.10.9] - 2022-07-16 ### Changed diff --git a/build.gradle.kts b/build.gradle.kts index 0c5ca9b2..35960cbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.13" } -version = "0.10.9" +version = "0.10.10" java { sourceCompatibility = JavaVersion.VERSION_17 From 525f04d44606cd11d0bf15b2bed7ec2dc1bff500 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 30 Jul 2022 16:52:13 +0200 Subject: [PATCH 073/833] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d70bb27..d2083b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [Unreleased] ## [0.10.10] - 2022-07-30 +**Attention**: Now requires native libsignal-client version 0.18.1 + ### Fixed - Fix setPin/removePin commands which broke due to server side changes - Workaround GraalVM 22.2.0 issue with daemon connection From 597ac9b5042bec57c845b4ca08ce935d84ef63d4 Mon Sep 17 00:00:00 2001 From: Sebastian Scheibner Date: Sat, 30 Jul 2022 16:57:48 +0200 Subject: [PATCH 074/833] Update FUNDING.yml --- FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/FUNDING.yml b/FUNDING.yml index 909168ff..c2420609 100644 --- a/FUNDING.yml +++ b/FUNDING.yml @@ -1,3 +1,4 @@ +github: AsamK liberapay: asamk ko_fi: asamk #bitcoin: bc1qykae53fry8a8ycgdzgv0rlxfc959hmmllvz698 From 81e36d4f9b690df2c39d772f9b3e99422329ff27 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 2 Aug 2022 23:20:26 +0200 Subject: [PATCH 075/833] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 4 ++- lib/build.gradle.kts | 2 +- .../signal/manager/helper/SendHelper.java | 32 +++++++++++++------ .../storage/sendLog/MessageSendLogEntry.java | 2 +- .../storage/sendLog/MessageSendLogStore.java | 16 ++++++---- .../manager/util/MessageCacheUtils.java | 13 ++++++-- 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 4c41de46..c223851b 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2806,7 +2806,8 @@ {"name":"sourceE164_"}, {"name":"sourceUuid_"}, {"name":"timestamp_"}, - {"name":"type_"} + {"name":"type_"}, + {"name":"urgent_"} ] }, { @@ -3052,6 +3053,7 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", "fields":[ {"name":"bitField0_"}, + {"name":"destinationE164_"}, {"name":"destinationUuid_"}, {"name":"expirationStartTimestamp_"}, {"name":"isRecipientUpdate_"}, diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index eb68b368..70de8dd7 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_52") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_53") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") 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 f71133ae..717db812 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 @@ -123,7 +123,7 @@ public class SendHelper { (messageSender, address, unidentifiedAccess) -> messageSender.sendReceipt(address, unidentifiedAccess, receiptMessage)); - messageSendLogStore.insertIfPossible(receiptMessage.getWhen(), result, ContentHint.IMPLICIT); + messageSendLogStore.insertIfPossible(receiptMessage.getWhen(), result, ContentHint.IMPLICIT, false); handleSendMessageResult(result); return result; } @@ -140,7 +140,8 @@ public class SendHelper { unidentifiedAccess, ContentHint.IMPLICIT, message, - SignalServiceMessageSender.IndividualSendEvents.EMPTY)); + SignalServiceMessageSender.IndividualSendEvents.EMPTY, + false)); } public SendMessageResult sendRetryReceipt( @@ -237,7 +238,8 @@ public class SendHelper { timestamp, messageSendLogEntry.content(), messageSendLogEntry.contentHint(), - Optional.empty())); + Optional.empty(), + messageSendLogEntry.urgent())); } final var groupId = messageSendLogEntry.groupId().get(); @@ -266,7 +268,8 @@ public class SendHelper { timestamp, contentToSend, messageSendLogEntry.contentHint(), - Optional.of(group.getGroupId().serialize()))); + Optional.of(group.getGroupId().serialize()), + messageSendLogEntry.urgent())); if (result.isSuccess()) { final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); @@ -315,6 +318,7 @@ public class SendHelper { final var messageSendLogStore = account.getMessageSendLogStore(); final AtomicLong entryId = new AtomicLong(-1); + final var urgent = true; final LegacySenderHandler legacySender = (recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendDataMessage( recipients, unidentifiedAccess, @@ -328,14 +332,16 @@ public class SendHelper { if (entryId.get() == -1) { final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), sendResult, - contentHint); + contentHint, + urgent); entryId.set(newId); } else { messageSendLogStore.addRecipientToExistingEntryIfPossible(entryId.get(), sendResult); } } }, - () -> false); + () -> false, + urgent); final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> { final var res = messageSender.sendGroupDataMessage(distId, recipients, @@ -343,10 +349,14 @@ public class SendHelper { isRecipientUpdate, contentHint, message, - SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY); + SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY, + urgent); synchronized (entryId) { if (entryId.get() == -1) { - final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), res, contentHint); + final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), + res, + contentHint, + urgent); entryId.set(newId); } else { messageSendLogStore.addRecipientToExistingEntryIfPossible(entryId.get(), res); @@ -582,13 +592,15 @@ public class SendHelper { SignalServiceDataMessage message, RecipientId recipientId ) { final var messageSendLogStore = account.getMessageSendLogStore(); + final var urgent = true; final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, message, - SignalServiceMessageSender.IndividualSendEvents.EMPTY)); - messageSendLogStore.insertIfPossible(message.getTimestamp(), result, ContentHint.RESENDABLE); + SignalServiceMessageSender.IndividualSendEvents.EMPTY, + urgent)); + messageSendLogStore.insertIfPossible(message.getTimestamp(), result, ContentHint.RESENDABLE, urgent); handleSendMessageResult(result); return result; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java index 31a4252a..67ca5cd4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java @@ -7,5 +7,5 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.util.Optional; public record MessageSendLogEntry( - Optional groupId, SignalServiceProtos.Content content, ContentHint contentHint + Optional groupId, SignalServiceProtos.Content content, ContentHint contentHint, boolean urgent ) {} diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java index c893f09c..b53c5ac0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java @@ -117,7 +117,8 @@ public class MessageSendLogStore implements AutoCloseable { return null; } final var contentHint = ContentHint.fromType(resultSet.getInt("content_hint")); - return new MessageSendLogEntry(groupId, content, contentHint); + final var urgent = true; // TODO + return new MessageSendLogEntry(groupId, content, contentHint, urgent); })) { return result.filter(Objects::nonNull) .filter(e -> !isSenderKey || e.groupId().isPresent()) @@ -131,7 +132,7 @@ public class MessageSendLogStore implements AutoCloseable { } public long insertIfPossible( - long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint + long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint, boolean urgent ) { final RecipientDevices recipientDevice = getRecipientDevices(sendMessageResult); if (recipientDevice == null) { @@ -141,11 +142,12 @@ public class MessageSendLogStore implements AutoCloseable { return insert(List.of(recipientDevice), sentTimestamp, sendMessageResult.getSuccess().getContent().get(), - contentHint); + contentHint, + urgent); } public long insertIfPossible( - long sentTimestamp, List sendMessageResults, ContentHint contentHint + long sentTimestamp, List sendMessageResults, ContentHint contentHint, boolean urgent ) { final var recipientDevices = sendMessageResults.stream() .map(this::getRecipientDevices) @@ -161,7 +163,7 @@ public class MessageSendLogStore implements AutoCloseable { .findFirst() .get(); - return insert(recipientDevices, sentTimestamp, content, contentHint); + return insert(recipientDevices, sentTimestamp, content, contentHint, urgent); } public void addRecipientToExistingEntryIfPossible(final long contentId, final SendMessageResult sendMessageResult) { @@ -272,10 +274,12 @@ public class MessageSendLogStore implements AutoCloseable { final List recipientDevices, final long sentTimestamp, final SignalServiceProtos.Content content, - final ContentHint contentHint + final ContentHint contentHint, + final boolean urgent ) { byte[] groupId = getGroupId(content); + // TODO store urgent final var sql = """ INSERT INTO %s (timestamp, group_id, content, content_hint) VALUES (?,?,?,?) diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index 347e3d81..43badfa6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -19,7 +19,8 @@ public class MessageCacheUtils { try (var f = new FileInputStream(file)) { var in = new DataInputStream(f); var version = in.readInt(); - if (version > 5) { + if (version > 6) { + // Unsupported envelope version return null; } var type = in.readInt(); @@ -63,6 +64,10 @@ public class MessageCacheUtils { if (version >= 4) { serverDeliveredTimestamp = in.readLong(); } + boolean isUrgent = true; + if (version >= 6) { + isUrgent = in.readBoolean(); + } Optional addressOptional = sourceServiceId == null ? Optional.empty() : Optional.of(new SignalServiceAddress(sourceServiceId, source)); @@ -75,14 +80,15 @@ public class MessageCacheUtils { serverReceivedTimestamp, serverDeliveredTimestamp, uuid, - destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid); + destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid, + isUrgent); } } public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { try (var f = new FileOutputStream(file)) { try (var out = new DataOutputStream(f)) { - out.writeInt(5); // version + out.writeInt(6); // version out.writeInt(envelope.getType()); out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); @@ -105,6 +111,7 @@ public class MessageCacheUtils { var uuid = envelope.getServerGuid(); out.writeUTF(uuid == null ? "" : uuid); out.writeLong(envelope.getServerDeliveredTimestamp()); + out.writeBoolean(envelope.isUrgent()); } } } From a593051512b716ed3cc42a1a7b69d49a459352ed Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 8 Aug 2022 18:12:30 +0200 Subject: [PATCH 076/833] Implement receive handling for story messages --- graalvm-config-dir/reflect-config.json | 82 ++++++++++++++ .../org/asamk/signal/manager/api/Color.java | 24 ++++ .../signal/manager/api/MessageEnvelope.java | 103 +++++++++++++++++- .../helper/IncomingMessageHandler.java | 103 ++++++++++++++---- .../asamk/signal/ReceiveMessageHandler.java | 42 +++++++ .../asamk/signal/dbus/DbusManagerImpl.java | 8 +- .../asamk/signal/json/JsonDataMessage.java | 9 +- .../signal/json/JsonMessageEnvelope.java | 3 + .../asamk/signal/json/JsonStoryContext.java | 16 +++ .../asamk/signal/json/JsonStoryMessage.java | 52 +++++++++ .../asamk/signal/json/JsonSyncMessage.java | 22 +--- .../signal/json/JsonSyncStoryMessage.java | 26 +++++ 12 files changed, 443 insertions(+), 47 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/Color.java create mode 100644 src/main/java/org/asamk/signal/json/JsonStoryContext.java create mode 100644 src/main/java/org/asamk/signal/json/JsonStoryMessage.java create mode 100644 src/main/java/org/asamk/signal/json/JsonSyncStoryMessage.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index c223851b..389c947c 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -837,6 +837,55 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonStoryContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"authorNumber","parameterTypes":[] }, + {"name":"authorUuid","parameterTypes":[] }, + {"name":"sentTimestamp","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.json.JsonStoryMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"allowsReplies","parameterTypes":[] }, + {"name":"fileAttachment","parameterTypes":[] }, + {"name":"groupId","parameterTypes":[] }, + {"name":"textAttachment","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.json.JsonStoryMessage$TextAttachment", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"backgroundColor","parameterTypes":[] }, + {"name":"backgroundGradient","parameterTypes":[] }, + {"name":"preview","parameterTypes":[] }, + {"name":"style","parameterTypes":[] }, + {"name":"text","parameterTypes":[] }, + {"name":"textBackgroundColor","parameterTypes":[] }, + {"name":"textForegroundColor","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.json.JsonStoryMessage$TextAttachment$Gradient", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"angle","parameterTypes":[] }, + {"name":"endColor","parameterTypes":[] }, + {"name":"startColor","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.json.JsonSyncDataMessage", "allDeclaredFields":true, @@ -860,6 +909,17 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonSyncStoryMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"dataMessage","parameterTypes":[] }, + {"name":"destinationNumber","parameterTypes":[] }, + {"name":"destinationUuid","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.json.JsonTypingMessage", "allDeclaredFields":true, @@ -3106,6 +3166,28 @@ {"name":"timestamp_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TextAttachment", + "fields":[ + {"name":"backgroundCase_"}, + {"name":"background_"}, + {"name":"bitField0_"}, + {"name":"preview_"}, + {"name":"textBackgroundColor_"}, + {"name":"textForegroundColor_"}, + {"name":"textStyle_"}, + {"name":"text_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TextAttachment$Gradient", + "fields":[ + {"name":"angle_"}, + {"name":"bitField0_"}, + {"name":"endColor_"}, + {"name":"startColor_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage", "fields":[ diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Color.java b/lib/src/main/java/org/asamk/signal/manager/api/Color.java new file mode 100644 index 00000000..11f7deb0 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/Color.java @@ -0,0 +1,24 @@ +package org.asamk.signal.manager.api; + +public record Color(int color) { + + public int alpha() { + return color >>> 24; + } + + public int red() { + return (color >> 16) & 0xFF; + } + + public int green() { + return (color >> 8) & 0xFF; + } + + public int blue() { + return color & 0xFF; + } + + public String toHexColor() { + return String.format("#%08x", color); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java index d2164e91..a3546a4b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java @@ -15,6 +15,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; import org.whispersystems.signalservice.api.messages.SignalServicePreview; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; +import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage; +import org.whispersystems.signalservice.api.messages.SignalServiceTextAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; import org.whispersystems.signalservice.api.messages.calls.BusyMessage; @@ -49,7 +51,8 @@ public record MessageEnvelope( Optional typing, Optional data, Optional sync, - Optional call + Optional call, + Optional story ) { public record Receipt(long when, Type type, List timestamps) { @@ -94,6 +97,7 @@ public record MessageEnvelope( public record Data( long timestamp, Optional groupContext, + Optional storyContext, Optional groupCallUpdate, Optional body, int expiresInSeconds, @@ -121,6 +125,10 @@ public record MessageEnvelope( ) { return new Data(dataMessage.getTimestamp(), dataMessage.getGroupContext().map(GroupContext::from), + dataMessage.getStoryContext() + .map((SignalServiceDataMessage.StoryContext storyContext) -> StoryContext.from(storyContext, + recipientResolver, + addressResolver)), dataMessage.getGroupCallUpdate().map(GroupCallUpdate::from), dataMessage.getBody(), dataMessage.getExpiresInSeconds(), @@ -168,6 +176,18 @@ public record MessageEnvelope( } } + public record StoryContext(RecipientAddress author, long sentTimestamp) { + + static StoryContext from( + SignalServiceDataMessage.StoryContext storyContext, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new StoryContext(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( + storyContext.getAuthorServiceId())), storyContext.getSentTimestamp()); + } + } + public record GroupCallUpdate(String eraId) { static GroupCallUpdate from(SignalServiceDataMessage.GroupCallUpdate groupCallUpdate) { @@ -519,7 +539,8 @@ public record MessageEnvelope( long expirationStartTimestamp, Optional destination, Set recipients, - Optional message + Optional message, + Optional story ) { static Sent from( @@ -537,7 +558,8 @@ public record MessageEnvelope( .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) .collect(Collectors.toSet()), sentMessage.getDataMessage() - .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider))); + .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)), + sentMessage.getStoryMessage().map(s -> Story.from(s, fileProvider))); } } @@ -757,6 +779,75 @@ public record MessageEnvelope( } } + public record Story( + boolean allowsReplies, + Optional groupId, + Optional fileAttachment, + Optional textAttachment + ) { + + public static Story from( + SignalServiceStoryMessage storyMessage, final AttachmentFileProvider fileProvider + ) { + return new Story(storyMessage.getAllowsReplies().orElse(false), + storyMessage.getGroupContext().map(c -> GroupUtils.getGroupIdV2(c.getMasterKey())), + storyMessage.getFileAttachment().map(f -> Data.Attachment.from(f, fileProvider)), + storyMessage.getTextAttachment().map(t -> TextAttachment.from(t, fileProvider))); + } + + public record TextAttachment( + Optional text, + Optional