]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/manager/Manager.java
Update libsignal
[signal-cli] / src / main / java / org / asamk / signal / manager / Manager.java
index 3c352cd475d46f5ec730c7576a530dff6c51759e..c4f32460d97692d75262be1d3cd08ba6450a5510 100644 (file)
@@ -114,6 +114,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
+import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
@@ -121,9 +122,9 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
 import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
 import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
 import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
-import org.whispersystems.signalservice.api.push.ContactTokenDetails;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
+import org.whispersystems.signalservice.api.storage.StorageKey;
 import org.whispersystems.signalservice.api.util.InvalidNumberException;
 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
 import org.whispersystems.signalservice.api.util.SleepTimer;
@@ -138,6 +139,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
 import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
 import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
 import org.whispersystems.signalservice.internal.util.Hex;
+import org.whispersystems.signalservice.internal.util.Util;
 
 import java.io.Closeable;
 import java.io.File;
@@ -217,6 +219,7 @@ public class Manager implements Closeable {
                         account.getDeviceId()),
                 userAgent,
                 groupsV2Operations,
+                ServiceConfig.AUTOMATIC_NETWORK_RETRY,
                 timer);
         this.groupsV2Api = accountManager.getGroupsV2Api();
         final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager);
@@ -232,7 +235,8 @@ public class Manager implements Closeable {
                 userAgent,
                 null,
                 timer,
-                clientZkProfileOperations);
+                clientZkProfileOperations,
+                ServiceConfig.AUTOMATIC_NETWORK_RETRY);
 
         this.account.setResolver(this::resolveSignalServiceAddress);
 
@@ -325,11 +329,9 @@ public class Manager implements Closeable {
      */
     public Map<String, Boolean> areUsersRegistered(Set<String> numbers) throws IOException {
         // Note "contactDetails" has no optionals. It only gives us info on users who are registered
-        List<ContactTokenDetails> contactDetails = this.accountManager.getContacts(numbers);
+        Map<String, UUID> contactDetails = getRegisteredUsers(numbers);
 
-        Set<String> registeredUsers = contactDetails.stream()
-                .map(ContactTokenDetails::getNumber)
-                .collect(Collectors.toSet());
+        Set<String> registeredUsers = contactDetails.keySet();
 
         return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains));
     }
@@ -352,10 +354,19 @@ public class Manager implements Closeable {
      *               if it's Optional.absent(), the avatar will be removed
      */
     public void setProfile(String name, Optional<File> avatar) throws IOException {
+        // TODO
+        String about = null;
+        String aboutEmoji = null;
+
         try (final StreamDetails streamDetails = avatar == null
                 ? avatarStore.retrieveProfileAvatar(getSelfAddress())
                 : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) {
-            accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), name, streamDetails);
+            accountManager.setVersionedProfile(account.getUuid(),
+                    account.getProfileKey(),
+                    name,
+                    about,
+                    aboutEmoji,
+                    streamDetails);
         }
 
         if (avatar != null) {
@@ -366,6 +377,11 @@ public class Manager implements Closeable {
                 avatarStore.deleteProfileAvatar(getSelfAddress());
             }
         }
+
+        try {
+            sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE));
+        } catch (UntrustedIdentityException ignored) {
+        }
     }
 
     public void unregister() throws IOException {
@@ -373,6 +389,7 @@ public class Manager implements Closeable {
         // If this is the master device, other users can't send messages to this number anymore.
         // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore.
         accountManager.setGcmId(Optional.absent());
+        accountManager.deleteAccount();
 
         account.setRegistered(false);
         account.save();
@@ -412,6 +429,9 @@ public class Manager implements Closeable {
     }
 
     public void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException {
+        if (!account.isMasterDevice()) {
+            throw new RuntimeException("Only master device can set a PIN");
+        }
         if (pin.isPresent()) {
             final MasterKey masterKey = account.getPinMasterKey() != null
                     ? account.getPinMasterKey()
@@ -491,11 +511,18 @@ public class Manager implements Closeable {
                 Optional.absent(),
                 clientZkProfileOperations,
                 executor,
-                ServiceConfig.MAX_ENVELOPE_SIZE);
+                ServiceConfig.MAX_ENVELOPE_SIZE,
+                ServiceConfig.AUTOMATIC_NETWORK_RETRY);
     }
 
     private SignalProfile getRecipientProfile(
             SignalServiceAddress address
+    ) {
+        return getRecipientProfile(address, false);
+    }
+
+    private SignalProfile getRecipientProfile(
+            SignalServiceAddress address, boolean force
     ) {
         SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address);
         if (profileEntry == null) {
@@ -504,7 +531,9 @@ public class Manager implements Closeable {
         long now = new Date().getTime();
         // Profiles are cached for 24h before retrieving them again
         if (!profileEntry.isRequestPending() && (
-                profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000
+                force
+                        || profileEntry.getProfile() == null
+                        || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000
         )) {
             profileEntry.setRequestPending(true);
             final SignalServiceProfile encryptedProfile;
@@ -775,12 +804,10 @@ public class Manager implements Closeable {
                 newE164Members.add(member.getNumber().get());
             }
 
-            final List<ContactTokenDetails> contacts = accountManager.getContacts(newE164Members);
-            if (contacts.size() != newE164Members.size()) {
+            final Map<String, UUID> registeredUsers = getRegisteredUsers(newE164Members);
+            if (registeredUsers.size() != newE164Members.size()) {
                 // Some of the new members are not registered on Signal
-                for (ContactTokenDetails contact : contacts) {
-                    newE164Members.remove(contact.getNumber());
-                }
+                newE164Members.removeAll(registeredUsers.keySet());
                 throw new IOException("Failed to add members "
                         + String.join(", ", newE164Members)
                         + " to group: Not registered on Signal");
@@ -1155,6 +1182,18 @@ public class Manager implements Closeable {
         }
     }
 
+    void requestSyncKeys() throws IOException {
+        SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder()
+                .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
+                .build();
+        SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
+        try {
+            sendSyncMessage(message);
+        } catch (UntrustedIdentityException e) {
+            throw new AssertionError(e);
+        }
+    }
+
     private byte[] getSenderCertificate() {
         // TODO support UUID capable sender certificates
         // byte[] certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy();
@@ -1184,28 +1223,29 @@ public class Manager implements Closeable {
 
     private Collection<SignalServiceAddress> getSignalServiceAddresses(Collection<String> numbers) throws InvalidNumberException {
         final Set<SignalServiceAddress> signalServiceAddresses = new HashSet<>(numbers.size());
-        final Set<SignalServiceAddress> missingUuids = new HashSet<>();
+        final Set<SignalServiceAddress> addressesMissingUuid = new HashSet<>();
 
         for (String number : numbers) {
             final SignalServiceAddress resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number);
             if (resolvedAddress.getUuid().isPresent()) {
                 signalServiceAddresses.add(resolvedAddress);
             } else {
-                missingUuids.add(resolvedAddress);
+                addressesMissingUuid.add(resolvedAddress);
             }
         }
 
+        final Set<String> numbersMissingUuid = addressesMissingUuid.stream()
+                .map(a -> a.getNumber().get())
+                .collect(Collectors.toSet());
         Map<String, UUID> registeredUsers;
         try {
-            registeredUsers = accountManager.getRegisteredUsers(getIasKeyStore(),
-                    missingUuids.stream().map(a -> a.getNumber().get()).collect(Collectors.toSet()),
-                    CDS_MRENCLAVE);
-        } catch (IOException | Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException e) {
+            registeredUsers = getRegisteredUsers(numbersMissingUuid);
+        } catch (IOException e) {
             logger.warn("Failed to resolve uuids from server, ignoring: {}", e.getMessage());
-            registeredUsers = new HashMap<>();
+            registeredUsers = Map.of();
         }
 
-        for (SignalServiceAddress address : missingUuids) {
+        for (SignalServiceAddress address : addressesMissingUuid) {
             final String number = address.getNumber().get();
             if (registeredUsers.containsKey(number)) {
                 final SignalServiceAddress newAddress = resolveSignalServiceAddress(new SignalServiceAddress(
@@ -1220,6 +1260,14 @@ public class Manager implements Closeable {
         return signalServiceAddresses;
     }
 
+    private Map<String, UUID> getRegisteredUsers(final Set<String> numbersMissingUuid) throws IOException {
+        try {
+            return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE);
+        } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
+            throw new IOException(e);
+        }
+    }
+
     private Pair<Long, List<SendMessageResult>> sendMessage(
             SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients
     ) throws IOException {
@@ -1356,10 +1404,13 @@ public class Manager implements Closeable {
             if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
                 org.whispersystems.libsignal.UntrustedIdentityException identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e
                         .getCause();
-                account.getSignalProtocolStore()
-                        .saveIdentity(resolveSignalServiceAddress(identityException.getName()),
-                                identityException.getUntrustedIdentity(),
-                                TrustLevel.UNTRUSTED);
+                final IdentityKey untrustedIdentity = identityException.getUntrustedIdentity();
+                if (untrustedIdentity != null) {
+                    account.getSignalProtocolStore()
+                            .saveIdentity(resolveSignalServiceAddress(identityException.getName()),
+                                    untrustedIdentity,
+                                    TrustLevel.UNTRUSTED);
+                }
                 throw identityException;
             }
             throw new AssertionError(e);
@@ -1988,6 +2039,21 @@ public class Manager implements Closeable {
                         account.getStickerStore().updateSticker(sticker);
                     }
                 }
+                if (syncMessage.getFetchType().isPresent()) {
+                    switch (syncMessage.getFetchType().get()) {
+                        case LOCAL_PROFILE:
+                            getRecipientProfile(getSelfAddress(), true);
+                        case STORAGE_MANIFEST:
+                            // TODO
+                    }
+                }
+                if (syncMessage.getKeys().isPresent()) {
+                    final KeysMessage keysMessage = syncMessage.getKeys().get();
+                    if (keysMessage.getStorageService().isPresent()) {
+                        final StorageKey storageKey = keysMessage.getStorageService().get();
+                        account.setStorageKey(storageKey);
+                    }
+                }
                 if (syncMessage.getConfiguration().isPresent()) {
                     // TODO
                 }
@@ -2279,8 +2345,20 @@ public class Manager implements Closeable {
         return account.getContactStore().getContacts();
     }
 
-    public ContactInfo getContact(String number) {
-        return account.getContactStore().getContact(Utils.getSignalServiceAddressFromIdentifier(number));
+    public String getContactOrProfileName(String number) {
+        final SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(number);
+
+        final ContactInfo contact = account.getContactStore().getContact(address);
+        if (contact != null && !Util.isEmpty(contact.name)) {
+            return contact.name;
+        }
+
+        final SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address);
+        if (profileEntry != null && profileEntry.getProfile() != null) {
+            return profileEntry.getProfile().getName();
+        }
+
+        return null;
     }
 
     public GroupInfo getGroup(GroupId groupId) {