]> nmode's Git Repositories - signal-cli/commitdiff
Configuration for Dbus and main
authorJohn Freed <okgithub@johnfreed.com>
Thu, 14 Oct 2021 22:09:26 +0000 (00:09 +0200)
committerJohn Freed <okgithub@johnfreed.com>
Thu, 14 Oct 2021 22:09:26 +0000 (00:09 +0200)
Main program subcommand
- fix logic to take into account previously unset flags
- provide output in json and plain-text formats

new Dbus Properties:
- ConfigurationReadReceipts
- ConfigurationUnidentifiedDeliveryIndicators
- ConfigurationTypingIndicators
- ConfigurationLinkPreviews

removed getConfiguration and setConfiguration methods

updated documentation

man/signal-cli-dbus.5.adoc
src/main/java/org/asamk/Signal.java
src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java
src/main/java/org/asamk/signal/dbus/DbusProperties.java
src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java

index 1a0fbfaaa76cd495b8d8628413b38b63aae80963..e379dcfb6436e0a270ef755cd07b16b4a75ec116 100755 (executable)
@@ -364,6 +364,26 @@ removeDevice() -> <>::
 
 Exceptions: Failure
 
+=== Configuration properties
+The configuration's object path, which exists only for primary devices, is constructed as follows:
+"/org/asamk/Signal/" + DBusNumber + "/Configuration"
+* DBusNumber  : recipient's phone number, with underscore (_) replacing plus (+)
+
+Configurations have the following (case-sensitive) properties:
+* ConfigurationReadReceipts<b>                   : should send read receipts (true/false)
+* ConfigurationUnidentifiedDeliveryIndicators<b> : should show unidentified delivery indicators (true/false)
+* ConfigurationTypingIndicators<b>               : should send/show typing indicators (true/false)
+* ConfigurationLinkPreviews<b>                   : should generate link previews (true/false)
+
+To get a property, use (replacing `--session` with `--system` if needed):
+`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.Get string:org.asamk.Signal.Configuration string:$PROPERTY_NAME`
+
+To set a property, use:
+`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.Set string:org.asamk.Signal.Configuration string:$PROPERTY_NAME variant:$PROPERTY_TYPE:$PROPERTY_VALUE`
+
+To get all properties, use:
+`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.GetAll string:org.asamk.Signal.Configuration`
+
 === Other methods
 
 getContactName(number<s>) -> name<s>::
@@ -529,27 +549,7 @@ uploadStickerPack(stickerPackPath<s>) -> url<s>::
 * stickerPackPath : Path to the manifest.json file or a zip file in the same directory
 * url             : URL of sticker pack after successful upload
 
-Exception: Failure, IOError
-
-getConfiguration() -> [readReceipts<b>, unidentifiedDeliveryIndicators<b>, typingIndicators<b>, linkPreviews<b>] -> <>::
-* readReceipts                   : Should Signal send read receipts (true/false).
-* unidentifiedDeliveryIndicators : Should Signal show unidentified delivery indicators (true/false).
-* typingIndicators               : Should Signal send/show typing indicators (true/false).
-* linkPreviews                   : Should Signal generate link previews (true/false).
-
-Gets an array of four booleans as indicated. Only works from primary device.
-
-Exceptions: IOError, UserError
-
-setConfiguration(readReceipts<b>, unidentifiedDeliveryIndicators<b>, typingIndicators<b>, linkPreviews<b>) -> <>::
-* readReceipts                   : Should Signal send read receipts (true/false).
-* unidentifiedDeliveryIndicators : Should Signal show unidentified delivery indicators (true/false).
-* typingIndicators               : Should Signal send/show typing indicators (true/false).
-* linkPreviews                   : Should Signal generate link previews (true/false).
-
-Update Signal configurations and sync them to linked devices. Only works from primary device.
-
-Exceptions: IOError, UserError
+Exceptions: Failure
 
 version() -> version<s>::
 * version : Version string of signal-cli
index 28c192a09557281bac20361cd4b9058c14925fe3..2dba13f826d807d442328e8a4c93488fa9c16d61 100644 (file)
@@ -160,10 +160,6 @@ public interface Signal extends DBusInterface {
 
     String uploadStickerPack(String stickerPackPath) throws Error.Failure;
 
-    void setConfiguration(boolean readReceipts, boolean unidentifiedDeliveryIndicators, boolean typingIndicators, boolean linkPreviews) throws Error.IOError, Error.UserError;
-
-    List<Boolean> getConfiguration();
-
     void submitRateLimitChallenge(String challenge, String captchaString) throws IOErrorException;
 
     class MessageReceived extends DBusSignal {
@@ -322,6 +318,13 @@ public interface Signal extends DBusInterface {
         void removeDevice() throws Error.Failure;
     }
 
+    @DBusProperty(name = "ConfigurationReadReceipts", type = Boolean.class)
+    @DBusProperty(name = "ConfigurationUnidentifiedDeliveryIndicators", type = Boolean.class)
+    @DBusProperty(name = "ConfigurationTypingIndicators", type = Boolean.class)
+    @DBusProperty(name = "ConfigurationLinkPreviews", type = Boolean.class)
+    interface Configuration extends DBusInterface, Properties {
+    }
+
     class StructGroup extends Struct {
 
         @Position(0)
index 9ca126d059c39cb9dbc943bc10f07da8e9806e9c..1588a72d896accfc1efdde7936b07f930a76e54b 100644 (file)
@@ -3,7 +3,9 @@ package org.asamk.signal.commands;
 import net.sourceforge.argparse4j.inf.Namespace;
 import net.sourceforge.argparse4j.inf.Subparser;
 
+import org.asamk.signal.JsonWriter;
 import org.asamk.signal.OutputWriter;
+import org.asamk.signal.PlainTextWriter;
 import org.asamk.signal.commands.exceptions.CommandException;
 import org.asamk.signal.commands.exceptions.IOErrorException;
 import org.asamk.signal.commands.exceptions.UserErrorException;
@@ -11,6 +13,9 @@ import org.asamk.signal.manager.Manager;
 import org.asamk.signal.manager.NotMasterDeviceException;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 public class UpdateConfigurationCommand implements JsonRpcLocalCommand {
 
@@ -40,12 +45,59 @@ public class UpdateConfigurationCommand implements JsonRpcLocalCommand {
     public void handleCommand(
             final Namespace ns, final Manager m, final OutputWriter outputWriter
     ) throws CommandException {
-        final var readReceipts = ns.getBoolean("read-receipts");
-        final var unidentifiedDeliveryIndicators = ns.getBoolean("unidentified-delivery-indicators");
-        final var typingIndicators = ns.getBoolean("typing-indicators");
-        final var linkPreviews = ns.getBoolean("link-previews");
+        var readReceipts = ns.getBoolean("read-receipts");
+        var unidentifiedDeliveryIndicators = ns.getBoolean("unidentified-delivery-indicators");
+        var typingIndicators = ns.getBoolean("typing-indicators");
+        var linkPreviews = ns.getBoolean("link-previews");
+        List<Boolean> configuration = new ArrayList<>(4);
+
+        try {
+            configuration = m.getConfiguration();
+        } catch (IOException | NotMasterDeviceException e) {
+            throw new CommandException(e.getMessage());
+        }
+
+        if (readReceipts == null) {
+            try {
+                readReceipts = configuration.get(0);
+            } catch (NullPointerException e) {
+                readReceipts = true;
+            }
+        }
+        if (unidentifiedDeliveryIndicators == null) {
+            try {
+                unidentifiedDeliveryIndicators = configuration.get(1);
+            } catch (NullPointerException e) {
+                unidentifiedDeliveryIndicators = true;
+            }
+        }
+        if (typingIndicators == null) {
+            try {
+                typingIndicators = configuration.get(2);
+            } catch (NullPointerException e) {
+                typingIndicators = true;
+            }
+        }
+        if (linkPreviews == null) {
+            try {
+                linkPreviews = configuration.get(3);
+            } catch (NullPointerException e) {
+                linkPreviews = true;
+            }
+        }
         try {
             m.updateConfiguration(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
+            if (outputWriter instanceof JsonWriter) {
+                final var writer = (JsonWriter) outputWriter;
+                writer.write(Map.of("readReceipts", readReceipts, "unidentifiedDeliveryIndicators", unidentifiedDeliveryIndicators, "typingIndicators", typingIndicators, "linkPreviews", linkPreviews));
+            } else {
+                final var writer = (PlainTextWriter) outputWriter;
+                writer.println("readReceipts=" + readReceipts
+                        + "\nunidentifiedDeliveryIndicators=" + unidentifiedDeliveryIndicators
+                        + "\ntypingIndicators=" + typingIndicators
+                        + "\nlinkPreviews=" + linkPreviews
+                        );
+            }
         } catch (IOException e) {
             throw new IOErrorException("UpdateAccount error: " + e.getMessage(), e);
         } catch (NotMasterDeviceException e) {
index a8f071f7a6723c4510944a12b4490c1ab305cc62..eb5b590ca5f303354e6f2f52010a396eb27bcc3d 100644 (file)
@@ -7,6 +7,7 @@ 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.Configuration;
 import org.asamk.signal.manager.api.Device;
 import org.asamk.signal.manager.api.Group;
 import org.asamk.signal.manager.api.Identity;
@@ -100,24 +101,21 @@ public class DbusManagerImpl implements Manager {
         }
     }
 
+
+    @Override
+    public List<Boolean> getConfiguration() {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void updateConfiguration(
             final Boolean readReceipts,
             final Boolean unidentifiedDeliveryIndicators,
             final Boolean typingIndicators,
             final Boolean linkPreviews
-    ) throws IOException {
-        signal.setConfiguration(
-                readReceipts,
-                unidentifiedDeliveryIndicators,
-                typingIndicators,
-                linkPreviews
-                );
-    }
-
-    @Override
-    public List<Boolean> getConfiguration() {
-        return signal.getConfiguration();
+    )
+    {
+        throw new UnsupportedOperationException();
     }
 
     @Override
index bbe01d6b581dc9e85025c2b222b64d51f7aa666a..4700deeeadf8938f8adc472dc14e1d2bd8c7ee82 100644 (file)
@@ -67,4 +67,5 @@ public abstract class DbusProperties implements Properties {
                     return o instanceof Variant ? (Variant<Object>) o : new Variant<>(o);
                 }));
     }
+
 }
index c6e3273ae4bfdad834908ede0837dd6df4883498..1f1f954f00b59a6dd73bbfe1a437c1a79ef8bb33 100644 (file)
@@ -2,12 +2,14 @@ package org.asamk.signal.dbus;
 
 import org.asamk.Signal;
 import org.asamk.signal.BaseConfig;
+import org.asamk.signal.commands.exceptions.CommandException;
 import org.asamk.signal.commands.exceptions.IOErrorException;
 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.Configuration;
 import org.asamk.signal.manager.api.Identity;
 import org.asamk.signal.manager.api.Message;
 import org.asamk.signal.manager.api.RecipientIdentifier;
@@ -29,6 +31,8 @@ import org.freedesktop.dbus.connections.impl.DBusConnection;
 import org.freedesktop.dbus.exceptions.DBusException;
 import org.freedesktop.dbus.exceptions.DBusExecutionException;
 import org.freedesktop.dbus.types.Variant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.whispersystems.libsignal.InvalidKeyException;
 import org.whispersystems.libsignal.util.Pair;
 import org.whispersystems.libsignal.util.guava.Optional;
@@ -65,6 +69,8 @@ public class DbusSignalImpl implements Signal {
     private final List<StructDevice> devices = new ArrayList<>();
     private final List<StructGroup> groups = new ArrayList<>();
 
+    private final static Logger logger = LoggerFactory.getLogger(DbusSignalImpl.class);
+
     public DbusSignalImpl(final Manager m, DBusConnection connection, final String objectPath) {
         this.m = m;
         this.connection = connection;
@@ -74,10 +80,13 @@ public class DbusSignalImpl implements Signal {
     public void initObjects() {
         updateDevices();
         updateGroups();
+        updateConfiguration();
     }
 
     public void close() {
         unExportDevices();
+        unExportGroups();
+        unExportConfiguration();
     }
 
     @Override
@@ -702,30 +711,6 @@ public class DbusSignalImpl implements Signal {
         }
     }
 
-    @Override
-    public void setConfiguration(boolean readReceipts, boolean unidentifiedDeliveryIndicators, boolean typingIndicators, boolean linkPreviews) {
-          try {
-              m.updateConfiguration(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
-          } catch (IOException e) {
-              throw new Error.IOError("UpdateAccount error: " + e.getMessage());
-          } catch (NotMasterDeviceException e) {
-              throw new Error.UserError("This command doesn't work on linked devices.");
-          }
-    }
-
-    @Override
-    public List<Boolean> getConfiguration() {
-        List<Boolean> config = new ArrayList<>(4);
-        try {
-            config = m.getConfiguration();
-        } catch (IOException e) {
-            throw new Error.IOError("Configuration storage error: " + e.getMessage());
-        } catch (NotMasterDeviceException e) {
-            throw new Error.UserError("This command doesn't work on linked devices.");
-        }
-        return config;
-    }
-
     private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException {
         var error = ErrorUtils.getErrorMessageFromSendMessageResult(result);
 
@@ -852,6 +837,7 @@ public class DbusSignalImpl implements Signal {
             final var deviceObjectPath = object.getObjectPath();
             try {
                 connection.exportObject(object);
+                logger.info("Exported dbus object: " + deviceObjectPath);
             } catch (DBusException e) {
                 e.printStackTrace();
             }
@@ -888,6 +874,7 @@ public class DbusSignalImpl implements Signal {
             final var object = new DbusSignalGroupImpl(g.getGroupId());
             try {
                 connection.exportObject(object);
+                logger.info("Exported dbus object: " + object.getObjectPath());
             } catch (DBusException e) {
                 e.printStackTrace();
             }
@@ -902,6 +889,82 @@ public class DbusSignalImpl implements Signal {
         this.groups.clear();
     }
 
+    private void unExportConfiguration() {
+        final var object = getConfigurationObjectPath(objectPath);
+        connection.unExportObject(object);
+    }
+
+    private static String getConfigurationObjectPath(String basePath) {
+        return basePath + "/Configuration";
+    }
+
+    private void updateConfiguration() {
+
+        Boolean readReceipts = null;
+        Boolean unidentifiedDeliveryIndicators = null;
+        Boolean typingIndicators = null;
+        Boolean linkPreviews = null;
+        List<Boolean> configuration = new ArrayList<>(4);
+
+        try {
+            configuration = m.getConfiguration();
+        } catch (NotMasterDeviceException e) {
+            logger.info("Not exporting Configuration for " + m.getSelfNumber() + ": " + e.getMessage());
+            return;
+        } catch (IOException e) {
+            throw new Error.IOError(objectPath + e.getMessage());
+        } catch (NullPointerException e) {
+            logger.info("No configuration found, creating one for " + m.getSelfNumber() + ": " + e.getMessage());
+            readReceipts = true;
+            unidentifiedDeliveryIndicators = true;
+            typingIndicators = true;
+            linkPreviews = true;
+        }
+        if (readReceipts == null) {
+            try {
+                readReceipts = configuration.get(0);
+            } catch (NullPointerException e) {
+                readReceipts = true;
+            }
+        }
+        if (unidentifiedDeliveryIndicators == null) {
+            try {
+                unidentifiedDeliveryIndicators = configuration.get(1);
+            } catch (NullPointerException e) {
+                unidentifiedDeliveryIndicators = true;
+            }
+        }
+        if (typingIndicators == null) {
+            try {
+                typingIndicators = configuration.get(2);
+            } catch (NullPointerException e) {
+                typingIndicators = true;
+            }
+        }
+        if (linkPreviews == null) {
+            try {
+                linkPreviews = configuration.get(3);
+            } catch (NullPointerException e) {
+                linkPreviews = true;
+            }
+        }
+
+        try {
+            unExportConfiguration();
+            m.updateConfiguration(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
+            final var object = new DbusSignalConfigurationImpl(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
+            connection.exportObject(object);
+            logger.info("Exported dbus object: " + objectPath + "/Configuration");
+        } catch (NotMasterDeviceException ignore) {
+            /* already caught */
+        } catch (IOException e) {
+            throw new Error.IOError(objectPath + e.getMessage());
+        } catch (DBusException e) {
+            e.printStackTrace();
+        }
+    }
+
+
     public class DbusSignalDeviceImpl extends DbusProperties implements Signal.Device {
 
         private final org.asamk.signal.manager.api.Device device;
@@ -944,6 +1007,124 @@ public class DbusSignalImpl implements Signal {
         }
     }
 
+    public class DbusSignalConfigurationImpl extends DbusProperties implements Signal.Configuration {
+
+        private final Boolean readReceipts;
+        private final Boolean unidentifiedDeliveryIndicators;
+        private final Boolean typingIndicators;
+        private final Boolean linkPreviews;
+
+        public DbusSignalConfigurationImpl(final Boolean readReceipts, final Boolean unidentifiedDeliveryIndicators, final Boolean typingIndicators, final Boolean linkPreviews) {
+            this.readReceipts = readReceipts;
+            this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
+            this.typingIndicators = typingIndicators;
+            this.linkPreviews = linkPreviews;
+            super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Configuration",
+                    List.of(new DbusProperty<>("ConfigurationReadReceipts", () -> getReadReceipts(), this::setReadReceipts),
+                            new DbusProperty<>("ConfigurationUnidentifiedDeliveryIndicators", () -> getUnidentifiedDeliveryIndicators(), this::setUnidentifiedDeliveryIndicators),
+                            new DbusProperty<>("ConfigurationTypingIndicators", () -> getTypingIndicators(), this::setTypingIndicators),
+                            new DbusProperty<>("ConfigurationLinkPreviews", () -> getLinkPreviews(), this::setLinkPreviews)
+                            )
+                    ));
+
+        }
+
+        @Override
+        public String getObjectPath() {
+              return getConfigurationObjectPath(objectPath);
+        }
+
+        public void setReadReceipts(Boolean readReceipts) {
+            setConfiguration(readReceipts, null, null, null);
+        }
+
+        public void setUnidentifiedDeliveryIndicators(Boolean unidentifiedDeliveryIndicators) {
+            setConfiguration(null, unidentifiedDeliveryIndicators, null, null);
+        }
+
+        public void setTypingIndicators(Boolean typingIndicators) {
+            setConfiguration(null, null, typingIndicators, null);
+        }
+
+        public void setLinkPreviews(Boolean linkPreviews) {
+            setConfiguration(null, null, null, linkPreviews);
+        }
+
+        private void setConfiguration(Boolean readReceipts, Boolean unidentifiedDeliveryIndicators, Boolean typingIndicators, Boolean linkPreviews) {
+            try {
+                if (readReceipts == null) {
+                    readReceipts = m.getConfiguration().get(0);
+                }
+                if (unidentifiedDeliveryIndicators == null) {
+                    unidentifiedDeliveryIndicators = m.getConfiguration().get(1);
+                }
+                if (typingIndicators == null) {
+                    typingIndicators = m.getConfiguration().get(2);
+                }
+                if (linkPreviews == null) {
+                    linkPreviews = m.getConfiguration().get(3);
+                }
+                m.updateConfiguration(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
+            } catch (IOException e) {
+                throw new Error.IOError("UpdateAccount error: " + e.getMessage());
+            } catch (NotMasterDeviceException e) {
+                throw new Error.UserError("This command doesn't work on linked devices.");
+            }
+        }
+
+        public List<Boolean> getConfiguration() {
+            List<Boolean> config = new ArrayList<>(4);
+            try {
+                config = m.getConfiguration();
+            } catch (IOException e) {
+                throw new Error.IOError("Configuration storage error: " + e.getMessage());
+            } catch (NotMasterDeviceException e) {
+                throw new Error.UserError("This command doesn't work on linked devices.");
+            }
+            return config;
+        }
+
+        public Boolean getReadReceipts() {
+            try {
+                return m.getConfiguration().get(0);
+            } catch (IOException e) {
+                throw new Error.IOError("Configuration storage error: " + e.getMessage());
+            } catch (NotMasterDeviceException e) {
+                throw new Error.UserError("This command doesn't work on linked devices.");
+            }
+        }
+
+        public Boolean getUnidentifiedDeliveryIndicators() {
+            try {
+                return m.getConfiguration().get(1);
+            } catch (IOException e) {
+                throw new Error.IOError("Configuration storage error: " + e.getMessage());
+            } catch (NotMasterDeviceException e) {
+                throw new Error.UserError("This command doesn't work on linked devices.");
+            }
+        }
+
+        public Boolean getTypingIndicators() {
+            try {
+                return m.getConfiguration().get(2);
+            } catch (IOException e) {
+                throw new Error.IOError("Configuration storage error: " + e.getMessage());
+            } catch (NotMasterDeviceException e) {
+                throw new Error.UserError("This command doesn't work on linked devices.");
+            }
+        }
+
+        public Boolean getLinkPreviews() {
+            try {
+                return m.getConfiguration().get(3);
+            } catch (IOException e) {
+                throw new Error.IOError("Configuration storage error: " + e.getMessage());
+            } catch (NotMasterDeviceException e) {
+                throw new Error.UserError("This command doesn't work on linked devices.");
+            }
+        }
+    }
+
     public class DbusSignalGroupImpl extends DbusProperties implements Signal.Group {
 
         private final GroupId groupId;