]> nmode's Git Repositories - signal-cli/commitdiff
Implement configuration handling
authorAsamK <asamk@gmx.de>
Wed, 29 Sep 2021 17:38:31 +0000 (19:38 +0200)
committerAsamK <asamk@gmx.de>
Thu, 30 Sep 2021 19:41:24 +0000 (21:41 +0200)
Closes #747

lib/src/main/java/org/asamk/signal/manager/Manager.java
lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java
lib/src/main/java/org/asamk/signal/manager/actions/SendSyncConfigurationAction.java [new file with mode: 0644]
lib/src/main/java/org/asamk/signal/manager/configuration/ConfigurationStore.java [new file with mode: 0644]
lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java
lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java
man/signal-cli.1.adoc
src/main/java/org/asamk/signal/commands/Commands.java
src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java [new file with mode: 0644]
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java

index cba438f817d1e3d634f31f9b06c0c30366a8b819..cd7b033550f4ac998f8387bd2dfd25b5c1c98bb3 100644 (file)
@@ -98,6 +98,13 @@ public interface Manager extends Closeable {
 
     void updateAccountAttributes(String deviceName) throws IOException;
 
+    void updateConfiguration(
+            final Boolean readReceipts,
+            final Boolean unidentifiedDeliveryIndicators,
+            final Boolean typingIndicators,
+            final Boolean linkPreviews
+    ) throws IOException, NotMasterDeviceException;
+
     void setProfile(
             String givenName, String familyName, String about, String aboutEmoji, Optional<File> avatar
     ) throws IOException;
index de60fa50aa8ce8b90d00f7727ef00e95c7fad763..36c131db1996941cc80724e2e0eae0a33653b55d 100644 (file)
@@ -320,6 +320,31 @@ public class ManagerImpl implements Manager {
                         account.isDiscoverableByPhoneNumber());
     }
 
+    @Override
+    public void updateConfiguration(
+            final Boolean readReceipts,
+            final Boolean unidentifiedDeliveryIndicators,
+            final Boolean typingIndicators,
+            final Boolean linkPreviews
+    ) throws IOException, NotMasterDeviceException {
+        if (!account.isMasterDevice()) {
+            throw new NotMasterDeviceException();
+        }
+        if (readReceipts != null) {
+            account.getConfigurationStore().setReadReceipts(readReceipts);
+        }
+        if (unidentifiedDeliveryIndicators != null) {
+            account.getConfigurationStore().setUnidentifiedDeliveryIndicators(unidentifiedDeliveryIndicators);
+        }
+        if (typingIndicators != null) {
+            account.getConfigurationStore().setTypingIndicators(typingIndicators);
+        }
+        if (linkPreviews != null) {
+            account.getConfigurationStore().setLinkPreviews(linkPreviews);
+        }
+        syncHelper.sendConfigurationMessage();
+    }
+
     /**
      * @param givenName  if null, the previous givenName will be kept
      * @param familyName if null, the previous familyName will be kept
diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/SendSyncConfigurationAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/SendSyncConfigurationAction.java
new file mode 100644 (file)
index 0000000..0e050f0
--- /dev/null
@@ -0,0 +1,20 @@
+package org.asamk.signal.manager.actions;
+
+import org.asamk.signal.manager.jobs.Context;
+
+public class SendSyncConfigurationAction implements HandleAction {
+
+    private static final SendSyncConfigurationAction INSTANCE = new SendSyncConfigurationAction();
+
+    private SendSyncConfigurationAction() {
+    }
+
+    public static SendSyncConfigurationAction create() {
+        return INSTANCE;
+    }
+
+    @Override
+    public void execute(Context context) throws Throwable {
+        context.getSyncHelper().sendConfigurationMessage();
+    }
+}
diff --git a/lib/src/main/java/org/asamk/signal/manager/configuration/ConfigurationStore.java b/lib/src/main/java/org/asamk/signal/manager/configuration/ConfigurationStore.java
new file mode 100644 (file)
index 0000000..e7e1b5f
--- /dev/null
@@ -0,0 +1,93 @@
+package org.asamk.signal.manager.configuration;
+
+public class ConfigurationStore {
+
+    private final Saver saver;
+
+    private Boolean readReceipts;
+    private Boolean unidentifiedDeliveryIndicators;
+    private Boolean typingIndicators;
+    private Boolean linkPreviews;
+
+    public ConfigurationStore(final Saver saver) {
+        this.saver = saver;
+    }
+
+    public static ConfigurationStore fromStorage(Storage storage, Saver saver) {
+        final var store = new ConfigurationStore(saver);
+        store.readReceipts = storage.readReceipts;
+        store.unidentifiedDeliveryIndicators = storage.unidentifiedDeliveryIndicators;
+        store.typingIndicators = storage.typingIndicators;
+        store.linkPreviews = storage.linkPreviews;
+        return store;
+    }
+
+    public Boolean getReadReceipts() {
+        return readReceipts;
+    }
+
+    public void setReadReceipts(final boolean readReceipts) {
+        this.readReceipts = readReceipts;
+        saver.save(toStorage());
+    }
+
+    public Boolean getUnidentifiedDeliveryIndicators() {
+        return unidentifiedDeliveryIndicators;
+    }
+
+    public void setUnidentifiedDeliveryIndicators(final boolean unidentifiedDeliveryIndicators) {
+        this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
+        saver.save(toStorage());
+    }
+
+    public Boolean getTypingIndicators() {
+        return typingIndicators;
+    }
+
+    public void setTypingIndicators(final boolean typingIndicators) {
+        this.typingIndicators = typingIndicators;
+        saver.save(toStorage());
+    }
+
+    public Boolean getLinkPreviews() {
+        return linkPreviews;
+    }
+
+    public void setLinkPreviews(final boolean linkPreviews) {
+        this.linkPreviews = linkPreviews;
+        saver.save(toStorage());
+    }
+
+    private Storage toStorage() {
+        return new Storage(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
+    }
+
+    public static final class Storage {
+
+        public Boolean readReceipts;
+        public Boolean unidentifiedDeliveryIndicators;
+        public Boolean typingIndicators;
+        public Boolean linkPreviews;
+
+        // For deserialization
+        private Storage() {
+        }
+
+        public Storage(
+                final Boolean readReceipts,
+                final Boolean unidentifiedDeliveryIndicators,
+                final Boolean typingIndicators,
+                final Boolean linkPreviews
+        ) {
+            this.readReceipts = readReceipts;
+            this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
+            this.typingIndicators = typingIndicators;
+            this.linkPreviews = linkPreviews;
+        }
+    }
+
+    public interface Saver {
+
+        void save(Storage storage);
+    }
+}
index 64e168572bdb22f9d170ebc11fafea9fb4338821..dead91b04e3f747e2fc97a567e7b03e1c4cb6167 100644 (file)
@@ -15,6 +15,7 @@ import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
 import org.asamk.signal.manager.actions.SendReceiptAction;
 import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
 import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
+import org.asamk.signal.manager.actions.SendSyncConfigurationAction;
 import org.asamk.signal.manager.actions.SendSyncContactsAction;
 import org.asamk.signal.manager.actions.SendSyncGroupsAction;
 import org.asamk.signal.manager.actions.SendSyncKeysAction;
@@ -271,7 +272,9 @@ public final class IncomingMessageHandler {
             if (rm.isKeysRequest()) {
                 actions.add(SendSyncKeysAction.create());
             }
-            // TODO Handle rm.isConfigurationRequest();
+            if (rm.isConfigurationRequest()) {
+                actions.add(SendSyncConfigurationAction.create());
+            }
         }
         if (syncMessage.getGroups().isPresent()) {
             logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
@@ -353,7 +356,13 @@ public final class IncomingMessageHandler {
             }
         }
         if (syncMessage.getConfiguration().isPresent()) {
-            // TODO
+            final var configurationMessage = syncMessage.getConfiguration().get();
+            account.getConfigurationStore().setReadReceipts(configurationMessage.getReadReceipts().orNull());
+            account.getConfigurationStore().setLinkPreviews(configurationMessage.getLinkPreviews().orNull());
+            account.getConfigurationStore().setTypingIndicators(configurationMessage.getTypingIndicators().orNull());
+            account.getConfigurationStore()
+                    .setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
+                            .orNull());
         }
         return actions;
     }
index 6db1ca7d838ca51fe1bcc6d8cece558545315643..e3fc7fc26676e7f7e284ef5d80b1d75f4c8cc12f 100644 (file)
@@ -14,6 +14,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
 import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
 import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
 import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
+import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream;
@@ -221,6 +222,15 @@ public class SyncHelper {
         sendHelper.sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage));
     }
 
+    public void sendConfigurationMessage() throws IOException {
+        final var config = account.getConfigurationStore();
+        var configurationMessage = new ConfigurationMessage(Optional.fromNullable(config.getReadReceipts()),
+                Optional.fromNullable(config.getUnidentifiedDeliveryIndicators()),
+                Optional.fromNullable(config.getTypingIndicators()),
+                Optional.fromNullable(config.getLinkPreviews()));
+        sendHelper.sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage));
+    }
+
     public void handleSyncDeviceContacts(final InputStream input) throws IOException {
         final var s = new DeviceContactsInputStream(input);
         DeviceContact c;
index fd4ec5973add935be77aa234a1f7f60940e8be0e..9c51017c4bcd3e64f8be7e36765570dd204fe809 100644 (file)
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
 import org.asamk.signal.manager.TrustLevel;
+import org.asamk.signal.manager.configuration.ConfigurationStore;
 import org.asamk.signal.manager.groups.GroupId;
 import org.asamk.signal.manager.storage.contacts.ContactsStore;
 import org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore;
@@ -103,6 +104,8 @@ public class SignalAccount implements Closeable {
     private RecipientStore recipientStore;
     private StickerStore stickerStore;
     private StickerStore.Storage stickerStoreStorage;
+    private ConfigurationStore configurationStore;
+    private ConfigurationStore.Storage configurationStoreStorage;
 
     private MessageCache messageCache;
 
@@ -159,6 +162,7 @@ public class SignalAccount implements Closeable {
                 account.recipientStore,
                 account::saveGroupStore);
         account.stickerStore = new StickerStore(account::saveStickerStore);
+        account.configurationStore = new ConfigurationStore(account::saveConfigurationStore);
 
         account.registered = false;
 
@@ -267,6 +271,7 @@ public class SignalAccount implements Closeable {
                 account.recipientStore,
                 account::saveGroupStore);
         account.stickerStore = new StickerStore(account::saveStickerStore);
+        account.configurationStore = new ConfigurationStore(account::saveConfigurationStore);
 
         account.recipientStore.resolveRecipientTrusted(account.getSelfAddress());
         account.migrateLegacyConfigs();
@@ -491,6 +496,15 @@ public class SignalAccount implements Closeable {
             stickerStore = new StickerStore(this::saveStickerStore);
         }
 
+        if (rootNode.hasNonNull("configurationStore")) {
+            configurationStoreStorage = jsonProcessor.convertValue(rootNode.get("configurationStore"),
+                    ConfigurationStore.Storage.class);
+            configurationStore = ConfigurationStore.fromStorage(configurationStoreStorage,
+                    this::saveConfigurationStore);
+        } else {
+            configurationStore = new ConfigurationStore(this::saveConfigurationStore);
+        }
+
         migratedLegacyConfig = loadLegacyThreadStore(rootNode) || migratedLegacyConfig;
 
         if (migratedLegacyConfig) {
@@ -677,6 +691,11 @@ public class SignalAccount implements Closeable {
         save();
     }
 
+    private void saveConfigurationStore(ConfigurationStore.Storage storage) {
+        this.configurationStoreStorage = storage;
+        save();
+    }
+
     private void save() {
         synchronized (fileChannel) {
             var rootNode = jsonProcessor.createObjectNode();
@@ -707,7 +726,8 @@ public class SignalAccount implements Closeable {
                             profileKey == null ? null : Base64.getEncoder().encodeToString(profileKey.serialize()))
                     .put("registered", registered)
                     .putPOJO("groupStore", groupStoreStorage)
-                    .putPOJO("stickerStore", stickerStoreStorage);
+                    .putPOJO("stickerStore", stickerStoreStorage)
+                    .putPOJO("configurationStore", configurationStoreStorage);
             try {
                 try (var output = new ByteArrayOutputStream()) {
                     // Write to memory first to prevent corrupting the file in case of serialization errors
@@ -797,6 +817,10 @@ public class SignalAccount implements Closeable {
         return senderKeyStore;
     }
 
+    public ConfigurationStore getConfigurationStore() {
+        return configurationStore;
+    }
+
     public MessageCache getMessageCache() {
         return messageCache;
     }
index 573ade7c432e01d4f7b98aa94284209c78dff09e..9829fe0006cb009dc0768310636275e36c797f44 100644 (file)
@@ -113,6 +113,23 @@ Can fix problems with receiving messages.
 *-n* NAME, *--device-name* NAME::
 Set a new device name for the main or linked device
 
+=== updateConfiguration
+
+Update signal configs and sync them to linked devices.
+This command only works on the main devices.
+
+*--read-receipts* {true,false}::
+Indicates if Signal should send read receipts.
+
+*--unidentified-delivery-indicators* {true,false}::
+Indicates if Signal should show unidentified delivery indicators.
+
+*--typing-indicators* {true,false}::
+Indicates if Signal should send/show typing indicators.
+
+*--link-previews* {true,false}::
+Indicates if Signal should generate link previews.
+
 === setPin
 
 Set a registration lock pin, to prevent others from registering this number.
index 5d637eee0c9833f18ea133bae4c38cb003c42e29..1d6dd26df240478ab55cc01466c45e25a0d83b16 100644 (file)
@@ -39,6 +39,7 @@ public class Commands {
         addCommand(new UnblockCommand());
         addCommand(new UnregisterCommand());
         addCommand(new UpdateAccountCommand());
+        addCommand(new UpdateConfigurationCommand());
         addCommand(new UpdateContactCommand());
         addCommand(new UpdateGroupCommand());
         addCommand(new UpdateProfileCommand());
diff --git a/src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java b/src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java
new file mode 100644 (file)
index 0000000..9ca126d
--- /dev/null
@@ -0,0 +1,55 @@
+package org.asamk.signal.commands;
+
+import net.sourceforge.argparse4j.inf.Namespace;
+import net.sourceforge.argparse4j.inf.Subparser;
+
+import org.asamk.signal.OutputWriter;
+import org.asamk.signal.commands.exceptions.CommandException;
+import org.asamk.signal.commands.exceptions.IOErrorException;
+import org.asamk.signal.commands.exceptions.UserErrorException;
+import org.asamk.signal.manager.Manager;
+import org.asamk.signal.manager.NotMasterDeviceException;
+
+import java.io.IOException;
+
+public class UpdateConfigurationCommand implements JsonRpcLocalCommand {
+
+    @Override
+    public String getName() {
+        return "updateConfiguration";
+    }
+
+    @Override
+    public void attachToSubparser(final Subparser subparser) {
+        subparser.help("Update signal configs and sync them to linked devices.");
+        subparser.addArgument("--read-receipts")
+                .type(Boolean.class)
+                .help("Indicates if Signal should send read receipts.");
+        subparser.addArgument("--unidentified-delivery-indicators")
+                .type(Boolean.class)
+                .help("Indicates if Signal should show unidentified delivery indicators.");
+        subparser.addArgument("--typing-indicators")
+                .type(Boolean.class)
+                .help("Indicates if Signal should send/show typing indicators.");
+        subparser.addArgument("--link-previews")
+                .type(Boolean.class)
+                .help("Indicates if Signal should generate link previews.");
+    }
+
+    @Override
+    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");
+        try {
+            m.updateConfiguration(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
+        } catch (IOException e) {
+            throw new IOErrorException("UpdateAccount error: " + e.getMessage(), e);
+        } catch (NotMasterDeviceException e) {
+            throw new UserErrorException("This command doesn't work on linked devices.");
+        }
+    }
+}
index b9f5ae11ee2c4165aa82dcfe19963b5cb0c68338..ea776797b7182c3c841804e734667ba367cfd6b5 100644 (file)
@@ -93,6 +93,16 @@ public class DbusManagerImpl implements Manager {
         }
     }
 
+    @Override
+    public void updateConfiguration(
+            final Boolean readReceipts,
+            final Boolean unidentifiedDeliveryIndicators,
+            final Boolean typingIndicators,
+            final Boolean linkPreviews
+    ) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void setProfile(
             final String givenName,