]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/Manager.java
Correctly use API for sending non group messages
[signal-cli] / src / main / java / org / asamk / signal / Manager.java
index 94bb555af37c0b8e191a9b0b6dfdf6ec181e41e3..d626ccaf4c583a33b33453e96ed81a948c0848f0 100644 (file)
@@ -37,10 +37,13 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
 import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
 import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
 import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.crypto.SignalServiceCipher;
+import org.whispersystems.signalservice.api.crypto.*;
+import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
 import org.whispersystems.signalservice.api.messages.*;
+import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.signalservice.api.push.TrustStore;
+import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
 import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptions;
 import org.whispersystems.signalservice.api.util.InvalidNumberException;
 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
@@ -58,7 +61,10 @@ class Manager implements Signal {
 
     public final static String PROJECT_NAME = Manager.class.getPackage().getImplementationTitle();
     public final static String PROJECT_VERSION = Manager.class.getPackage().getImplementationVersion();
-    private final static String USER_AGENT = PROJECT_NAME + " " + PROJECT_VERSION;
+    private final static String USER_AGENT = PROJECT_NAME == null ? null : PROJECT_NAME + " " + PROJECT_VERSION;
+
+    private final static int PREKEY_MINIMUM_COUNT = 20;
+    private static final int PREKEY_BATCH_SIZE = 100;
 
     private final String settingsPath;
     private final String dataPath;
@@ -140,6 +146,14 @@ class Manager implements Signal {
             groupStore = new JsonGroupStore();
         }
         accountManager = new SignalServiceAccountManager(URL, TRUST_STORE, username, password, USER_AGENT);
+        try {
+            if (registered && accountManager.getPreKeysCount() < PREKEY_MINIMUM_COUNT) {
+                refreshPreKeys();
+                save();
+            }
+        } catch (AuthorizationFailedException e) {
+            System.err.println("Authorization failed, was the number registered elsewhere?");
+        }
     }
 
     private void save() {
@@ -187,12 +201,10 @@ class Manager implements Signal {
         save();
     }
 
-    private static final int BATCH_SIZE = 100;
-
     private List<PreKeyRecord> generatePreKeys() {
         List<PreKeyRecord> records = new LinkedList<>();
 
-        for (int i = 0; i < BATCH_SIZE; i++) {
+        for (int i = 0; i < PREKEY_BATCH_SIZE; i++) {
             int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
             ECKeyPair keyPair = Curve.generateKeyPair();
             PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair);
@@ -201,13 +213,13 @@ class Manager implements Signal {
             records.add(record);
         }
 
-        preKeyIdOffset = (preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE;
+        preKeyIdOffset = (preKeyIdOffset + PREKEY_BATCH_SIZE + 1) % Medium.MAX_VALUE;
         save();
 
         return records;
     }
 
-    private PreKeyRecord generateLastResortPreKey() {
+    private PreKeyRecord getOrGenerateLastResortPreKey() {
         if (signalProtocolStore.containsPreKey(Medium.MAX_VALUE)) {
             try {
                 return signalProtocolStore.loadPreKey(Medium.MAX_VALUE);
@@ -249,14 +261,16 @@ class Manager implements Signal {
         //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
         registered = true;
 
-        List<PreKeyRecord> oneTimePreKeys = generatePreKeys();
-
-        PreKeyRecord lastResortKey = generateLastResortPreKey();
+        refreshPreKeys();
+        save();
+    }
 
+    private void refreshPreKeys() throws IOException {
+        List<PreKeyRecord> oneTimePreKeys = generatePreKeys();
+        PreKeyRecord lastResortKey = getOrGenerateLastResortPreKey();
         SignedPreKeyRecord signedPreKeyRecord = generateSignedPreKey(signalProtocolStore.getIdentityKeyPair());
 
         accountManager.setPreKeys(signalProtocolStore.getIdentityKeyPair().getPublicKey(), lastResortKey, signedPreKeyRecord, oneTimePreKeys);
-        save();
     }
 
 
@@ -286,7 +300,7 @@ class Manager implements Signal {
     @Override
     public void sendGroupMessage(String messageText, List<String> attachments,
                                  byte[] groupId)
-            throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+            throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, UntrustedIdentityException {
         final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
         if (attachments != null) {
             messageBuilder.withAttachments(getSignalServiceAttachments(attachments));
@@ -302,7 +316,7 @@ class Manager implements Signal {
         sendMessage(message, groupStore.getGroup(groupId).members);
     }
 
-    public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions {
+    public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions, UntrustedIdentityException {
         SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT)
                 .withId(groupId)
                 .build();
@@ -314,7 +328,7 @@ class Manager implements Signal {
         sendMessage(message, groupStore.getGroup(groupId).members);
     }
 
-    public byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<String> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+    public byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<String> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, UntrustedIdentityException {
         GroupInfo g;
         if (groupId == null) {
             // Create new group
@@ -367,7 +381,7 @@ class Manager implements Signal {
 
     @Override
     public void sendMessage(String message, List<String> attachments, String recipient)
-            throws EncapsulatedExceptions, AttachmentInvalidException, IOException {
+            throws EncapsulatedExceptions, AttachmentInvalidException, IOException, UntrustedIdentityException {
         List<String> recipients = new ArrayList<>(1);
         recipients.add(recipient);
         sendMessage(message, attachments, recipients);
@@ -376,7 +390,7 @@ class Manager implements Signal {
     @Override
     public void sendMessage(String messageText, List<String> attachments,
                             List<String> recipients)
-            throws IOException, EncapsulatedExceptions, AttachmentInvalidException {
+            throws IOException, EncapsulatedExceptions, AttachmentInvalidException, UntrustedIdentityException {
         final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
         if (attachments != null) {
             messageBuilder.withAttachments(getSignalServiceAttachments(attachments));
@@ -387,7 +401,7 @@ class Manager implements Signal {
     }
 
     @Override
-    public void sendEndSessionMessage(List<String> recipients) throws IOException, EncapsulatedExceptions {
+    public void sendEndSessionMessage(List<String> recipients) throws IOException, EncapsulatedExceptions, UntrustedIdentityException {
         SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
                 .asEndSessionMessage()
                 .build();
@@ -396,7 +410,7 @@ class Manager implements Signal {
     }
 
     private void sendMessage(SignalServiceDataMessage message, Collection<String> recipients)
-            throws IOException, EncapsulatedExceptions {
+            throws IOException, EncapsulatedExceptions, UntrustedIdentityException {
         SignalServiceMessageSender messageSender = new SignalServiceMessageSender(URL, TRUST_STORE, username, password,
                 signalProtocolStore, USER_AGENT, Optional.<SignalServiceMessageSender.EventListener>absent());
 
@@ -412,7 +426,14 @@ class Manager implements Signal {
             }
         }
 
-        messageSender.sendMessage(new ArrayList<>(recipientsTS), message);
+        if (message.getGroupInfo().isPresent()) {
+            messageSender.sendMessage(new ArrayList<>(recipientsTS), message);
+        } else {
+            // Send to all individually, so sync messages are sent correctly
+            for (SignalServiceAddress address : recipientsTS) {
+                messageSender.sendMessage(address, message);
+            }
+        }
 
         if (message.isEndSession()) {
             for (SignalServiceAddress recipient : recipientsTS) {
@@ -441,6 +462,73 @@ class Manager implements Signal {
         void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent decryptedContent, GroupInfo group);
     }
 
+    private GroupInfo handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, String source, String destination) {
+        GroupInfo group = null;
+        if (message.getGroupInfo().isPresent()) {
+            SignalServiceGroup groupInfo = message.getGroupInfo().get();
+            switch (groupInfo.getType()) {
+                case UPDATE:
+                    try {
+                        group = groupStore.getGroup(groupInfo.getGroupId());
+                    } catch (GroupNotFoundException e) {
+                        group = new GroupInfo(groupInfo.getGroupId());
+                    }
+
+                    if (groupInfo.getAvatar().isPresent()) {
+                        SignalServiceAttachment avatar = groupInfo.getAvatar().get();
+                        if (avatar.isPointer()) {
+                            long avatarId = avatar.asPointer().getId();
+                            try {
+                                retrieveAttachment(avatar.asPointer());
+                                group.avatarId = avatarId;
+                            } catch (IOException | InvalidMessageException e) {
+                                System.err.println("Failed to retrieve group avatar (" + avatarId + "): " + e.getMessage());
+                            }
+                        }
+                    }
+
+                    if (groupInfo.getName().isPresent()) {
+                        group.name = groupInfo.getName().get();
+                    }
+
+                    if (groupInfo.getMembers().isPresent()) {
+                        group.members.addAll(groupInfo.getMembers().get());
+                    }
+
+                    groupStore.updateGroup(group);
+                    break;
+                case DELIVER:
+                    try {
+                        group = groupStore.getGroup(groupInfo.getGroupId());
+                    } catch (GroupNotFoundException e) {
+                    }
+                    break;
+                case QUIT:
+                    try {
+                        group = groupStore.getGroup(groupInfo.getGroupId());
+                        group.members.remove(source);
+                    } catch (GroupNotFoundException e) {
+                    }
+                    break;
+            }
+        }
+        if (message.isEndSession()) {
+            handleEndSession(isSync ? destination : source);
+        }
+        if (message.getAttachments().isPresent()) {
+            for (SignalServiceAttachment attachment : message.getAttachments().get()) {
+                if (attachment.isPointer()) {
+                    try {
+                        retrieveAttachment(attachment.asPointer());
+                    } catch (IOException | InvalidMessageException e) {
+                        System.err.println("Failed to retrieve attachment (" + attachment.asPointer().getId() + "): " + e.getMessage());
+                    }
+                }
+            }
+        }
+        return group;
+    }
+
     public void receiveMessages(int timeoutSeconds, boolean returnOnTimeout, ReceiveMessageHandler handler) throws IOException {
         final SignalServiceMessageReceiver messageReceiver = new SignalServiceMessageReceiver(URL, TRUST_STORE, username, password, signalingKey, USER_AGENT);
         SignalServiceMessagePipe messagePipe = null;
@@ -459,67 +547,19 @@ class Manager implements Signal {
                         if (content != null) {
                             if (content.getDataMessage().isPresent()) {
                                 SignalServiceDataMessage message = content.getDataMessage().get();
-                                if (message.getGroupInfo().isPresent()) {
-                                    SignalServiceGroup groupInfo = message.getGroupInfo().get();
-                                    switch (groupInfo.getType()) {
-                                        case UPDATE:
-                                            try {
-                                                group = groupStore.getGroup(groupInfo.getGroupId());
-                                            } catch (GroupNotFoundException e) {
-                                                group = new GroupInfo(groupInfo.getGroupId());
-                                            }
-
-                                            if (groupInfo.getAvatar().isPresent()) {
-                                                SignalServiceAttachment avatar = groupInfo.getAvatar().get();
-                                                if (avatar.isPointer()) {
-                                                    long avatarId = avatar.asPointer().getId();
-                                                    try {
-                                                        retrieveAttachment(avatar.asPointer());
-                                                        group.avatarId = avatarId;
-                                                    } catch (IOException | InvalidMessageException e) {
-                                                        System.err.println("Failed to retrieve group avatar (" + avatarId + "): " + e.getMessage());
-                                                    }
-                                                }
-                                            }
-
-                                            if (groupInfo.getName().isPresent()) {
-                                                group.name = groupInfo.getName().get();
-                                            }
-
-                                            if (groupInfo.getMembers().isPresent()) {
-                                                group.members.addAll(groupInfo.getMembers().get());
-                                            }
-
-                                            groupStore.updateGroup(group);
-                                            break;
-                                        case DELIVER:
-                                            try {
-                                                group = groupStore.getGroup(groupInfo.getGroupId());
-                                            } catch (GroupNotFoundException e) {
-                                            }
-                                            break;
-                                        case QUIT:
-                                            try {
-                                                group = groupStore.getGroup(groupInfo.getGroupId());
-                                                group.members.remove(envelope.getSource());
-                                            } catch (GroupNotFoundException e) {
-                                            }
-                                            break;
-                                    }
+                                group = handleSignalServiceDataMessage(message, false, envelope.getSource(), username);
+                            }
+                            if (content.getSyncMessage().isPresent()) {
+                                SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
+                                if (syncMessage.getSent().isPresent()) {
+                                    SignalServiceDataMessage message = syncMessage.getSent().get().getMessage();
+                                    group = handleSignalServiceDataMessage(message, true, envelope.getSource(), syncMessage.getSent().get().getDestination().get());
                                 }
-                                if (message.isEndSession()) {
-                                    handleEndSession(envelope.getSource());
+                                if (syncMessage.getRequest().isPresent()) {
+                                    // TODO
                                 }
-                                if (message.getAttachments().isPresent()) {
-                                    for (SignalServiceAttachment attachment : message.getAttachments().get()) {
-                                        if (attachment.isPointer()) {
-                                            try {
-                                                retrieveAttachment(attachment.asPointer());
-                                            } catch (IOException | InvalidMessageException e) {
-                                                System.err.println("Failed to retrieve attachment (" + attachment.asPointer().getId() + "): " + e.getMessage());
-                                            }
-                                        }
-                                    }
+                                if (syncMessage.getGroups().isPresent()) {
+                                    // TODO
                                 }
                             }
                         }