X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/4f8da7819e080382ffea7754e4fa5f29177d102f..c1775913b9c54c84b45154aa841a0ba475c7c9fd:/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index f87eded6..574f68c6 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -1,9 +1,10 @@ package org.asamk.signal.dbus; import org.asamk.Signal; -import org.asamk.signal.DbusConfig; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.api.AlreadyReceivingException; import org.asamk.signal.manager.api.AttachmentInvalidException; +import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Contact; import org.asamk.signal.manager.api.Device; @@ -17,15 +18,19 @@ import org.asamk.signal.manager.api.GroupSendingNotAllowedException; import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.IdentityVerificationCode; import org.asamk.signal.manager.api.InactiveGroupLinkException; +import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.InvalidUsernameException; import org.asamk.signal.manager.api.LastGroupAdminException; import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.NotAGroupMemberException; import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.PinLockedException; +import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.ReceiveConfig; import org.asamk.signal.manager.api.Recipient; import org.asamk.signal.manager.api.RecipientAddress; @@ -33,17 +38,22 @@ import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.StickerPack; +import org.asamk.signal.manager.api.StickerPackId; import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; +import org.asamk.signal.manager.api.TrustLevel; 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.api.UsernameLinkUrl; +import org.asamk.signal.manager.api.UsernameStatus; import org.freedesktop.dbus.DBusMap; import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.interfaces.DBusInterface; import org.freedesktop.dbus.interfaces.DBusSigHandler; import org.freedesktop.dbus.types.Variant; @@ -82,13 +92,16 @@ public class DbusManagerImpl implements Manager { private final Set weakHandlers = new HashSet<>(); private final Set messageHandlers = new HashSet<>(); private final List closedListeners = new ArrayList<>(); + private final String busname; private DBusSigHandler dbusMsgHandler; + private DBusSigHandler dbusEditMsgHandler; private DBusSigHandler dbusRcptHandler; private DBusSigHandler dbusSyncHandler; - public DbusManagerImpl(final Signal signal, DBusConnection connection) { + public DbusManagerImpl(final Signal signal, DBusConnection connection, final String busname) { this.signal = signal; this.connection = connection; + this.busname = busname; } @Override @@ -112,10 +125,22 @@ public class DbusManagerImpl implements Manager { } @Override - public void updateAccountAttributes(final String deviceName) throws IOException { + public Map getUsernameStatus(final Set usernames) { + throw new UnsupportedOperationException(); + } + + @Override + public void updateAccountAttributes( + final String deviceName, + final Boolean unrestrictedUnidentifiedSender, + final Boolean discoverableByNumber, + final Boolean numberSharing + ) throws IOException { if (deviceName != null) { final var devicePath = signal.getThisDevice(); getRemoteObject(devicePath, Signal.Device.class).Set("org.asamk.Signal.Device", "Name", deviceName); + } else { + throw new UnsupportedOperationException(); } } @@ -130,7 +155,7 @@ public class DbusManagerImpl implements Manager { } @Override - public void updateConfiguration(Configuration newConfiguration) throws IOException { + public void updateConfiguration(Configuration newConfiguration) { final var configuration = getRemoteObject(new DBusPath(signal.getObjectPath() + "/Configuration"), Signal.Configuration.class); newConfiguration.readReceipts() @@ -156,7 +181,17 @@ public class DbusManagerImpl implements Manager { } @Override - public String setUsername(final String username) throws IOException, InvalidUsernameException { + public String getUsername() { + throw new UnsupportedOperationException(); + } + + @Override + public UsernameLinkUrl getUsernameLink() { + throw new UnsupportedOperationException(); + } + + @Override + public void setUsername(final String username) throws IOException, InvalidUsernameException { throw new UnsupportedOperationException(); } @@ -165,6 +200,20 @@ public class DbusManagerImpl implements Manager { throw new UnsupportedOperationException(); } + @Override + public void startChangeNumber( + final String newNumber, final boolean voiceVerification, final String captcha + ) throws RateLimitException, IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { + throw new UnsupportedOperationException(); + } + + @Override + public void finishChangeNumber( + final String newNumber, final String verificationCode, final String pin + ) throws IncorrectPinException, PinLockedException, IOException { + throw new UnsupportedOperationException(); + } + @Override public void unregister() throws IOException { signal.unregister(); @@ -224,7 +273,7 @@ public class DbusManagerImpl implements Manager { public SendGroupMessageResults quitGroup( final GroupId groupId, final Set groupAdmins ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException { - if (groupAdmins.size() > 0) { + if (!groupAdmins.isEmpty()) { throw new UnsupportedOperationException(); } final var group = getRemoteObject(signal.getGroup(groupId.serialize()), Signal.Group.class); @@ -321,8 +370,13 @@ public class DbusManagerImpl implements Manager { @Override public Pair joinGroup(final GroupInviteLinkUrl inviteLinkUrl) throws IOException, InactiveGroupLinkException { - final var newGroupId = signal.joinGroup(inviteLinkUrl.getUrl()); - return new Pair<>(GroupId.unknownVersion(newGroupId), new SendGroupMessageResults(0, List.of())); + try { + final var newGroupId = signal.joinGroup(inviteLinkUrl.getUrl()); + return new Pair<>(GroupId.unknownVersion(newGroupId), new SendGroupMessageResults(0, List.of())); + } catch (DBusExecutionException e) { + throw new IOException("Failed to join group: " + e.getMessage() + " (" + e.getClass().getSimpleName() + ")", + e); + } } @Override @@ -359,7 +413,7 @@ public class DbusManagerImpl implements Manager { @Override public SendMessageResults sendMessage( - final Message message, final Set recipients + final Message message, final Set recipients, final boolean notifySelf ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { return handleMessage(recipients, numbers -> signal.sendMessage(message.messageText(), message.attachments(), numbers), @@ -425,6 +479,18 @@ public class DbusManagerImpl implements Manager { return new SendMessageResults(0, Map.of()); } + @Override + public SendMessageResults sendMessageRequestResponse( + final MessageEnvelope.Sync.MessageRequestResponse.Type type, + final Set recipientIdentifiers + ) { + throw new UnsupportedOperationException(); + } + + public void hideRecipient(final RecipientIdentifier.Single recipient) { + throw new UnsupportedOperationException(); + } + @Override public void deleteRecipient(final RecipientIdentifier.Single recipient) { signal.deleteRecipient(recipient.getIdentifier()); @@ -481,6 +547,11 @@ public class DbusManagerImpl implements Manager { } } + @Override + public void installStickerPack(final StickerPackUrl url) throws IOException { + throw new UnsupportedOperationException(); + } + @Override public List getStickerPacks() { throw new UnsupportedOperationException(); @@ -497,7 +568,7 @@ public class DbusManagerImpl implements Manager { if (isWeakListener) { weakHandlers.add(handler); } else { - if (messageHandlers.size() == 0) { + if (messageHandlers.isEmpty()) { installMessageHandlers(); } messageHandlers.add(handler); @@ -510,7 +581,7 @@ public class DbusManagerImpl implements Manager { synchronized (messageHandlers) { weakHandlers.remove(handler); messageHandlers.remove(handler); - if (messageHandlers.size() == 0) { + if (messageHandlers.isEmpty()) { uninstallMessageHandlers(); } } @@ -519,14 +590,21 @@ public class DbusManagerImpl implements Manager { @Override public boolean isReceiving() { synchronized (messageHandlers) { - return messageHandlers.size() > 0; + return !messageHandlers.isEmpty(); } } + private Thread receiveThread; + @Override public void receiveMessages( Optional timeout, Optional maxMessages, ReceiveMessageHandler handler - ) throws IOException { + ) throws IOException, AlreadyReceivingException { + if (receiveThread != null) { + throw new AlreadyReceivingException("Already receiving message."); + } + receiveThread = Thread.currentThread(); + final var remainingMessages = new AtomicInteger(maxMessages.orElse(-1)); final var lastMessage = new AtomicLong(System.currentTimeMillis()); final var thread = Thread.currentThread(); @@ -552,6 +630,7 @@ public class DbusManagerImpl implements Manager { } Thread.sleep(sleepTimeRemaining); } catch (InterruptedException ignored) { + break; } } } else { @@ -564,6 +643,14 @@ public class DbusManagerImpl implements Manager { } removeReceiveHandler(receiveHandler); + receiveThread = null; + } + + @Override + public void stopReceiveMessages() { + if (receiveThread != null) { + receiveThread.interrupt(); + } } @Override @@ -597,15 +684,29 @@ public class DbusManagerImpl implements Manager { return null; } final var contactName = signal.getContactName(n); - if (onlyContacts && contactName.length() == 0) { + if (onlyContacts && contactName.isEmpty()) { return null; } if (name.isPresent() && !name.get().equals(contactName)) { return null; } return Recipient.newBuilder() - .withAddress(new RecipientAddress(null, n)) - .withContact(new Contact(contactName, null, null, 0, contactBlocked, false, false)) + .withAddress(new RecipientAddress(n)) + .withContact(new Contact(contactName, + null, + null, + null, + null, + null, + null, + 0, + 0, + false, + contactBlocked, + false, + false, + false, + null)) .build(); }).filter(Objects::nonNull).toList(); } @@ -631,19 +732,19 @@ public class DbusManagerImpl implements Manager { (String) group.get("Description").getValue(), GroupInviteLinkUrl.fromUri((String) group.get("GroupInviteLink").getValue()), ((List) group.get("Members").getValue()).stream() - .map(m -> new RecipientAddress(null, m)) + .map(m -> new RecipientAddress(m)) .collect(Collectors.toSet()), ((List) group.get("PendingMembers").getValue()).stream() - .map(m -> new RecipientAddress(null, m)) + .map(m -> new RecipientAddress(m)) .collect(Collectors.toSet()), ((List) group.get("RequestingMembers").getValue()).stream() - .map(m -> new RecipientAddress(null, m)) + .map(m -> new RecipientAddress(m)) .collect(Collectors.toSet()), ((List) group.get("Admins").getValue()).stream() - .map(m -> new RecipientAddress(null, m)) + .map(m -> new RecipientAddress(m)) .collect(Collectors.toSet()), ((List) group.get("Banned").getValue()).stream() - .map(m -> new RecipientAddress(null, m)) + .map(m -> new RecipientAddress(m)) .collect(Collectors.toSet()), (boolean) group.get("IsBlocked").getValue(), (int) group.get("MessageExpirationTimer").getValue(), @@ -659,12 +760,26 @@ public class DbusManagerImpl implements Manager { @Override public List getIdentities() { - throw new UnsupportedOperationException(); + final var identities = signal.listIdentities(); + return identities.stream().map(Signal.StructIdentity::getObjectPath).map(this::getIdentity).toList(); } @Override public List getIdentities(final RecipientIdentifier.Single recipient) { - throw new UnsupportedOperationException(); + final var path = signal.getIdentity(recipient.getIdentifier()); + return List.of(getIdentity(path)); + } + + private Identity getIdentity(final DBusPath identityPath) { + final var group = getRemoteObject(identityPath, Signal.Identity.class).GetAll("org.asamk.Signal.Identity"); + final var aci = (String) group.get("Uuid").getValue(); + final var number = (String) group.get("Number").getValue(); + return new Identity(new RecipientAddress(aci, null, number, null), + (byte[]) group.get("Fingerprint").getValue(), + (String) group.get("SafetyNumber").getValue(), + (byte[]) group.get("ScannableSafetyNumber").getValue(), + TrustLevel.valueOf((String) group.get("TrustLevel").getValue()), + (Long) group.get("AddedDate").getValue()); } @Override @@ -696,7 +811,7 @@ public class DbusManagerImpl implements Manager { this.notify(); } synchronized (messageHandlers) { - if (messageHandlers.size() > 0) { + if (!messageHandlers.isEmpty()) { uninstallMessageHandlers(); } weakHandlers.clear(); @@ -720,7 +835,7 @@ public class DbusManagerImpl implements Manager { .map(RecipientIdentifier.Single.class::cast) .map(RecipientIdentifier.Single::getIdentifier) .toList(); - if (singleRecipients.size() > 0) { + if (!singleRecipients.isEmpty()) { timestamp = recipientsHandler.apply(singleRecipients); } @@ -744,7 +859,7 @@ public class DbusManagerImpl implements Manager { private T getRemoteObject(final DBusPath path, final Class type) { try { - return connection.getRemoteObject(DbusConfig.getBusname(), path.getPath(), type); + return connection.getRemoteObject(busname, path.getPath(), type); } catch (DBusException e) { throw new AssertionError(e); } @@ -754,8 +869,7 @@ public class DbusManagerImpl implements Manager { try { this.dbusMsgHandler = messageReceived -> { final var extras = messageReceived.getExtras(); - final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(null, - messageReceived.getSender())), + final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(messageReceived.getSender())), 0, messageReceived.getTimestamp(), 0, @@ -784,7 +898,7 @@ public class DbusManagerImpl implements Manager { Optional.empty(), Optional.empty(), List.of(), - List.of(), + getMentions(extras), List.of(), List.of())), Optional.empty(), @@ -794,6 +908,48 @@ public class DbusManagerImpl implements Manager { notifyMessageHandlers(envelope); }; connection.addSigHandler(Signal.MessageReceivedV2.class, signal, this.dbusMsgHandler); + this.dbusEditMsgHandler = messageReceived -> { + final var extras = messageReceived.getExtras(); + final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(messageReceived.getSender())), + 0, + messageReceived.getTimestamp(), + 0, + 0, + false, + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.of(new MessageEnvelope.Edit(messageReceived.getTargetSentTimestamp(), + new MessageEnvelope.Data(messageReceived.getTimestamp(), + messageReceived.getGroupId().length > 0 + ? Optional.of(new MessageEnvelope.Data.GroupContext(GroupId.unknownVersion( + messageReceived.getGroupId()), false, 0)) + : Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.of(messageReceived.getMessage()), + 0, + false, + false, + false, + false, + false, + Optional.empty(), + Optional.empty(), + Optional.empty(), + getAttachments(extras), + Optional.empty(), + Optional.empty(), + List.of(), + getMentions(extras), + List.of(), + List.of()))), + Optional.empty(), + Optional.empty(), + Optional.empty()); + notifyMessageHandlers(envelope); + }; + connection.addSigHandler(Signal.EditMessageReceived.class, signal, this.dbusEditMsgHandler); this.dbusRcptHandler = receiptReceived -> { final var type = switch (receiptReceived.getReceiptType()) { @@ -802,8 +958,7 @@ public class DbusManagerImpl implements Manager { case "delivery" -> MessageEnvelope.Receipt.Type.DELIVERY; default -> MessageEnvelope.Receipt.Type.UNKNOWN; }; - final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(null, - receiptReceived.getSender())), + final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(receiptReceived.getSender())), 0, receiptReceived.getTimestamp(), 0, @@ -824,8 +979,7 @@ public class DbusManagerImpl implements Manager { this.dbusSyncHandler = syncReceived -> { final var extras = syncReceived.getExtras(); - final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(null, - syncReceived.getSource())), + final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(syncReceived.getSource())), 0, syncReceived.getTimestamp(), 0, @@ -839,7 +993,7 @@ public class DbusManagerImpl implements Manager { syncReceived.getTimestamp(), syncReceived.getDestination().isEmpty() ? Optional.empty() - : Optional.of(new RecipientAddress(null, syncReceived.getDestination())), + : Optional.of(new RecipientAddress(syncReceived.getDestination())), Set.of(), Optional.of(new MessageEnvelope.Data(syncReceived.getTimestamp(), syncReceived.getGroupId().length > 0 @@ -862,7 +1016,7 @@ public class DbusManagerImpl implements Manager { Optional.empty(), Optional.empty(), List.of(), - List.of(), + getMentions(extras), List.of(), List.of())), Optional.empty(), @@ -880,7 +1034,7 @@ public class DbusManagerImpl implements Manager { }; connection.addSigHandler(Signal.SyncMessageReceivedV2.class, signal, this.dbusSyncHandler); } catch (DBusException e) { - e.printStackTrace(); + throw new RuntimeException(e); } signal.subscribeReceive(); } @@ -896,10 +1050,11 @@ public class DbusManagerImpl implements Manager { try { signal.unsubscribeReceive(); connection.removeSigHandler(Signal.MessageReceivedV2.class, signal, this.dbusMsgHandler); + connection.removeSigHandler(Signal.EditMessageReceived.class, signal, this.dbusEditMsgHandler); connection.removeSigHandler(Signal.ReceiptReceivedV2.class, signal, this.dbusRcptHandler); connection.removeSigHandler(Signal.SyncMessageReceivedV2.class, signal, this.dbusSyncHandler); } catch (DBusException e) { - e.printStackTrace(); + throw new RuntimeException(e); } } @@ -930,11 +1085,44 @@ public class DbusManagerImpl implements Manager { }).toList(); } + private List getMentions(final Map> extras) { + if (!extras.containsKey("mentions")) { + return List.of(); + } + + final List>> mentions = getValue(extras, "mentions"); + return mentions.stream() + .map(a -> new MessageEnvelope.Data.Mention(new RecipientAddress(this.getValue(a, "recipient")), + getValue(a, "start"), + getValue(a, "length"))) + .toList(); + } + @Override public InputStream retrieveAttachment(final String id) throws IOException { throw new UnsupportedOperationException(); } + @Override + public InputStream retrieveContactAvatar(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + throw new UnsupportedOperationException(); + } + + @Override + public InputStream retrieveProfileAvatar(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + throw new UnsupportedOperationException(); + } + + @Override + public InputStream retrieveGroupAvatar(final GroupId groupId) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public InputStream retrieveSticker(final StickerPackId stickerPackId, final int stickerId) throws IOException { + throw new UnsupportedOperationException(); + } + @SuppressWarnings("unchecked") private T getValue( final Map> stringVariantMap, final String field