]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java
Improve handling of CDSI resource exhaustion
[signal-cli] / src / main / java / org / asamk / signal / dbus / DbusSignalImpl.java
index b1d648869e6e59b83920422f210002208f68d0fb..2223c96a6d31d2af3d6bf672523a3d09185c563c 100644 (file)
@@ -11,6 +11,7 @@ import org.asamk.signal.manager.api.GroupLinkState;
 import org.asamk.signal.manager.api.GroupNotFoundException;
 import org.asamk.signal.manager.api.GroupPermission;
 import org.asamk.signal.manager.api.GroupSendingNotAllowedException;
+import org.asamk.signal.manager.api.IdentityVerificationCode;
 import org.asamk.signal.manager.api.InactiveGroupLinkException;
 import org.asamk.signal.manager.api.InvalidDeviceLinkException;
 import org.asamk.signal.manager.api.InvalidNumberException;
@@ -20,6 +21,7 @@ import org.asamk.signal.manager.api.Message;
 import org.asamk.signal.manager.api.NotAGroupMemberException;
 import org.asamk.signal.manager.api.NotPrimaryDeviceException;
 import org.asamk.signal.manager.api.PendingAdminApprovalException;
+import org.asamk.signal.manager.api.RateLimitException;
 import org.asamk.signal.manager.api.RecipientAddress;
 import org.asamk.signal.manager.api.RecipientIdentifier;
 import org.asamk.signal.manager.api.SendMessageResult;
@@ -30,6 +32,7 @@ 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.util.DateUtils;
 import org.asamk.signal.util.SendMessageResultUtils;
 import org.freedesktop.dbus.DBusPath;
 import org.freedesktop.dbus.connections.impl.DBusConnection;
@@ -54,6 +57,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Collectors;
 
 import static org.asamk.signal.dbus.DbusUtils.makeValidObjectPathElement;
@@ -68,6 +72,7 @@ public class DbusSignalImpl implements Signal {
     private DBusPath thisDevice;
     private final List<StructDevice> devices = new ArrayList<>();
     private final List<StructGroup> groups = new ArrayList<>();
+    private final List<StructIdentity> identities = new ArrayList<>();
     private DbusReceiveMessageHandler dbusMessageHandler;
     private int subscriberCount;
 
@@ -100,6 +105,7 @@ public class DbusSignalImpl implements Signal {
         updateDevices();
         updateGroups();
         updateConfiguration();
+        updateIdentities();
     }
 
     public void close() {
@@ -114,6 +120,7 @@ public class DbusSignalImpl implements Signal {
         unExportDevices();
         unExportGroups();
         unExportConfiguration();
+        unExportIdentities();
         connection.unExportObject(this.objectPath);
     }
 
@@ -180,6 +187,8 @@ public class DbusSignalImpl implements Signal {
             m.addDeviceLink(deviceLinkUrl);
         } catch (IOException | InvalidDeviceLinkException e) {
             throw new Error.Failure(e.getClass().getSimpleName() + " Add device link failed. " + e.getMessage());
+        } catch (NotPrimaryDeviceException e) {
+            throw new Error.Failure("This command doesn't work on linked devices.");
         } catch (URISyntaxException e) {
             throw new Error.InvalidUri(e.getClass().getSimpleName()
                     + " Device link uri has invalid format: "
@@ -674,6 +683,10 @@ public class DbusSignalImpl implements Signal {
             registered = m.getUserStatus(new HashSet<>(numbers));
         } catch (IOException e) {
             throw new Error.Failure(e.getMessage());
+        } catch (RateLimitException e) {
+            throw new Error.Failure(e.getMessage()
+                    + ", retry at "
+                    + DateUtils.formatTimestamp(e.getNextAttemptTimestamp()));
         }
 
         return numbers.stream().map(number -> registered.get(number).uuid() != null).toList();
@@ -886,7 +899,7 @@ public class DbusSignalImpl implements Signal {
         }
 
         var errors = SendMessageResultUtils.getErrorMessagesFromSendMessageResults(results);
-        if (errors.size() == 0 || errors.size() < results.size()) {
+        if (errors.isEmpty() || errors.size() < results.size()) {
             return;
         }
 
@@ -1025,7 +1038,114 @@ public class DbusSignalImpl implements Signal {
             connection.exportObject(object);
             logger.debug("Exported dbus object: " + object.getObjectPath());
         } catch (DBusException e) {
-            e.printStackTrace();
+            logger.warn("Failed to export dbus object (" + object.getObjectPath() + "): " + e.getMessage());
+        }
+    }
+
+    private void updateIdentities() {
+        List<org.asamk.signal.manager.api.Identity> identities;
+        identities = m.getIdentities();
+
+        unExportIdentities();
+
+        identities.forEach(i -> {
+            final var object = new DbusSignalIdentityImpl(i);
+            exportObject(object);
+            this.identities.add(new StructIdentity(new DBusPath(object.getObjectPath()),
+                    i.recipient().uuid().map(UUID::toString).orElse(""),
+                    i.recipient().number().orElse("")));
+        });
+    }
+
+    private static String getIdentityObjectPath(String basePath, String id) {
+        return basePath + "/Identities/" + makeValidObjectPathElement(id);
+    }
+
+    private void unExportIdentities() {
+        this.identities.stream()
+                .map(StructIdentity::getObjectPath)
+                .map(DBusPath::getPath)
+                .forEach(connection::unExportObject);
+        this.identities.clear();
+    }
+
+    @Override
+    public DBusPath getIdentity(String number) throws Error.Failure {
+        final var found = identities.stream()
+                .filter(identity -> identity.getNumber().equals(number) || identity.getUuid().equals(number))
+                .findFirst();
+
+        if (found.isEmpty()) {
+            throw new Error.Failure("Identity for " + number + " unknown");
+        }
+        return found.get().getObjectPath();
+    }
+
+    @Override
+    public List<StructIdentity> listIdentities() {
+        updateIdentities();
+        return this.identities;
+    }
+
+    public class DbusSignalIdentityImpl extends DbusProperties implements Signal.Identity {
+
+        private final org.asamk.signal.manager.api.Identity identity;
+
+        public DbusSignalIdentityImpl(final org.asamk.signal.manager.api.Identity identity) {
+            this.identity = identity;
+            super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Identity",
+                    List.of(new DbusProperty<>("Number", () -> identity.recipient().number().orElse("")),
+                            new DbusProperty<>("Uuid",
+                                    () -> identity.recipient().uuid().map(UUID::toString).orElse("")),
+                            new DbusProperty<>("Fingerprint", identity::getFingerprint),
+                            new DbusProperty<>("SafetyNumber", identity::safetyNumber),
+                            new DbusProperty<>("ScannableSafetyNumber", identity::scannableSafetyNumber),
+                            new DbusProperty<>("TrustLevel", identity::trustLevel),
+                            new DbusProperty<>("AddedDate", identity::dateAddedTimestamp))));
+        }
+
+        @Override
+        public String getObjectPath() {
+            return getIdentityObjectPath(objectPath,
+                    identity.recipient().getLegacyIdentifier() + "_" + identity.recipient().getIdentifier());
+        }
+
+        @Override
+        public void trust() throws Error.Failure {
+            var recipient = RecipientIdentifier.Single.fromAddress(identity.recipient());
+            try {
+                m.trustIdentityAllKeys(recipient);
+            } catch (UnregisteredRecipientException e) {
+                throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered.");
+            }
+            updateIdentities();
+        }
+
+        @Override
+        public void trustVerified(String safetyNumber) throws Error.Failure {
+            var recipient = RecipientIdentifier.Single.fromAddress(identity.recipient());
+
+            if (safetyNumber == null) {
+                throw new Error.Failure("You need to specify a fingerprint/safety number");
+            }
+            final IdentityVerificationCode verificationCode;
+            try {
+                verificationCode = IdentityVerificationCode.parse(safetyNumber);
+            } catch (Exception e) {
+                throw new Error.Failure(
+                        "Safety number has invalid format, either specify the old hex fingerprint or the new safety number");
+            }
+
+            try {
+                final var res = m.trustIdentityVerified(recipient, verificationCode);
+                if (!res) {
+                    throw new Error.Failure(
+                            "Failed to set the trust for this number, make sure the number and the fingerprint/safety number are correct.");
+                }
+            } catch (UnregisteredRecipientException e) {
+                throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered.");
+            }
+            updateIdentities();
         }
     }
 
@@ -1073,8 +1193,7 @@ public class DbusSignalImpl implements Signal {
 
     public class DbusSignalConfigurationImpl extends DbusProperties implements Signal.Configuration {
 
-        public DbusSignalConfigurationImpl(
-        ) {
+        public DbusSignalConfigurationImpl() {
             super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Configuration",
                     List.of(new DbusProperty<>("ReadReceipts", this::getReadReceipts, this::setReadReceipts),
                             new DbusProperty<>("UnidentifiedDeliveryIndicators",