]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java
Cleanup manager package
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / ManagerImpl.java
index 5d091d29631f21d52010e79597c98d91cd507250..c15f1612ff82c7e2aad096a4650e604a7e543c64 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2015-2021 AsamK and contributors
+  Copyright (C) 2015-2022 AsamK and contributors
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
  */
 package org.asamk.signal.manager;
 
+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;
 import org.asamk.signal.manager.api.Identity;
 import org.asamk.signal.manager.api.InactiveGroupLinkException;
 import org.asamk.signal.manager.api.InvalidDeviceLinkException;
+import org.asamk.signal.manager.api.InvalidStickerException;
 import org.asamk.signal.manager.api.Message;
+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.SendMessageResult;
 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.TypingAction;
 import org.asamk.signal.manager.api.UnregisteredRecipientException;
 import org.asamk.signal.manager.api.UpdateGroup;
@@ -46,8 +53,10 @@ import org.asamk.signal.manager.storage.recipients.Contact;
 import org.asamk.signal.manager.storage.recipients.Profile;
 import org.asamk.signal.manager.storage.recipients.RecipientAddress;
 import org.asamk.signal.manager.storage.recipients.RecipientId;
+import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack;
+import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore;
 import org.asamk.signal.manager.storage.stickers.Sticker;
-import org.asamk.signal.manager.storage.stickers.StickerPackId;
+import org.asamk.signal.manager.util.AttachmentUtils;
 import org.asamk.signal.manager.util.KeyUtils;
 import org.asamk.signal.manager.util.StickerUtils;
 import org.slf4j.Logger;
@@ -67,9 +76,6 @@ import org.whispersystems.signalservice.internal.util.Util;
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -80,12 +86,15 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-public class ManagerImpl implements Manager {
+import io.reactivex.rxjava3.disposables.CompositeDisposable;
+
+class ManagerImpl implements Manager {
 
     private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class);
 
@@ -100,6 +109,7 @@ public class ManagerImpl implements Manager {
     private final Set<ReceiveMessageHandler> weakHandlers = new HashSet<>();
     private final Set<ReceiveMessageHandler> messageHandlers = new HashSet<>();
     private final List<Runnable> closedListeners = new ArrayList<>();
+    private final CompositeDisposable disposable = new CompositeDisposable();
 
     ManagerImpl(
             SignalAccount account,
@@ -140,6 +150,20 @@ public class ManagerImpl implements Manager {
                 this.notifyAll();
             }
         });
+        disposable.add(account.getIdentityKeyStore().getIdentityChanges().subscribe(recipientId -> {
+            logger.trace("Archiving old sessions for {}", recipientId);
+            account.getSessionStore().archiveSessions(recipientId);
+            account.getSenderKeyStore().deleteSharedWith(recipientId);
+            final var profile = account.getRecipientStore().getProfile(recipientId);
+            if (profile != null) {
+                account.getRecipientStore()
+                        .storeProfile(recipientId,
+                                Profile.newBuilder(profile)
+                                        .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN)
+                                        .withLastUpdateTimestamp(0)
+                                        .build());
+            }
+        }));
     }
 
     @Override
@@ -147,8 +171,7 @@ public class ManagerImpl implements Manager {
         return account.getAccount();
     }
 
-    @Override
-    public void checkAccountState() throws IOException {
+    void checkAccountState() throws IOException {
         context.getAccountHelper().checkAccountState();
     }
 
@@ -271,7 +294,7 @@ public class ManagerImpl implements Manager {
     }
 
     @Override
-    public void removeLinkedDevices(long deviceId) throws IOException {
+    public void removeLinkedDevices(int deviceId) throws IOException {
         context.getAccountHelper().removeLinkedDevices(deviceId);
     }
 
@@ -484,7 +507,7 @@ public class ManagerImpl implements Manager {
     @Override
     public SendMessageResults sendMessage(
             Message message, Set<RecipientIdentifier> recipients
-    ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException {
+    ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException {
         final var messageBuilder = SignalServiceDataMessage.newBuilder();
         applyMessage(messageBuilder, message);
         return sendMessage(messageBuilder, recipients);
@@ -492,7 +515,7 @@ public class ManagerImpl implements Manager {
 
     private void applyMessage(
             final SignalServiceDataMessage.Builder messageBuilder, final Message message
-    ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException {
+    ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException, InvalidStickerException {
         messageBuilder.withBody(message.messageText());
         final var attachments = message.attachments();
         if (attachments != null) {
@@ -510,6 +533,30 @@ public class ManagerImpl implements Manager {
                     List.of(),
                     resolveMentions(quote.mentions())));
         }
+        if (message.sticker().isPresent()) {
+            final var sticker = message.sticker().get();
+            final var packId = StickerPackId.deserialize(sticker.packId());
+            final var stickerId = sticker.stickerId();
+
+            final var stickerPack = context.getAccount().getStickerStore().getStickerPack(packId);
+            if (stickerPack == null) {
+                throw new InvalidStickerException("Sticker pack not found");
+            }
+            final var manifest = context.getStickerHelper().getOrRetrieveStickerPack(packId, stickerPack.getPackKey());
+            if (manifest.stickers().size() <= stickerId) {
+                throw new InvalidStickerException("Sticker id not part of this pack");
+            }
+            final var manifestSticker = manifest.stickers().get(stickerId);
+            final var streamDetails = context.getStickerPackStore().retrieveSticker(packId, stickerId);
+            if (streamDetails == null) {
+                throw new InvalidStickerException("Missing local sticker file");
+            }
+            messageBuilder.withSticker(new SignalServiceDataMessage.Sticker(packId.serialize(),
+                    stickerPack.getPackKey(),
+                    stickerId,
+                    manifestSticker.emoji(),
+                    AttachmentUtils.createAttachment(streamDetails, Optional.absent())));
+        }
     }
 
     private ArrayList<SignalServiceDataMessage.Mention> resolveMentions(final List<Message.Mention> mentionList) throws IOException, UnregisteredRecipientException {
@@ -529,6 +576,17 @@ public class ManagerImpl implements Manager {
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
         var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
         final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete);
+        for (final var recipient : recipients) {
+            if (recipient instanceof RecipientIdentifier.Single r) {
+                try {
+                    final var recipientId = context.getRecipientHelper().resolveRecipient(r);
+                    account.getMessageSendLogStore().deleteEntryForRecipientNonGroup(targetSentTimestamp, recipientId);
+                } catch (UnregisteredRecipientException ignored) {
+                }
+            } else if (recipient instanceof RecipientIdentifier.Group r) {
+                account.getMessageSendLogStore().deleteEntryForGroup(targetSentTimestamp, r.groupId());
+            }
+        }
         return sendMessage(messageBuilder, recipients);
     }
 
@@ -631,7 +689,7 @@ public class ManagerImpl implements Manager {
     }
 
     @Override
-    public URI uploadStickerPack(File path) throws IOException, StickerPackInvalidException {
+    public StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException {
         var manifest = StickerUtils.getSignalServiceStickerManifestUpload(path);
 
         var messageSender = dependencies.getMessageSender();
@@ -643,17 +701,30 @@ public class ManagerImpl implements Manager {
         var sticker = new Sticker(packId, packKey);
         account.getStickerStore().updateSticker(sticker);
 
-        try {
-            return new URI("https",
-                    "signal.art",
-                    "/addstickers/",
-                    "pack_id="
-                            + URLEncoder.encode(Hex.toStringCondensed(packId.serialize()), StandardCharsets.UTF_8)
-                            + "&pack_key="
-                            + URLEncoder.encode(Hex.toStringCondensed(packKey), StandardCharsets.UTF_8));
-        } catch (URISyntaxException e) {
-            throw new AssertionError(e);
-        }
+        return new StickerPackUrl(packId, packKey);
+    }
+
+    @Override
+    public List<StickerPack> getStickerPacks() {
+        final var stickerPackStore = context.getStickerPackStore();
+        return account.getStickerStore().getStickerPacks().stream().map(pack -> {
+            if (stickerPackStore.existsStickerPack(pack.getPackId())) {
+                try {
+                    final var manifest = stickerPackStore.retrieveManifest(pack.getPackId());
+                    return new StickerPack(pack.getPackId(),
+                            new StickerPackUrl(pack.getPackId(), pack.getPackKey()),
+                            pack.isInstalled(),
+                            manifest.title(),
+                            manifest.author(),
+                            java.util.Optional.ofNullable(manifest.cover() == null ? null : manifest.cover().toApi()),
+                            manifest.stickers().stream().map(JsonStickerPack.JsonSticker::toApi).toList());
+                } catch (Exception e) {
+                    logger.warn("Failed to read local sticker pack manifest: {}", e.getMessage(), e);
+                }
+            }
+
+            return new StickerPack(pack.getPackId(), pack.getPackKey(), pack.isInstalled());
+        }).toList();
     }
 
     @Override
@@ -683,6 +754,8 @@ public class ManagerImpl implements Manager {
         }
     }
 
+    private static final AtomicInteger threadNumber = new AtomicInteger(0);
+
     private void startReceiveThreadIfRequired() {
         if (receiveThread != null) {
             return;
@@ -718,6 +791,7 @@ public class ManagerImpl implements Manager {
                 }
             }
         });
+        receiveThread.setName("receive-" + threadNumber.getAndIncrement());
 
         receiveThread.start();
     }
@@ -944,6 +1018,7 @@ public class ManagerImpl implements Manager {
         executor.shutdown();
 
         dependencies.getSignalWebSocket().disconnect();
+        disposable.dispose();
 
         synchronized (closedListeners) {
             closedListeners.forEach(Runnable::run);