X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/e22cc457ae91e22deb54373078bef7e8cd22edcb..53f47d42fc30a86a9bb6cd08f4678a756f4a4aaf:/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 c8dd1d45..29889c3d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -2,11 +2,8 @@ package org.asamk.signal.dbus; import org.asamk.Signal; import org.asamk.signal.DbusConfig; -import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.NotMasterDeviceException; -import org.asamk.signal.manager.StickerPackInvalidException; -import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Group; @@ -15,12 +12,17 @@ import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.MessageEnvelope; +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.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResults; +import org.asamk.signal.manager.api.StickerPack; +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.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -43,18 +45,21 @@ import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; 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.UUID; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This class implements the Manager interface using the DBus Signal interface, where possible. @@ -65,7 +70,9 @@ public class DbusManagerImpl implements Manager { private final Signal signal; private final DBusConnection connection; + private final Set weakHandlers = new HashSet<>(); private final Set messageHandlers = new HashSet<>(); + private final List closedListeners = new ArrayList<>(); private DBusSigHandler dbusMsgHandler; private DBusSigHandler dbusRcptHandler; private DBusSigHandler dbusSyncHandler; @@ -81,19 +88,16 @@ public class DbusManagerImpl implements Manager { } @Override - public void checkAccountState() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Map> areUsersRegistered(final Set numbers) throws IOException { + public Map getUserStatus(final Set numbers) throws IOException { final var numbersList = new ArrayList<>(numbers); final var registered = signal.isRegistered(numbersList); - final var result = new HashMap>(); + final var result = new HashMap(); for (var i = 0; i < numbersList.size(); i++) { result.put(numbersList.get(i), - new Pair<>(numbersList.get(i), registered.get(i) ? RecipientAddress.UNKNOWN_UUID : null)); + new UserStatus(numbersList.get(i), + registered.get(i) ? RecipientAddress.UNKNOWN_UUID : null, + false)); } return result; } @@ -108,12 +112,28 @@ public class DbusManagerImpl implements Manager { @Override public Configuration getConfiguration() { - throw new UnsupportedOperationException(); + final var configuration = getRemoteObject(new DBusPath(signal.getObjectPath() + "/Configuration"), + Signal.Configuration.class).GetAll("org.asamk.Signal.Configuration"); + return new Configuration(Optional.of((Boolean) configuration.get("ReadReceipts").getValue()), + Optional.of((Boolean) configuration.get("UnidentifiedDeliveryIndicators").getValue()), + Optional.of((Boolean) configuration.get("TypingIndicators").getValue()), + Optional.of((Boolean) configuration.get("LinkPreviews").getValue())); } @Override - public void updateConfiguration(Configuration configuration) throws IOException { - throw new UnsupportedOperationException(); + public void updateConfiguration(Configuration newConfiguration) throws IOException { + final var configuration = getRemoteObject(new DBusPath(signal.getObjectPath() + "/Configuration"), + Signal.Configuration.class); + newConfiguration.readReceipts() + .ifPresent(v -> configuration.Set("org.asamk.Signal.Configuration", "ReadReceipts", v)); + newConfiguration.unidentifiedDeliveryIndicators() + .ifPresent(v -> configuration.Set("org.asamk.Signal.Configuration", + "UnidentifiedDeliveryIndicators", + v)); + newConfiguration.typingIndicators() + .ifPresent(v -> configuration.Set("org.asamk.Signal.Configuration", "TypingIndicators", v)); + newConfiguration.linkPreviews() + .ifPresent(v -> configuration.Set("org.asamk.Signal.Configuration", "LinkPreviews", v)); } @Override @@ -129,17 +149,17 @@ public class DbusManagerImpl implements Manager { emptyIfNull(about), emptyIfNull(aboutEmoji), avatar == null ? "" : avatar.map(File::getPath).orElse(""), - avatar != null && !avatar.isPresent()); + avatar != null && avatar.isEmpty()); } @Override public void unregister() throws IOException { - throw new UnsupportedOperationException(); + signal.unregister(); } @Override public void deleteAccount() throws IOException { - throw new UnsupportedOperationException(); + signal.deleteAccount(); } @Override @@ -153,16 +173,16 @@ public class DbusManagerImpl implements Manager { return signal.listDevices().stream().map(d -> { final var device = getRemoteObject(d.getObjectPath(), Signal.Device.class).GetAll("org.asamk.Signal.Device"); - return new Device((long) device.get("Id").getValue(), + return new Device((Integer) device.get("Id").getValue(), (String) device.get("Name").getValue(), (long) device.get("Created").getValue(), (long) device.get("LastSeen").getValue(), thisDevice.equals(d.getObjectPath())); - }).collect(Collectors.toList()); + }).toList(); } @Override - public void removeLinkedDevices(final long deviceId) throws IOException { + public void removeLinkedDevices(final int deviceId) throws IOException { final var devicePath = signal.getDevice(deviceId); getRemoteObject(devicePath, Signal.Device.class).removeDevice(); } @@ -189,7 +209,7 @@ public class DbusManagerImpl implements Manager { @Override public List getGroups() { final var groups = signal.listGroups(); - return groups.stream().map(Signal.StructGroup::getObjectPath).map(this::getGroup).collect(Collectors.toList()); + return groups.stream().map(Signal.StructGroup::getObjectPath).map(this::getGroup).toList(); } @Override @@ -206,7 +226,8 @@ public class DbusManagerImpl implements Manager { @Override public void deleteGroup(final GroupId groupId) throws IOException { - throw new UnsupportedOperationException(); + final var group = getRemoteObject(signal.getGroup(groupId.serialize()), Signal.Group.class); + group.deleteGroup(); } @Override @@ -214,7 +235,7 @@ public class DbusManagerImpl implements Manager { final String name, final Set members, final File avatarFile ) throws IOException, AttachmentInvalidException { final var newGroupId = signal.createGroup(emptyIfNull(name), - members.stream().map(RecipientIdentifier.Single::getIdentifier).collect(Collectors.toList()), + members.stream().map(RecipientIdentifier.Single::getIdentifier).toList(), avatarFile == null ? "" : avatarFile.getPath()); return new Pair<>(GroupId.unknownVersion(newGroupId), new SendGroupMessageResults(0, List.of())); } @@ -252,28 +273,22 @@ public class DbusManagerImpl implements Manager { : GroupPermission.EVERY_MEMBER.name()); } if (updateGroup.getMembers() != null) { - group.addMembers(updateGroup.getMembers() - .stream() - .map(RecipientIdentifier.Single::getIdentifier) - .collect(Collectors.toList())); + group.addMembers(updateGroup.getMembers().stream().map(RecipientIdentifier.Single::getIdentifier).toList()); } if (updateGroup.getRemoveMembers() != null) { group.removeMembers(updateGroup.getRemoveMembers() .stream() .map(RecipientIdentifier.Single::getIdentifier) - .collect(Collectors.toList())); + .toList()); } if (updateGroup.getAdmins() != null) { - group.addAdmins(updateGroup.getAdmins() - .stream() - .map(RecipientIdentifier.Single::getIdentifier) - .collect(Collectors.toList())); + group.addAdmins(updateGroup.getAdmins().stream().map(RecipientIdentifier.Single::getIdentifier).toList()); } if (updateGroup.getRemoveAdmins() != null) { group.removeAdmins(updateGroup.getRemoveAdmins() .stream() .map(RecipientIdentifier.Single::getIdentifier) - .collect(Collectors.toList())); + .toList()); } if (updateGroup.isResetGroupLink()) { group.resetLink(); @@ -295,31 +310,35 @@ public class DbusManagerImpl implements Manager { } @Override - public void sendTypingMessage( + public SendMessageResults sendTypingMessage( final TypingAction action, final Set recipients - ) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { - for (final var recipient : recipients) { - if (recipient instanceof RecipientIdentifier.Single) { - signal.sendTyping(((RecipientIdentifier.Single) recipient).getIdentifier(), - action == TypingAction.STOP); - } else if (recipient instanceof RecipientIdentifier.Group) { - throw new UnsupportedOperationException(); - } - } + ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { + return handleMessage(recipients, numbers -> { + numbers.forEach(n -> signal.sendTyping(n, action == TypingAction.STOP)); + return 0L; + }, () -> { + signal.sendTyping(signal.getSelfNumber(), action == TypingAction.STOP); + return 0L; + }, groupId -> { + signal.sendGroupTyping(groupId, action == TypingAction.STOP); + return 0L; + }); } @Override - public void sendReadReceipt( + public SendMessageResults sendReadReceipt( final RecipientIdentifier.Single sender, final List messageIds - ) throws IOException, UntrustedIdentityException { + ) { signal.sendReadReceipt(sender.getIdentifier(), messageIds); + return new SendMessageResults(0, Map.of()); } @Override - public void sendViewedReceipt( + public SendMessageResults sendViewedReceipt( final RecipientIdentifier.Single sender, final List messageIds - ) throws IOException, UntrustedIdentityException { + ) { signal.sendViewedReceipt(sender.getIdentifier(), messageIds); + return new SendMessageResults(0, Map.of()); } @Override @@ -370,12 +389,20 @@ public class DbusManagerImpl implements Manager { @Override public SendMessageResults sendEndSessionMessage(final Set recipients) throws IOException { - signal.sendEndSessionMessage(recipients.stream() - .map(RecipientIdentifier.Single::getIdentifier) - .collect(Collectors.toList())); + signal.sendEndSessionMessage(recipients.stream().map(RecipientIdentifier.Single::getIdentifier).toList()); return new SendMessageResults(0, Map.of()); } + @Override + public void deleteRecipient(final RecipientIdentifier.Single recipient) { + signal.deleteRecipient(recipient.getIdentifier()); + } + + @Override + public void deleteContact(final RecipientIdentifier.Single recipient) { + signal.deleteContact(recipient.getIdentifier()); + } + @Override public void setContactName( final RecipientIdentifier.Single recipient, final String name @@ -384,17 +411,21 @@ public class DbusManagerImpl implements Manager { } @Override - public void setContactBlocked( - final RecipientIdentifier.Single recipient, final boolean blocked + public void setContactsBlocked( + final Collection recipients, final boolean blocked ) throws NotMasterDeviceException, IOException { - signal.setContactBlocked(recipient.getIdentifier(), blocked); + for (final var recipient : recipients) { + signal.setContactBlocked(recipient.getIdentifier(), blocked); + } } @Override - public void setGroupBlocked( - final GroupId groupId, final boolean blocked + public void setGroupsBlocked( + final Collection groupIds, final boolean blocked ) throws GroupNotFoundException, IOException { - setGroupProperty(groupId, "IsBlocked", blocked); + for (final var groupId : groupIds) { + setGroupProperty(groupId, "IsBlocked", blocked); + } } private void setGroupProperty(final GroupId groupId, final String propertyName, final boolean blocked) { @@ -410,32 +441,42 @@ public class DbusManagerImpl implements Manager { } @Override - public URI uploadStickerPack(final File path) throws IOException, StickerPackInvalidException { + public StickerPackUrl uploadStickerPack(final File path) throws IOException, StickerPackInvalidException { try { - return new URI(signal.uploadStickerPack(path.getPath())); - } catch (URISyntaxException e) { + return StickerPackUrl.fromUri(new URI(signal.uploadStickerPack(path.getPath()))); + } catch (URISyntaxException | StickerPackUrl.InvalidStickerPackLinkException e) { throw new AssertionError(e); } } + @Override + public List getStickerPacks() { + throw new UnsupportedOperationException(); + } + @Override public void requestAllSyncData() throws IOException { signal.sendSyncRequest(); } @Override - public void addReceiveHandler(final ReceiveMessageHandler handler) { + public void addReceiveHandler(final ReceiveMessageHandler handler, final boolean isWeakListener) { synchronized (messageHandlers) { - if (messageHandlers.size() == 0) { - installMessageHandlers(); + if (isWeakListener) { + weakHandlers.add(handler); + } else { + if (messageHandlers.size() == 0) { + installMessageHandlers(); + } + messageHandlers.add(handler); } - messageHandlers.add(handler); } } @Override public void removeReceiveHandler(final ReceiveMessageHandler handler) { synchronized (messageHandlers) { + weakHandlers.remove(handler); messageHandlers.remove(handler); if (messageHandlers.size() == 0) { uninstallMessageHandlers(); @@ -464,14 +505,26 @@ public class DbusManagerImpl implements Manager { @Override public void receiveMessages( - final long timeout, final TimeUnit unit, final ReceiveMessageHandler handler + final Duration timeout, final ReceiveMessageHandler handler ) throws IOException { - addReceiveHandler(handler); - try { - Thread.sleep(unit.toMillis(timeout)); - } catch (InterruptedException ignored) { + final var lastMessage = new AtomicLong(System.currentTimeMillis()); + + final ReceiveMessageHandler receiveHandler = (envelope, e) -> { + lastMessage.set(System.currentTimeMillis()); + handler.handleMessage(envelope, e); + }; + addReceiveHandler(receiveHandler); + while (true) { + try { + final var sleepTimeRemaining = timeout.toMillis() - (System.currentTimeMillis() - lastMessage.get()); + if (sleepTimeRemaining < 0) { + break; + } + Thread.sleep(sleepTimeRemaining); + } catch (InterruptedException ignored) { + } } - removeReceiveHandler(handler); + removeReceiveHandler(receiveHandler); } @Override @@ -495,7 +548,14 @@ public class DbusManagerImpl implements Manager { @Override public List> getContacts() { - throw new UnsupportedOperationException(); + return signal.listNumbers().stream().map(n -> { + final var contactName = signal.getContactName(n); + if (contactName.length() == 0) { + return null; + } + return new Pair<>(new RecipientAddress(null, n), + new Contact(contactName, null, 0, signal.isContactBlocked(n), false)); + }).filter(Objects::nonNull).toList(); } @Override @@ -530,6 +590,9 @@ public class DbusManagerImpl implements Manager { ((List) group.get("Admins").getValue()).stream() .map(m -> new RecipientAddress(null, m)) .collect(Collectors.toSet()), + ((List) group.get("Banned").getValue()).stream() + .map(m -> new RecipientAddress(null, m)) + .collect(Collectors.toSet()), (boolean) group.get("IsBlocked").getValue(), (int) group.get("MessageExpirationTimer").getValue(), GroupPermission.valueOf((String) group.get("PermissionAddMember").getValue()), @@ -577,13 +640,31 @@ public class DbusManagerImpl implements Manager { } @Override - public void close() throws IOException { + public void addAddressChangedListener(final Runnable listener) { + } + + @Override + public void addClosedListener(final Runnable listener) { + synchronized (closedListeners) { + closedListeners.add(listener); + } + } + + @Override + public void close() { synchronized (this) { this.notify(); } synchronized (messageHandlers) { + if (messageHandlers.size() > 0) { + uninstallMessageHandlers(); + } + weakHandlers.clear(); messageHandlers.clear(); - uninstallMessageHandlers(); + } + synchronized (closedListeners) { + closedListeners.forEach(Runnable::run); + closedListeners.clear(); } } @@ -598,7 +679,7 @@ public class DbusManagerImpl implements Manager { .filter(r -> r instanceof RecipientIdentifier.Single) .map(RecipientIdentifier.Single.class::cast) .map(RecipientIdentifier.Single::getIdentifier) - .collect(Collectors.toList()); + .toList(); if (singleRecipients.size() > 0) { timestamp = recipientsHandler.apply(singleRecipients); } @@ -610,7 +691,7 @@ public class DbusManagerImpl implements Manager { .filter(r -> r instanceof RecipientIdentifier.Group) .map(RecipientIdentifier.Group.class::cast) .map(RecipientIdentifier.Group::groupId) - .collect(Collectors.toList()); + .toList(); for (final var groupId : groupRecipients) { timestamp = groupHandler.apply(groupId.serialize()); } @@ -621,9 +702,9 @@ public class DbusManagerImpl implements Manager { return string == null ? "" : string; } - private T getRemoteObject(final DBusPath devicePath, final Class type) { + private T getRemoteObject(final DBusPath path, final Class type) { try { - return connection.getRemoteObject(DbusConfig.getBusname(), devicePath.getPath(), type); + return connection.getRemoteObject(DbusConfig.getBusname(), path.getPath(), type); } catch (DBusException e) { throw new AssertionError(e); } @@ -656,6 +737,7 @@ public class DbusManagerImpl implements Manager { false, Optional.empty(), Optional.empty(), + Optional.empty(), getAttachments(extras), Optional.empty(), Optional.empty(), @@ -664,11 +746,7 @@ public class DbusManagerImpl implements Manager { List.of())), Optional.empty(), Optional.empty()); - synchronized (messageHandlers) { - for (final var messageHandler : messageHandlers) { - messageHandler.handleMessage(envelope, null); - } - } + notifyMessageHandlers(envelope); }; connection.addSigHandler(Signal.MessageReceivedV2.class, signal, this.dbusMsgHandler); @@ -693,11 +771,7 @@ public class DbusManagerImpl implements Manager { Optional.empty(), Optional.empty(), Optional.empty()); - synchronized (messageHandlers) { - for (final var messageHandler : messageHandlers) { - messageHandler.handleMessage(envelope, null); - } - } + notifyMessageHandlers(envelope); }; connection.addSigHandler(Signal.ReceiptReceivedV2.class, signal, this.dbusRcptHandler); @@ -719,7 +793,7 @@ public class DbusManagerImpl implements Manager { ? Optional.empty() : Optional.of(new RecipientAddress(null, syncReceived.getDestination())), Set.of(), - new MessageEnvelope.Data(syncReceived.getTimestamp(), + Optional.of(new MessageEnvelope.Data(syncReceived.getTimestamp(), syncReceived.getGroupId().length > 0 ? Optional.of(new MessageEnvelope.Data.GroupContext(GroupId.unknownVersion( syncReceived.getGroupId()), false, 0)) @@ -733,12 +807,13 @@ public class DbusManagerImpl implements Manager { false, Optional.empty(), Optional.empty(), + Optional.empty(), getAttachments(extras), Optional.empty(), Optional.empty(), List.of(), List.of(), - List.of()))), + List.of())))), Optional.empty(), List.of(), List.of(), @@ -747,20 +822,25 @@ public class DbusManagerImpl implements Manager { Optional.empty(), Optional.empty())), Optional.empty()); - synchronized (messageHandlers) { - for (final var messageHandler : messageHandlers) { - messageHandler.handleMessage(envelope, null); - } - } + notifyMessageHandlers(envelope); }; connection.addSigHandler(Signal.SyncMessageReceivedV2.class, signal, this.dbusSyncHandler); } catch (DBusException e) { e.printStackTrace(); } + signal.subscribeReceive(); + } + + private void notifyMessageHandlers(final MessageEnvelope envelope) { + synchronized (messageHandlers) { + Stream.concat(messageHandlers.stream(), weakHandlers.stream()) + .forEach(h -> h.handleMessage(envelope, null)); + } } private void uninstallMessageHandlers() { try { + signal.unsubscribeReceive(); connection.removeSigHandler(Signal.MessageReceivedV2.class, signal, this.dbusMsgHandler); connection.removeSigHandler(Signal.ReceiptReceivedV2.class, signal, this.dbusRcptHandler); connection.removeSigHandler(Signal.SyncMessageReceivedV2.class, signal, this.dbusSyncHandler); @@ -793,7 +873,7 @@ public class DbusManagerImpl implements Manager { getValue(a, "isVoiceNote"), getValue(a, "isGif"), getValue(a, "isBorderless")); - }).collect(Collectors.toList()); + }).toList(); } @SuppressWarnings("unchecked")