X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/05abb3f9f6294677d2de97a9d88c8e9de04c10ec..11b3758416ea395d04b8538a28be07c240749767:/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 6b22029b..5e8fd432 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -4,32 +4,50 @@ import org.asamk.Signal; import org.asamk.signal.BaseConfig; import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.NotMasterDeviceException; +import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.Message; +import org.asamk.signal.manager.api.RecipientIdentifier; +import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; 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.identities.IdentityInfo; import org.asamk.signal.util.ErrorUtils; +import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.asamk.signal.util.Util.getLegacyIdentifier; + public class DbusSignalImpl implements Signal { private final Manager m; + private final String objectPath; - public DbusSignalImpl(final Manager m) { + public DbusSignalImpl(final Manager m, final String objectPath) { this.m = m; + this.objectPath = objectPath; } @Override @@ -39,7 +57,7 @@ public class DbusSignalImpl implements Signal { @Override public String getObjectPath() { - return null; + return objectPath; } @Override @@ -49,57 +67,22 @@ public class DbusSignalImpl implements Signal { return sendMessage(message, attachments, recipients); } - private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException { - var error = ErrorUtils.getErrorMessageFromSendMessageResult(result); - - if (error == null) { - return; - } - - final var message = timestamp + "\nFailed to send message:\n" + error + '\n'; - - if (result.getIdentityFailure() != null) { - throw new Error.UntrustedIdentity(message); - } else { - throw new Error.Failure(message); - } - } - - private static void checkSendMessageResults( - long timestamp, List results - ) throws DBusExecutionException { - if (results.size() == 1) { - checkSendMessageResult(timestamp, results.get(0)); - return; - } - - var errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results); - if (errors.size() == 0) { - return; - } - - var message = new StringBuilder(); - message.append(timestamp).append('\n'); - message.append("Failed to send (some) messages:\n"); - for (var error : errors) { - message.append(error).append('\n'); - } - - throw new Error.Failure(message.toString()); - } - @Override public long sendMessage(final String message, final List attachments, final List recipients) { try { - final var results = m.sendMessage(message, attachments, recipients); - checkSendMessageResults(results.first(), results.second()); - return results.first(); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); + final var results = m.sendMessage(new Message(message, attachments), + getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() + .map(RecipientIdentifier.class::cast) + .collect(Collectors.toSet())); + + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (AttachmentInvalidException e) { throw new Error.AttachmentInvalid(e.getMessage()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { + throw new Error.GroupNotFound(e.getMessage()); } } @@ -117,13 +100,16 @@ public class DbusSignalImpl implements Signal { final long targetSentTimestamp, final List recipients ) { try { - final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, recipients); - checkSendMessageResults(results.first(), results.second()); - return results.first(); + final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, + getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() + .map(RecipientIdentifier.class::cast) + .collect(Collectors.toSet())); + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { + throw new Error.GroupNotFound(e.getMessage()); } } @@ -132,19 +118,24 @@ public class DbusSignalImpl implements Signal { final long targetSentTimestamp, final byte[] groupId ) { try { - final var results = m.sendGroupRemoteDeleteMessage(targetSentTimestamp, GroupId.unknownVersion(groupId)); - checkSendMessageResults(results.first(), results.second()); - return results.first(); + final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, + Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (GroupNotFoundException | NotAGroupMemberException e) { + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new Error.GroupNotFound(e.getMessage()); } } @Override public long sendMessageReaction( - final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final String recipient + final String emoji, + final boolean remove, + final String targetAuthor, + final long targetSentTimestamp, + final String recipient ) { var recipients = new ArrayList(1); recipients.add(recipient); @@ -153,16 +144,59 @@ public class DbusSignalImpl implements Signal { @Override public long sendMessageReaction( - final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final List recipients + final String emoji, + final boolean remove, + final String targetAuthor, + final long targetSentTimestamp, + final List recipients ) { try { - final var results = m.sendMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, recipients); - checkSendMessageResults(results.first(), results.second()); - return results.first(); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); + final var results = m.sendMessageReaction(emoji, + remove, + getSingleRecipientIdentifier(targetAuthor, m.getUsername()), + targetSentTimestamp, + getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() + .map(RecipientIdentifier.class::cast) + .collect(Collectors.toSet())); + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (IOException e) { throw new Error.Failure(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { + throw new Error.GroupNotFound(e.getMessage()); + } + } + + @Override + public void sendTyping( + final String recipient, final boolean stop + ) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity { + try { + var recipients = new ArrayList(1); + recipients.add(recipient); + m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START, + getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() + .map(RecipientIdentifier.class::cast) + .collect(Collectors.toSet())); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { + throw new Error.GroupNotFound(e.getMessage()); + } catch (UntrustedIdentityException e) { + throw new Error.UntrustedIdentity(e.getMessage()); + } + } + + @Override + public void sendReadReceipt( + final String recipient, final List timestamps + ) throws Error.Failure, Error.UntrustedIdentity { + try { + m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getUsername()), timestamps); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (UntrustedIdentityException e) { + throw new Error.UntrustedIdentity(e.getMessage()); } } @@ -171,37 +205,39 @@ public class DbusSignalImpl implements Signal { final String message, final List attachments ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { try { - final var results = m.sendSelfMessage(message, attachments); - checkSendMessageResult(results.first(), results.second()); - return results.first(); + final var results = m.sendMessage(new Message(message, attachments), + Set.of(new RecipientIdentifier.NoteToSelf())); + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (AttachmentInvalidException e) { throw new Error.AttachmentInvalid(e.getMessage()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { + throw new Error.GroupNotFound(e.getMessage()); } } @Override public void sendEndSessionMessage(final List recipients) { try { - final var results = m.sendEndSessionMessage(recipients); - checkSendMessageResults(results.first(), results.second()); + final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getUsername())); + checkSendMessageResults(results.getTimestamp(), results.getResults()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); } } @Override public long sendGroupMessage(final String message, final List attachments, final byte[] groupId) { try { - var results = m.sendGroupMessage(message, attachments, GroupId.unknownVersion(groupId)); - checkSendMessageResults(results.first(), results.second()); - return results.first(); + var results = m.sendMessage(new Message(message, attachments), + Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (GroupNotFoundException | NotAGroupMemberException e) { + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new Error.GroupNotFound(e.getMessage()); } catch (AttachmentInvalidException e) { throw new Error.AttachmentInvalid(e.getMessage()); @@ -210,17 +246,23 @@ public class DbusSignalImpl implements Signal { @Override public long sendGroupMessageReaction( - final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final byte[] groupId + final String emoji, + final boolean remove, + final String targetAuthor, + final long targetSentTimestamp, + final byte[] groupId ) { try { - final var results = m.sendGroupMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, GroupId.unknownVersion(groupId)); - checkSendMessageResults(results.first(), results.second()); - return results.first(); + final var results = m.sendMessageReaction(emoji, + remove, + getSingleRecipientIdentifier(targetAuthor, m.getUsername()), + targetSentTimestamp, + Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); + checkSendMessageResults(results.getTimestamp(), results.getResults()); + return results.getTimestamp(); } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); - } catch (GroupNotFoundException | NotAGroupMemberException e) { + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new Error.GroupNotFound(e.getMessage()); } } @@ -229,37 +271,39 @@ public class DbusSignalImpl implements Signal { // the profile name @Override public String getContactName(final String number) { - try { - return m.getContactOrProfileName(number); - } catch (Exception e) { - throw new Error.InvalidNumber(e.getMessage()); - } + return m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getUsername())); } @Override public void setContactName(final String number, final String name) { try { - m.setContactName(number, name); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); + m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name); + } catch (NotMasterDeviceException e) { + throw new Error.Failure("This command doesn't work on linked devices."); + } catch (UnregisteredUserException e) { + throw new Error.Failure("Contact is not registered."); } } @Override public void setContactBlocked(final String number, final boolean blocked) { try { - m.setContactBlocked(number, blocked); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); + m.setContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()), blocked); + } catch (NotMasterDeviceException e) { + throw new Error.Failure("This command doesn't work on linked devices."); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); } } @Override public void setGroupBlocked(final byte[] groupId, final boolean blocked) { try { - m.setGroupBlocked(GroupId.unknownVersion(groupId), blocked); + m.setGroupBlocked(getGroupId(groupId), blocked); } catch (GroupNotFoundException e) { throw new Error.GroupNotFound(e.getMessage()); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); } } @@ -275,7 +319,7 @@ public class DbusSignalImpl implements Signal { @Override public String getGroupName(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); + var group = m.getGroup(getGroupId(groupId)); if (group == null) { return ""; } else { @@ -285,14 +329,14 @@ public class DbusSignalImpl implements Signal { @Override public List getGroupMembers(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); + var group = m.getGroup(getGroupId(groupId)); if (group == null) { return List.of(); } else { return group.getMembers() .stream() .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) + .map(Util::getLegacyIdentifier) .collect(Collectors.toList()); } } @@ -306,24 +350,38 @@ public class DbusSignalImpl implements Signal { if (name.isEmpty()) { name = null; } - if (members.isEmpty()) { - members = null; - } if (avatar.isEmpty()) { avatar = null; } - final var results = m.updateGroup(groupId == null ? null : GroupId.unknownVersion(groupId), - name, - members, - avatar == null ? null : new File(avatar)); - checkSendMessageResults(0, results.second()); - return results.first().serialize(); + final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getUsername()); + if (groupId == null) { + final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar)); + checkSendMessageResults(results.second().getTimestamp(), results.second().getResults()); + return results.first().serialize(); + } else { + final var results = m.updateGroup(getGroupId(groupId), + name, + null, + memberIdentifiers, + null, + null, + null, + false, + null, + null, + null, + avatar == null ? null : new File(avatar), + null, + null); + if (results != null) { + checkSendMessageResults(results.getTimestamp(), results.getResults()); + } + return groupId; + } } catch (IOException e) { throw new Error.Failure(e.getMessage()); - } catch (GroupNotFoundException | NotAGroupMemberException e) { + } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new Error.GroupNotFound(e.getMessage()); - } catch (InvalidNumberException e) { - throw new Error.InvalidNumber(e.getMessage()); } catch (AttachmentInvalidException e) { throw new Error.AttachmentInvalid(e.getMessage()); } @@ -349,7 +407,7 @@ public class DbusSignalImpl implements Signal { Optional avatarFile = removeAvatar ? Optional.absent() : avatarPath == null ? null : Optional.of(new File(avatarPath)); - m.setProfile(name, about, aboutEmoji, avatarFile); + m.setProfile(name, null, about, aboutEmoji, avatarFile); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } @@ -366,8 +424,10 @@ public class DbusSignalImpl implements Signal { // all numbers the system knows @Override public List listNumbers() { - return Stream.concat(m.getIdentities().stream().map(i -> i.getAddress().getNumber().orNull()), - m.getContacts().stream().map(c -> c.number)) + return Stream.concat(m.getIdentities().stream().map(IdentityInfo::getRecipientId), + m.getContacts().stream().map(Pair::first)) + .map(m::resolveSignalServiceAddress) + .map(a -> a.getNumber().orNull()) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); @@ -379,16 +439,17 @@ public class DbusSignalImpl implements Signal { var numbers = new ArrayList(); var contacts = m.getContacts(); for (var c : contacts) { - if (c.name != null && c.name.equals(name)) { - numbers.add(c.number); + if (name.equals(c.second().getName())) { + numbers.add(getLegacyIdentifier(m.resolveSignalServiceAddress(c.first()))); } } // Try profiles if no contact name was found for (var identity : m.getIdentities()) { - final var address = identity.getAddress(); + final var recipientId = identity.getRecipientId(); + final var address = m.resolveSignalServiceAddress(recipientId); var number = address.getNumber().orNull(); if (number != null) { - var profile = m.getRecipientProfile(address); + var profile = m.getRecipientProfile(recipientId); if (profile != null && profile.getDisplayName().equals(name)) { numbers.add(number); } @@ -399,24 +460,25 @@ public class DbusSignalImpl implements Signal { @Override public void quitGroup(final byte[] groupId) { - var group = GroupId.unknownVersion(groupId); + var group = getGroupId(groupId); try { - m.sendQuitGroupMessage(group); + m.quitGroup(group, Set.of()); } catch (GroupNotFoundException | NotAGroupMemberException e) { throw new Error.GroupNotFound(e.getMessage()); - } catch (IOException e) { + } catch (IOException | LastGroupAdminException e) { throw new Error.Failure(e.getMessage()); } } @Override - public void joinGroup(final String groupLink) { + public byte[] joinGroup(final String groupLink) { try { final var linkUrl = GroupInviteLinkUrl.fromUri(groupLink); if (linkUrl == null) { throw new Error.Failure("Group link is invalid:"); } - m.joinGroup(linkUrl); + final var result = m.joinGroup(linkUrl); + return result.first().serialize(); } catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupLinkNotActiveException e) { throw new Error.Failure("Group link is invalid: " + e.getMessage()); } catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) { @@ -428,18 +490,12 @@ public class DbusSignalImpl implements Signal { @Override public boolean isContactBlocked(final String number) { - var contacts = m.getContacts(); - for (var c : contacts) { - if (c.number.equals(number)) { - return c.blocked; - } - } - return false; + return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getUsername())); } @Override public boolean isGroupBlocked(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); + var group = m.getGroup(getGroupId(groupId)); if (group == null) { return false; } else { @@ -449,11 +505,102 @@ public class DbusSignalImpl implements Signal { @Override public boolean isMember(final byte[] groupId) { - var group = m.getGroup(GroupId.unknownVersion(groupId)); + var group = m.getGroup(getGroupId(groupId)); if (group == null) { return false; } else { - return group.isMember(m.getSelfAddress()); + return group.isMember(m.getSelfRecipientId()); + } + } + + private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException { + var error = ErrorUtils.getErrorMessageFromSendMessageResult(result); + + if (error == null) { + return; + } + + final var message = timestamp + "\nFailed to send message:\n" + error + '\n'; + + if (result.getIdentityFailure() != null) { + throw new Error.UntrustedIdentity(message); + } else { + throw new Error.Failure(message); + } + } + + private static void checkSendMessageResults( + long timestamp, Map> results + ) throws DBusExecutionException { + final var sendMessageResults = results.values().stream().findFirst(); + if (results.size() == 1 && sendMessageResults.get().size() == 1) { + checkSendMessageResult(timestamp, sendMessageResults.get().stream().findFirst().get()); + return; + } + + var errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results); + if (errors.size() == 0) { + return; + } + + var message = new StringBuilder(); + message.append(timestamp).append('\n'); + message.append("Failed to send (some) messages:\n"); + for (var error : errors) { + message.append(error).append('\n'); + } + + throw new Error.Failure(message.toString()); + } + + private static void checkSendMessageResults( + long timestamp, Collection results + ) throws DBusExecutionException { + if (results.size() == 1) { + checkSendMessageResult(timestamp, results.stream().findFirst().get()); + return; + } + + var errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results); + if (errors.size() == 0) { + return; + } + + var message = new StringBuilder(); + message.append(timestamp).append('\n'); + message.append("Failed to send (some) messages:\n"); + for (var error : errors) { + message.append(error).append('\n'); + } + + throw new Error.Failure(message.toString()); + } + + private static Set getSingleRecipientIdentifiers( + final Collection recipientStrings, final String localNumber + ) throws DBusExecutionException { + final var identifiers = new HashSet(); + for (var recipientString : recipientStrings) { + identifiers.add(getSingleRecipientIdentifier(recipientString, localNumber)); + } + return identifiers; + } + + private static RecipientIdentifier.Single getSingleRecipientIdentifier( + final String recipientString, final String localNumber + ) throws DBusExecutionException { + try { + return RecipientIdentifier.Single.fromString(recipientString, localNumber); + } catch (InvalidNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); + } + } + + private static GroupId getGroupId(byte[] groupId) throws DBusExecutionException { + try { + return GroupId.unknownVersion(groupId); + } catch (Throwable e) { + throw new Error.InvalidGroupId("Invalid group id: " + e.getMessage()); } } }