]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/manager/Manager.java
Only allow setting PIN by master device
[signal-cli] / src / main / java / org / asamk / signal / manager / Manager.java
index a0f6aa532a1ef2313eb4deaf1fdf5b3a3eb3c1a3..b8a0f57b1681419b424168247e56d442ff738375 100644 (file)
@@ -121,7 +121,6 @@ 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.util.InvalidNumberException;
@@ -138,6 +137,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;
@@ -288,6 +288,22 @@ public class Manager implements Closeable {
         return new Manager(account, pathConfig, serviceConfiguration, userAgent);
     }
 
+    public static List<String> getAllLocalUsernames(File settingsPath) {
+        PathConfig pathConfig = PathConfig.createDefault(settingsPath);
+        final File dataPath = pathConfig.getDataPath();
+        final File[] files = dataPath.listFiles();
+
+        if (files == null) {
+            return List.of();
+        }
+
+        return Arrays.stream(files)
+                .filter(File::isFile)
+                .map(File::getName)
+                .filter(file -> PhoneNumberFormatter.isValidNumber(file, null))
+                .collect(Collectors.toList());
+    }
+
     public void checkAccountState() throws IOException {
         if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
             refreshPreKeys();
@@ -309,11 +325,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));
     }
@@ -350,6 +364,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 {
@@ -396,6 +415,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()
@@ -480,6 +502,12 @@ public class Manager implements Closeable {
 
     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) {
@@ -488,7 +516,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;
@@ -759,12 +789,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");
@@ -937,6 +965,17 @@ public class Manager implements Closeable {
         return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
     }
 
+    public Pair<Long, SendMessageResult> sendSelfMessage(
+            String messageText, List<String> attachments
+    ) throws IOException, AttachmentInvalidException {
+        final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
+                .withBody(messageText);
+        if (attachments != null) {
+            messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
+        }
+        return sendSelfMessage(messageBuilder);
+    }
+
     public Pair<Long, List<SendMessageResult>> sendMessageReaction(
             String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List<String> recipients
     ) throws IOException, InvalidNumberException {
@@ -1157,28 +1196,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(
@@ -1193,6 +1233,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 e) {
+            throw new IOException(e);
+        }
+    }
+
     private Pair<Long, List<SendMessageResult>> sendMessage(
             SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients
     ) throws IOException {
@@ -1237,11 +1285,7 @@ public class Manager implements Closeable {
                     final int expirationTime = contact != null ? contact.messageExpirationTime : 0;
                     messageBuilder.withExpiration(expirationTime);
                     message = messageBuilder.build();
-                    if (address.matches(account.getSelfAddress())) {
-                        results.add(sendSelfMessage(message));
-                    } else {
-                        results.add(sendMessage(address, message));
-                    }
+                    results.add(sendMessage(address, message));
                 }
                 return new Pair<>(timestamp, results);
             }
@@ -1255,6 +1299,28 @@ public class Manager implements Closeable {
         }
     }
 
+    private Pair<Long, SendMessageResult> sendSelfMessage(
+            SignalServiceDataMessage.Builder messageBuilder
+    ) throws IOException {
+        final long timestamp = System.currentTimeMillis();
+        messageBuilder.withTimestamp(timestamp);
+        getOrCreateMessagePipe();
+        getOrCreateUnidentifiedMessagePipe();
+        try {
+            final SignalServiceAddress address = getSelfAddress();
+
+            final ContactInfo contact = account.getContactStore().getContact(address);
+            final int expirationTime = contact != null ? contact.messageExpirationTime : 0;
+            messageBuilder.withExpiration(expirationTime);
+
+            SignalServiceDataMessage message = messageBuilder.build();
+            final SendMessageResult result = sendSelfMessage(message);
+            return new Pair<>(timestamp, result);
+        } finally {
+            account.save();
+        }
+    }
+
     private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
         SignalServiceMessageSender messageSender = createMessageSender();
 
@@ -1943,6 +2009,14 @@ 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.getConfiguration().isPresent()) {
                     // TODO
                 }
@@ -2234,8 +2308,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) {