]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/manager/Manager.java
Output json when receiving messages from dbus and --json parameter is given
[signal-cli] / src / main / java / org / asamk / signal / manager / Manager.java
index 3cf345c87156e9ed304a0612e5ad92c01088f173..11c49d844723a60fb95731bee401446edf0e38fa 100644 (file)
@@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.asamk.Signal;
 import org.asamk.signal.AttachmentInvalidException;
 import org.asamk.signal.GroupNotFoundException;
-import org.asamk.signal.JsonStickerPack;
 import org.asamk.signal.NotAGroupMemberException;
 import org.asamk.signal.StickerPackInvalidException;
 import org.asamk.signal.TrustLevel;
@@ -106,6 +105,7 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException;
 import org.whispersystems.signalservice.api.util.SleepTimer;
 import org.whispersystems.signalservice.api.util.StreamDetails;
 import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
+import org.whispersystems.signalservice.api.util.UuidUtil;
 import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
 import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
 import org.whispersystems.signalservice.internal.util.Hex;
@@ -133,19 +133,17 @@ import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 public class Manager implements Signal {
 
-    private static final SignalServiceProfile.Capabilities capabilities = new SignalServiceProfile.Capabilities(false, false);
-
     private final String settingsPath;
     private final String dataPath;
     private final String attachmentsPath;
@@ -192,6 +190,10 @@ public class Manager implements Signal {
     }
 
     private String getMessageCachePath(String sender) {
+        if (sender == null || sender.isEmpty()) {
+            return getMessageCachePath();
+        }
+
         return getMessageCachePath() + "/" + sender.replace("/", "_");
     }
 
@@ -210,6 +212,7 @@ public class Manager implements Signal {
             return;
         }
         account = SignalAccount.load(dataPath, username);
+        account.setResolver(this::resolveSignalServiceAddress);
 
         migrateLegacyConfigs();
 
@@ -257,9 +260,11 @@ public class Manager implements Signal {
         int registrationId = KeyHelper.generateRegistrationId(false);
         if (username == null) {
             account = SignalAccount.createTemporaryAccount(identityKey, registrationId);
+            account.setResolver(this::resolveSignalServiceAddress);
         } else {
             ProfileKey profileKey = KeyUtils.createProfileKey();
             account = SignalAccount.create(dataPath, username, identityKey, registrationId, profileKey);
+            account.setResolver(this::resolveSignalServiceAddress);
             account.save();
         }
     }
@@ -287,7 +292,7 @@ public class Manager implements Signal {
     }
 
     public void updateAccountAttributes() throws IOException {
-        accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, capabilities);
+        accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, BaseConfig.capabilities);
     }
 
     public void setProfileName(String name) throws IOException {
@@ -348,6 +353,7 @@ public class Manager implements Signal {
             }
         }
         account = SignalAccount.createLinkedAccount(dataPath, username, ret.getUuid(), account.getPassword(), ret.getDeviceId(), ret.getIdentity(), account.getSignalProtocolStore().getLocalRegistrationId(), account.getSignalingKey(), profileKey);
+        account.setResolver(this::resolveSignalServiceAddress);
 
         refreshPreKeys();
 
@@ -425,12 +431,13 @@ public class Manager implements Signal {
         verificationCode = verificationCode.replace("-", "");
         account.setSignalingKey(KeyUtils.createSignalingKey());
         // TODO make unrestricted unidentified access configurable
-        UUID uuid = accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false, capabilities);
+        UUID uuid = accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false, BaseConfig.capabilities);
 
         //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
         account.setRegistered(true);
         account.setUuid(uuid);
         account.setRegistrationLockPin(pin);
+        account.getSignalProtocolStore().saveIdentity(account.getSelfAddress(), account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), TrustLevel.TRUSTED_VERIFIED);
 
         refreshPreKeys();
         account.save();
@@ -517,7 +524,7 @@ public class Manager implements Signal {
     }
 
     @Override
-    public void sendGroupMessage(String messageText, List<String> attachments,
+    public long sendGroupMessage(String messageText, List<String> attachments,
                                  byte[] groupId)
             throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
         final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
@@ -535,13 +542,13 @@ public class Manager implements Signal {
 
         messageBuilder.withExpiration(g.messageExpirationTime);
 
-        sendMessageLegacy(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
+        return sendMessageLegacy(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
     }
 
-    public void sendGroupMessageReaction(String emoji, boolean remove, SignalServiceAddress targetAuthor,
+    public void sendGroupMessageReaction(String emoji, boolean remove, String targetAuthor,
                                          long targetSentTimestamp, byte[] groupId)
-            throws IOException, EncapsulatedExceptions, AttachmentInvalidException {
-        SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp);
+            throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
+        SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp);
         final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
                 .withReaction(reaction);
         if (groupId != null) {
@@ -680,15 +687,15 @@ public class Manager implements Signal {
     }
 
     @Override
-    public void sendMessage(String message, List<String> attachments, String recipient)
+    public long sendMessage(String message, List<String> attachments, String recipient)
             throws EncapsulatedExceptions, AttachmentInvalidException, IOException, InvalidNumberException {
         List<String> recipients = new ArrayList<>(1);
         recipients.add(recipient);
-        sendMessage(message, attachments, recipients);
+        return sendMessage(message, attachments, recipients);
     }
 
     @Override
-    public void sendMessage(String messageText, List<String> attachments,
+    public long sendMessage(String messageText, List<String> attachments,
                             List<String> recipients)
             throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
         final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
@@ -708,13 +715,13 @@ public class Manager implements Signal {
 
             messageBuilder.withAttachments(attachmentPointers);
         }
-        sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
+        return sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
     }
 
-    public void sendMessageReaction(String emoji, boolean remove, SignalServiceAddress targetAuthor,
+    public void sendMessageReaction(String emoji, boolean remove, String targetAuthor,
                                     long targetSentTimestamp, List<String> recipients)
             throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
-        SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp);
+        SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp);
         final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
                 .withReaction(reaction);
         sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
@@ -730,8 +737,7 @@ public class Manager implements Signal {
 
     @Override
     public String getContactName(String number) throws InvalidNumberException {
-        String canonicalizedNumber = Utils.canonicalizeNumber(number, account.getUsername());
-        ContactInfo contact = account.getContactStore().getContact(new SignalServiceAddress(null, canonicalizedNumber));
+        ContactInfo contact = account.getContactStore().getContact(canonicalizeAndResolveSignalServiceAddress(number));
         if (contact == null) {
             return "";
         } else {
@@ -741,14 +747,13 @@ public class Manager implements Signal {
 
     @Override
     public void setContactName(String number, String name) throws InvalidNumberException {
-        String canonicalizedNumber = Utils.canonicalizeNumber(number, account.getUsername());
-        final SignalServiceAddress address = new SignalServiceAddress(null, canonicalizedNumber);
+        final SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number);
         ContactInfo contact = account.getContactStore().getContact(address);
         if (contact == null) {
             contact = new ContactInfo(address);
-            System.err.println("Add contact " + canonicalizedNumber + " named " + name);
+            System.err.println("Add contact " + contact.number + " named " + name);
         } else {
-            System.err.println("Updating contact " + canonicalizedNumber + " name " + contact.name + " -> " + name);
+            System.err.println("Updating contact " + contact.number + " name " + contact.name + " -> " + name);
         }
         contact.name = name;
         account.getContactStore().updateContact(contact);
@@ -757,14 +762,16 @@ public class Manager implements Signal {
 
     @Override
     public void setContactBlocked(String number, boolean blocked) throws InvalidNumberException {
-        number = Utils.canonicalizeNumber(number, account.getUsername());
-        final SignalServiceAddress address = new SignalServiceAddress(null, number);
+        setContactBlocked(canonicalizeAndResolveSignalServiceAddress(number), blocked);
+    }
+
+    private void setContactBlocked(SignalServiceAddress address, boolean blocked) {
         ContactInfo contact = account.getContactStore().getContact(address);
         if (contact == null) {
             contact = new ContactInfo(address);
-            System.err.println("Adding and " + (blocked ? "blocking" : "unblocking") + " contact " + number);
+            System.err.println("Adding and " + (blocked ? "blocking" : "unblocking") + " contact " + address.getNumber().orNull());
         } else {
-            System.err.println((blocked ? "Blocking" : "Unblocking") + " contact " + number);
+            System.err.println((blocked ? "Blocking" : "Unblocking") + " contact " + address.getNumber().orNull());
         }
         contact.blocked = blocked;
         account.getContactStore().updateContact(contact);
@@ -994,8 +1001,16 @@ public class Manager implements Signal {
         }
     }
 
-    private byte[] getSenderCertificate() throws IOException {
-        byte[] certificate = accountManager.getSenderCertificate();
+    private byte[] getSenderCertificate() {
+        // TODO support UUID capable sender certificates
+        // byte[] certificate = accountManager.getSenderCertificate();
+        byte[] certificate;
+        try {
+            certificate = accountManager.getSenderCertificateLegacy();
+        } catch (IOException e) {
+            System.err.println("Failed to get sender certificate: " + e);
+            return null;
+        }
         // TODO cache for a day
         return certificate;
     }
@@ -1019,7 +1034,7 @@ public class Manager implements Signal {
         }
     }
 
-    private byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) throws IOException {
+    private byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) {
         ContactInfo contact = account.getContactStore().getContact(recipient);
         if (contact == null || contact.profileKey == null) {
             return null;
@@ -1027,10 +1042,16 @@ public class Manager implements Signal {
         ProfileKey theirProfileKey;
         try {
             theirProfileKey = new ProfileKey(Base64.decode(contact.profileKey));
-        } catch (InvalidInputException e) {
+        } catch (InvalidInputException | IOException e) {
             throw new AssertionError(e);
         }
-        SignalProfile targetProfile = decryptProfile(getRecipientProfile(recipient, Optional.absent()), theirProfileKey);
+        SignalProfile targetProfile;
+        try {
+            targetProfile = decryptProfile(getRecipientProfile(recipient, Optional.absent()), theirProfileKey);
+        } catch (IOException e) {
+            System.err.println("Failed to get recipient profile: " + e);
+            return null;
+        }
 
         if (targetProfile == null || targetProfile.getUnidentifiedAccess() == null) {
             return null;
@@ -1043,7 +1064,7 @@ public class Manager implements Signal {
         return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
     }
 
-    private Optional<UnidentifiedAccessPair> getAccessForSync() throws IOException {
+    private Optional<UnidentifiedAccessPair> getAccessForSync() {
         byte[] selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey();
         byte[] selfUnidentifiedAccessCertificate = getSenderCertificate();
 
@@ -1061,7 +1082,7 @@ public class Manager implements Signal {
         }
     }
 
-    private List<Optional<UnidentifiedAccessPair>> getAccessFor(Collection<SignalServiceAddress> recipients) throws IOException {
+    private List<Optional<UnidentifiedAccessPair>> getAccessFor(Collection<SignalServiceAddress> recipients) {
         List<Optional<UnidentifiedAccessPair>> result = new ArrayList<>(recipients.size());
         for (SignalServiceAddress recipient : recipients) {
             result.add(getAccessFor(recipient));
@@ -1069,7 +1090,7 @@ public class Manager implements Signal {
         return result;
     }
 
-    private Optional<UnidentifiedAccessPair> getAccessFor(SignalServiceAddress recipient) throws IOException {
+    private Optional<UnidentifiedAccessPair> getAccessFor(SignalServiceAddress recipient) {
         byte[] recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
         byte[] selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey();
         byte[] selfUnidentifiedAccessCertificate = getSenderCertificate();
@@ -1088,13 +1109,23 @@ public class Manager implements Signal {
         }
     }
 
+    private Optional<UnidentifiedAccess> getUnidentifiedAccess(SignalServiceAddress recipient) {
+        Optional<UnidentifiedAccessPair> unidentifiedAccess = getAccessFor(recipient);
+
+        if (unidentifiedAccess.isPresent()) {
+            return unidentifiedAccess.get().getTargetUnidentifiedAccess();
+        }
+
+        return Optional.absent();
+    }
+
     private void sendSyncMessage(SignalServiceSyncMessage message)
             throws IOException, UntrustedIdentityException {
         SignalServiceMessageSender messageSender = getMessageSender();
         try {
             messageSender.sendMessage(message, getAccessForSync());
         } catch (UntrustedIdentityException e) {
-            account.getSignalProtocolStore().saveIdentity(e.getIdentifier(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
+            account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
             throw e;
         }
     }
@@ -1102,8 +1133,10 @@ public class Manager implements Signal {
     /**
      * This method throws an EncapsulatedExceptions exception instead of returning a list of SendMessageResult.
      */
-    private void sendMessageLegacy(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
+    private long sendMessageLegacy(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
             throws EncapsulatedExceptions, IOException {
+        final long timestamp = System.currentTimeMillis();
+        messageBuilder.withTimestamp(timestamp);
         List<SendMessageResult> results = sendMessage(messageBuilder, recipients);
 
         List<UntrustedIdentityException> untrustedIdentities = new LinkedList<>();
@@ -1112,33 +1145,24 @@ public class Manager implements Signal {
 
         for (SendMessageResult result : results) {
             if (result.isUnregisteredFailure()) {
-                unregisteredUsers.add(new UnregisteredUserException(result.getAddress().getNumber().get(), null));
+                unregisteredUsers.add(new UnregisteredUserException(result.getAddress().getLegacyIdentifier(), null));
             } else if (result.isNetworkFailure()) {
-                networkExceptions.add(new NetworkFailureException(result.getAddress().getNumber().get(), null));
+                networkExceptions.add(new NetworkFailureException(result.getAddress().getLegacyIdentifier(), null));
             } else if (result.getIdentityFailure() != null) {
-                untrustedIdentities.add(new UntrustedIdentityException("Untrusted", result.getAddress().getNumber().get(), result.getIdentityFailure().getIdentityKey()));
+                untrustedIdentities.add(new UntrustedIdentityException("Untrusted", result.getAddress().getLegacyIdentifier(), result.getIdentityFailure().getIdentityKey()));
             }
         }
         if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty() || !networkExceptions.isEmpty()) {
             throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers, networkExceptions);
         }
+        return timestamp;
     }
 
     private Collection<SignalServiceAddress> getSignalServiceAddresses(Collection<String> numbers) throws InvalidNumberException {
         final Set<SignalServiceAddress> signalServiceAddresses = new HashSet<>(numbers.size());
-        final String username = account.getUsername();
 
         for (String number : numbers) {
-            String canonicalizedNumber = Utils.canonicalizeNumber(number, username);
-            if (canonicalizedNumber.equals(username)) {
-                signalServiceAddresses.add(account.getSelfAddress());
-            } else {
-                SignalServiceAddress address = new SignalServiceAddress(null, canonicalizedNumber);
-                ContactInfo contact = account.getContactStore().getContact(address);
-                signalServiceAddresses.add(contact == null
-                        ? address
-                        : contact.getAddress());
-            }
+            signalServiceAddresses.add(canonicalizeAndResolveSignalServiceAddress(number));
         }
         return signalServiceAddresses;
     }
@@ -1156,18 +1180,18 @@ public class Manager implements Signal {
             SignalServiceMessageSender messageSender = getMessageSender();
 
             message = messageBuilder.build();
-            if (message.getGroupInfo().isPresent()) {
+            if (message.getGroupContext().isPresent()) {
                 try {
                     final boolean isRecipientUpdate = false;
                     List<SendMessageResult> result = messageSender.sendMessage(new ArrayList<>(recipients), getAccessFor(recipients), isRecipientUpdate, message);
                     for (SendMessageResult r : result) {
                         if (r.getIdentityFailure() != null) {
-                            account.getSignalProtocolStore().saveIdentity(r.getAddress().getNumber().get(), r.getIdentityFailure().getIdentityKey(), TrustLevel.UNTRUSTED);
+                            account.getSignalProtocolStore().saveIdentity(r.getAddress(), r.getIdentityFailure().getIdentityKey(), TrustLevel.UNTRUSTED);
                         }
                     }
                     return result;
                 } catch (UntrustedIdentityException e) {
-                    account.getSignalProtocolStore().saveIdentity(e.getIdentifier(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
+                    account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
                     return Collections.emptyList();
                 }
             } else if (recipients.size() == 1 && recipients.contains(account.getSelfAddress())) {
@@ -1185,7 +1209,7 @@ public class Manager implements Signal {
                 try {
                     messageSender.sendMessage(syncMessage, unidentifiedAccess);
                 } catch (UntrustedIdentityException e) {
-                    account.getSignalProtocolStore().saveIdentity(e.getIdentifier(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
+                    account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
                     results.add(SendMessageResult.identityFailure(recipient, e.getIdentityKey()));
                 }
                 return results;
@@ -1206,7 +1230,7 @@ public class Manager implements Signal {
                         SendMessageResult result = messageSender.sendMessage(address, getAccessFor(address), message);
                         results.add(result);
                     } catch (UntrustedIdentityException e) {
-                        account.getSignalProtocolStore().saveIdentity(e.getIdentifier(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
+                        account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
                         results.add(SendMessageResult.identityFailure(address, e.getIdentityKey()));
                     }
                 }
@@ -1215,31 +1239,34 @@ public class Manager implements Signal {
         } finally {
             if (message != null && message.isEndSession()) {
                 for (SignalServiceAddress recipient : recipients) {
-                    handleEndSession(recipient.getNumber().get());
+                    handleEndSession(recipient);
                 }
             }
             account.save();
         }
     }
 
-    private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, ProtocolUntrustedIdentityException, SelfSendException, UnsupportedDataMessageException {
+    private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException {
         SignalServiceCipher cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), Utils.getCertificateValidator());
         try {
             return cipher.decrypt(envelope);
         } catch (ProtocolUntrustedIdentityException e) {
-            // TODO We don't get the new untrusted identity from ProtocolUntrustedIdentityException anymore ... we need to get it from somewhere else
-//            account.getSignalProtocolStore().saveIdentity(e.getSender(), e.getUntrustedIdentity(), TrustLevel.UNTRUSTED);
-            throw e;
+            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);
+                throw identityException;
+            }
+            throw new AssertionError(e);
         }
     }
 
-    private void handleEndSession(String source) {
+    private void handleEndSession(SignalServiceAddress source) {
         account.getSignalProtocolStore().deleteAllSessions(source);
     }
 
     private void handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, SignalServiceAddress source, SignalServiceAddress destination, boolean ignoreAttachments) {
-        if (message.getGroupInfo().isPresent()) {
-            SignalServiceGroup groupInfo = message.getGroupInfo().get();
+        if (message.getGroupContext().isPresent() && message.getGroupContext().get().getGroupV1().isPresent()) {
+            SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
             GroupInfo group = account.getGroupStore().getGroup(groupInfo.getGroupId());
             switch (groupInfo.getType()) {
                 case UPDATE:
@@ -1263,7 +1290,10 @@ public class Manager implements Signal {
                     }
 
                     if (groupInfo.getMembers().isPresent()) {
-                        group.addMembers(groupInfo.getMembers().get());
+                        group.addMembers(groupInfo.getMembers().get()
+                                .stream()
+                                .map(this::resolveSignalServiceAddress)
+                                .collect(Collectors.toSet()));
                     }
 
                     account.getGroupStore().updateGroup(group);
@@ -1302,12 +1332,13 @@ public class Manager implements Signal {
                     break;
             }
         }
+        final SignalServiceAddress conversationPartnerAddress = isSync ? destination : source;
         if (message.isEndSession()) {
-            handleEndSession(isSync ? destination.getNumber().get() : source.getNumber().get());
+            handleEndSession(conversationPartnerAddress);
         }
         if (message.isExpirationUpdate() || message.getBody().isPresent()) {
-            if (message.getGroupInfo().isPresent()) {
-                SignalServiceGroup groupInfo = message.getGroupInfo().get();
+            if (message.getGroupContext().isPresent() && message.getGroupContext().get().getGroupV1().isPresent()) {
+                SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
                 GroupInfo group = account.getGroupStore().getGroup(groupInfo.getGroupId());
                 if (group == null) {
                     group = new GroupInfo(groupInfo.getGroupId());
@@ -1317,9 +1348,9 @@ public class Manager implements Signal {
                     account.getGroupStore().updateGroup(group);
                 }
             } else {
-                ContactInfo contact = account.getContactStore().getContact(isSync ? destination : source);
+                ContactInfo contact = account.getContactStore().getContact(conversationPartnerAddress);
                 if (contact == null) {
-                    contact = new ContactInfo(isSync ? destination : source);
+                    contact = new ContactInfo(conversationPartnerAddress);
                 }
                 if (contact.messageExpirationTime != message.getExpiresInSeconds()) {
                     contact.messageExpirationTime = message.getExpiresInSeconds();
@@ -1380,6 +1411,7 @@ public class Manager implements Signal {
         }
         for (final File dir : Objects.requireNonNull(cachePath.listFiles())) {
             if (!dir.isDirectory()) {
+                retryFailedReceivedMessage(handler, ignoreAttachments, dir);
                 continue;
             }
 
@@ -1387,38 +1419,42 @@ public class Manager implements Signal {
                 if (!fileEntry.isFile()) {
                     continue;
                 }
-                SignalServiceEnvelope envelope;
-                try {
-                    envelope = Utils.loadEnvelope(fileEntry);
-                    if (envelope == null) {
-                        continue;
-                    }
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    continue;
-                }
-                SignalServiceContent content = null;
-                if (!envelope.isReceipt()) {
-                    try {
-                        content = decryptMessage(envelope);
-                    } catch (Exception e) {
-                        continue;
-                    }
-                    handleMessage(envelope, content, ignoreAttachments);
-                }
-                account.save();
-                handler.handleMessage(envelope, content, null);
-                try {
-                    Files.delete(fileEntry.toPath());
-                } catch (IOException e) {
-                    System.err.println("Failed to delete cached message file “" + fileEntry + "”: " + e.getMessage());
-                }
+                retryFailedReceivedMessage(handler, ignoreAttachments, fileEntry);
             }
             // Try to delete directory if empty
             dir.delete();
         }
     }
 
+    private void retryFailedReceivedMessage(final ReceiveMessageHandler handler, final boolean ignoreAttachments, final File fileEntry) {
+        SignalServiceEnvelope envelope;
+        try {
+            envelope = Utils.loadEnvelope(fileEntry);
+            if (envelope == null) {
+                return;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            return;
+        }
+        SignalServiceContent content = null;
+        if (!envelope.isReceipt()) {
+            try {
+                content = decryptMessage(envelope);
+            } catch (Exception e) {
+                return;
+            }
+            handleMessage(envelope, content, ignoreAttachments);
+        }
+        account.save();
+        handler.handleMessage(envelope, content, null);
+        try {
+            Files.delete(fileEntry.toPath());
+        } catch (IOException e) {
+            System.err.println("Failed to delete cached message file “" + fileEntry + "”: " + e.getMessage());
+        }
+    }
+
     public void receiveMessages(long timeout, TimeUnit unit, boolean returnOnTimeout, boolean ignoreAttachments, ReceiveMessageHandler handler) throws IOException {
         retryFailedReceivedMessages(handler, ignoreAttachments);
         final SignalServiceMessageReceiver messageReceiver = getMessageReceiver();
@@ -1463,7 +1499,7 @@ public class Manager implements Signal {
                 if (!isMessageBlocked(envelope, content)) {
                     handler.handleMessage(envelope, content, exception);
                 }
-                if (!(exception instanceof ProtocolUntrustedIdentityException)) {
+                if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
                     File cacheFile = null;
                     try {
                         cacheFile = getMessageCacheFile(envelope.getSourceE164().get(), now, envelope.getTimestamp());
@@ -1492,15 +1528,15 @@ public class Manager implements Signal {
         } else {
             return false;
         }
-        ContactInfo sourceContact = getContact(source.getNumber().get());
+        ContactInfo sourceContact = account.getContactStore().getContact(source);
         if (sourceContact != null && sourceContact.blocked) {
             return true;
         }
 
         if (content != null && content.getDataMessage().isPresent()) {
             SignalServiceDataMessage message = content.getDataMessage().get();
-            if (message.getGroupInfo().isPresent()) {
-                SignalServiceGroup groupInfo = message.getGroupInfo().get();
+            if (message.getGroupContext().isPresent() && message.getGroupContext().get().getGroupV1().isPresent()) {
+                SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
                 GroupInfo group = getGroup(groupInfo.getGroupId());
                 if (groupInfo.getType() == SignalServiceGroup.Type.DELIVER && group != null && group.blocked) {
                     return true;
@@ -1578,7 +1614,10 @@ public class Manager implements Signal {
                                 if (g.getName().isPresent()) {
                                     syncGroup.name = g.getName().get();
                                 }
-                                syncGroup.addMembers(g.getMembers());
+                                syncGroup.addMembers(g.getMembers()
+                                        .stream()
+                                        .map(this::resolveSignalServiceAddress)
+                                        .collect(Collectors.toSet()));
                                 if (!g.isActive()) {
                                     syncGroup.removeMember(account.getSelfAddress());
                                 } else {
@@ -1613,13 +1652,7 @@ public class Manager implements Signal {
                 if (syncMessage.getBlockedList().isPresent()) {
                     final BlockedListMessage blockedListMessage = syncMessage.getBlockedList().get();
                     for (SignalServiceAddress address : blockedListMessage.getAddresses()) {
-                        if (address.getNumber().isPresent()) {
-                            try {
-                                setContactBlocked(address.getNumber().get(), true);
-                            } catch (InvalidNumberException e) {
-                                e.printStackTrace();
-                            }
-                        }
+                        setContactBlocked(resolveSignalServiceAddress(address), true);
                     }
                     for (byte[] groupId : blockedListMessage.getGroupIds()) {
                         try {
@@ -1644,9 +1677,10 @@ public class Manager implements Signal {
                                 if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) {
                                     account.setProfileKey(c.getProfileKey().get());
                                 }
-                                ContactInfo contact = account.getContactStore().getContact(c.getAddress());
+                                final SignalServiceAddress address = resolveSignalServiceAddress(c.getAddress());
+                                ContactInfo contact = account.getContactStore().getContact(address);
                                 if (contact == null) {
-                                    contact = new ContactInfo(c.getAddress());
+                                    contact = new ContactInfo(address);
                                 }
                                 if (c.getName().isPresent()) {
                                     contact.name = c.getName().get();
@@ -1659,7 +1693,7 @@ public class Manager implements Signal {
                                 }
                                 if (c.getVerified().isPresent()) {
                                     final VerifiedMessage verifiedMessage = c.getVerified().get();
-                                    account.getSignalProtocolStore().saveIdentity(verifiedMessage.getDestination().getNumber().get(), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
+                                    account.getSignalProtocolStore().setIdentityTrustLevel(verifiedMessage.getDestination(), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
                                 }
                                 if (c.getExpirationTimer().isPresent()) {
                                     contact.messageExpirationTime = c.getExpirationTimer().get();
@@ -1688,7 +1722,7 @@ public class Manager implements Signal {
                 }
                 if (syncMessage.getVerified().isPresent()) {
                     final VerifiedMessage verifiedMessage = syncMessage.getVerified().get();
-                    account.getSignalProtocolStore().saveIdentity(verifiedMessage.getDestination().getNumber().get(), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
+                    account.getSignalProtocolStore().setIdentityTrustLevel(resolveSignalServiceAddress(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
                 }
                 if (syncMessage.getConfiguration().isPresent()) {
                     // TODO
@@ -1825,16 +1859,9 @@ public class Manager implements Signal {
                 DeviceContactsOutputStream out = new DeviceContactsOutputStream(fos);
                 for (ContactInfo record : account.getContactStore().getContacts()) {
                     VerifiedMessage verifiedMessage = null;
-                    if (getIdentities().containsKey(record.number)) {
-                        JsonIdentityKeyStore.Identity currentIdentity = null;
-                        for (JsonIdentityKeyStore.Identity id : getIdentities().get(record.number)) {
-                            if (currentIdentity == null || id.getDateAdded().after(currentIdentity.getDateAdded())) {
-                                currentIdentity = id;
-                            }
-                        }
-                        if (currentIdentity != null) {
-                            verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), currentIdentity.getTrustLevel().toVerifiedState(), currentIdentity.getDateAdded().getTime());
-                        }
+                    JsonIdentityKeyStore.Identity currentIdentity = account.getSignalProtocolStore().getIdentity(record.getAddress());
+                    if (currentIdentity != null) {
+                        verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), currentIdentity.getTrustLevel().toVerifiedState(), currentIdentity.getDateAdded().getTime());
                     }
 
                     ProfileKey profileKey = null;
@@ -1905,20 +1932,19 @@ public class Manager implements Signal {
     }
 
     public ContactInfo getContact(String number) {
-        return account.getContactStore().getContact(new SignalServiceAddress(null, number));
+        return account.getContactStore().getContact(Util.getSignalServiceAddressFromIdentifier(number));
     }
 
     public GroupInfo getGroup(byte[] groupId) {
         return account.getGroupStore().getGroup(groupId);
     }
 
-    public Map<String, List<JsonIdentityKeyStore.Identity>> getIdentities() {
+    public List<JsonIdentityKeyStore.Identity> getIdentities() {
         return account.getSignalProtocolStore().getIdentities();
     }
 
-    public Pair<String, List<JsonIdentityKeyStore.Identity>> getIdentities(String number) throws InvalidNumberException {
-        String canonicalizedNumber = Utils.canonicalizeNumber(number, account.getUsername());
-        return new Pair<>(canonicalizedNumber, account.getSignalProtocolStore().getIdentities(canonicalizedNumber));
+    public List<JsonIdentityKeyStore.Identity> getIdentities(String number) throws InvalidNumberException {
+        return account.getSignalProtocolStore().getIdentities(canonicalizeAndResolveSignalServiceAddress(number));
     }
 
     /**
@@ -1927,8 +1953,9 @@ public class Manager implements Signal {
      * @param name        username of the identity
      * @param fingerprint Fingerprint
      */
-    public boolean trustIdentityVerified(String name, byte[] fingerprint) {
-        List<JsonIdentityKeyStore.Identity> ids = account.getSignalProtocolStore().getIdentities(name);
+    public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException {
+        SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name);
+        List<JsonIdentityKeyStore.Identity> ids = account.getSignalProtocolStore().getIdentities(address);
         if (ids == null) {
             return false;
         }
@@ -1937,9 +1964,9 @@ public class Manager implements Signal {
                 continue;
             }
 
-            account.getSignalProtocolStore().saveIdentity(name, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
+            account.getSignalProtocolStore().setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
             try {
-                sendVerifiedMessage(new SignalServiceAddress(null, name), id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
+                sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
             } catch (IOException | UntrustedIdentityException e) {
                 e.printStackTrace();
             }
@@ -1955,19 +1982,20 @@ public class Manager implements Signal {
      * @param name         username of the identity
      * @param safetyNumber Safety number
      */
-    public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) {
-        List<JsonIdentityKeyStore.Identity> ids = account.getSignalProtocolStore().getIdentities(name);
+    public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException {
+        SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name);
+        List<JsonIdentityKeyStore.Identity> ids = account.getSignalProtocolStore().getIdentities(address);
         if (ids == null) {
             return false;
         }
         for (JsonIdentityKeyStore.Identity id : ids) {
-            if (!safetyNumber.equals(computeSafetyNumber(name, id.getIdentityKey()))) {
+            if (!safetyNumber.equals(computeSafetyNumber(address, id.getIdentityKey()))) {
                 continue;
             }
 
-            account.getSignalProtocolStore().saveIdentity(name, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
+            account.getSignalProtocolStore().setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
             try {
-                sendVerifiedMessage(new SignalServiceAddress(null, name), id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
+                sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED);
             } catch (IOException | UntrustedIdentityException e) {
                 e.printStackTrace();
             }
@@ -1983,15 +2011,16 @@ public class Manager implements Signal {
      * @param name username of the identity
      */
     public boolean trustIdentityAllKeys(String name) {
-        List<JsonIdentityKeyStore.Identity> ids = account.getSignalProtocolStore().getIdentities(name);
+        SignalServiceAddress address = resolveSignalServiceAddress(name);
+        List<JsonIdentityKeyStore.Identity> ids = account.getSignalProtocolStore().getIdentities(address);
         if (ids == null) {
             return false;
         }
         for (JsonIdentityKeyStore.Identity id : ids) {
             if (id.getTrustLevel() == TrustLevel.UNTRUSTED) {
-                account.getSignalProtocolStore().saveIdentity(name, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED);
+                account.getSignalProtocolStore().setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED);
                 try {
-                    sendVerifiedMessage(new SignalServiceAddress(null, name), id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED);
+                    sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED);
                 } catch (IOException | UntrustedIdentityException e) {
                     e.printStackTrace();
                 }
@@ -2001,8 +2030,27 @@ public class Manager implements Signal {
         return true;
     }
 
-    public String computeSafetyNumber(String theirUsername, IdentityKey theirIdentityKey) {
-        return Utils.computeSafetyNumber(account.getUsername(), getIdentity(), theirUsername, theirIdentityKey);
+    public String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) {
+        return Utils.computeSafetyNumber(account.getSelfAddress(), getIdentity(), theirAddress, theirIdentityKey);
+    }
+
+    public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException {
+        String canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier : Util.canonicalizeNumber(identifier, account.getUsername());
+        return resolveSignalServiceAddress(canonicalizedNumber);
+    }
+
+    public SignalServiceAddress resolveSignalServiceAddress(String identifier) {
+        SignalServiceAddress address = Util.getSignalServiceAddressFromIdentifier(identifier);
+
+        return resolveSignalServiceAddress(address);
+    }
+
+    public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) {
+        if (address.matches(account.getSelfAddress())) {
+            return account.getSelfAddress();
+        }
+
+        return account.getRecipientStore().resolveServiceAddress(address);
     }
 
     public interface ReceiveMessageHandler {