From: AsamK Date: Fri, 3 Apr 2020 09:56:26 +0000 (+0200) Subject: Add RecipientStore to resolve all identifiers to SignalServiceAddress X-Git-Tag: v0.6.7~4 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/320e126eebc4e3ebac5144197e640508704eeb0c Add RecipientStore to resolve all identifiers to SignalServiceAddress Should fix #290 --- diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index ccb2fbce..f1316d64 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -139,6 +139,7 @@ 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; @@ -1287,7 +1288,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); @@ -1326,8 +1330,9 @@ public class Manager implements Signal { break; } } + final SignalServiceAddress conversationPartnerAddress = isSync ? destination : source; if (message.isEndSession()) { - handleEndSession(isSync ? destination : source); + handleEndSession(conversationPartnerAddress); } if (message.isExpirationUpdate() || message.getBody().isPresent()) { if (message.getGroupContext().isPresent() && message.getGroupContext().get().getGroupV1().isPresent()) { @@ -1341,9 +1346,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(); @@ -1607,7 +1612,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 { @@ -1642,7 +1650,7 @@ public class Manager implements Signal { if (syncMessage.getBlockedList().isPresent()) { final BlockedListMessage blockedListMessage = syncMessage.getBlockedList().get(); for (SignalServiceAddress address : blockedListMessage.getAddresses()) { - setContactBlocked(address, true); + setContactBlocked(resolveSignalServiceAddress(address), true); } for (byte[] groupId : blockedListMessage.getGroupIds()) { try { @@ -1667,9 +1675,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(); @@ -1711,7 +1720,7 @@ public class Manager implements Signal { } if (syncMessage.getVerified().isPresent()) { final VerifiedMessage verifiedMessage = syncMessage.getVerified().get(); - account.getSignalProtocolStore().setIdentityTrustLevel(verifiedMessage.getDestination(), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); + account.getSignalProtocolStore().setIdentityTrustLevel(resolveSignalServiceAddress(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } if (syncMessage.getConfiguration().isPresent()) { // TODO @@ -2030,15 +2039,16 @@ public class Manager implements Signal { 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(); } - ContactInfo contactInfo = account.getContactStore().getContact(address); - if (contactInfo == null) { - return address; - } - return contactInfo.getAddress(); + return account.getRecipientStore().resolveServiceAddress(address); } public interface ReceiveMessageHandler { diff --git a/src/main/java/org/asamk/signal/manager/Utils.java b/src/main/java/org/asamk/signal/manager/Utils.java index 0b74f01d..449681d2 100644 --- a/src/main/java/org/asamk/signal/manager/Utils.java +++ b/src/main/java/org/asamk/signal/manager/Utils.java @@ -252,6 +252,9 @@ class Utils { } else { // Version 1: E164 user version = 1; + if (!ownAddress.getNumber().isPresent() || !theirAddress.getNumber().isPresent()) { + return "INVALID ID"; + } ownId = ownAddress.getNumber().get().getBytes(); theirId = theirAddress.getNumber().get().getBytes(); } diff --git a/src/main/java/org/asamk/signal/storage/SignalAccount.java b/src/main/java/org/asamk/signal/storage/SignalAccount.java index 73f79a48..f03bac3a 100644 --- a/src/main/java/org/asamk/signal/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/storage/SignalAccount.java @@ -14,7 +14,10 @@ import org.asamk.signal.storage.contacts.ContactInfo; import org.asamk.signal.storage.contacts.JsonContactsStore; import org.asamk.signal.storage.groups.GroupInfo; import org.asamk.signal.storage.groups.JsonGroupStore; +import org.asamk.signal.storage.protocol.JsonIdentityKeyStore; import org.asamk.signal.storage.protocol.JsonSignalProtocolStore; +import org.asamk.signal.storage.protocol.RecipientStore; +import org.asamk.signal.storage.protocol.SessionInfo; import org.asamk.signal.storage.protocol.SignalServiceAddressResolver; import org.asamk.signal.storage.threads.LegacyJsonThreadStore; import org.asamk.signal.storage.threads.ThreadInfo; @@ -37,6 +40,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.Collection; import java.util.UUID; +import java.util.stream.Collectors; public class SignalAccount { @@ -59,6 +63,7 @@ public class SignalAccount { private JsonSignalProtocolStore signalProtocolStore; private JsonGroupStore groupStore; private JsonContactsStore contactStore; + private RecipientStore recipientStore; private SignalAccount() { jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); // disable autodetect @@ -88,6 +93,7 @@ public class SignalAccount { account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId); account.groupStore = new JsonGroupStore(); account.contactStore = new JsonContactsStore(); + account.recipientStore = new RecipientStore(); account.registered = false; return account; @@ -108,6 +114,7 @@ public class SignalAccount { account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId); account.groupStore = new JsonGroupStore(); account.contactStore = new JsonContactsStore(); + account.recipientStore = new RecipientStore(); account.registered = true; account.isMultiDevice = true; @@ -199,6 +206,35 @@ public class SignalAccount { if (contactStore == null) { contactStore = new JsonContactsStore(); } + + JsonNode recipientStoreNode = rootNode.get("recipientStore"); + if (recipientStoreNode != null) { + recipientStore = jsonProcessor.convertValue(recipientStoreNode, RecipientStore.class); + } + if (recipientStore == null) { + recipientStore = new RecipientStore(); + + recipientStore.resolveServiceAddress(getSelfAddress()); + + for (ContactInfo contact : contactStore.getContacts()) { + recipientStore.resolveServiceAddress(contact.getAddress()); + } + + for (GroupInfo group : groupStore.getGroups()) { + group.members = group.members.stream() + .map(m -> recipientStore.resolveServiceAddress(m)) + .collect(Collectors.toSet()); + } + + for (SessionInfo session : signalProtocolStore.getSessions()) { + session.address = recipientStore.resolveServiceAddress(session.address); + } + + for (JsonIdentityKeyStore.Identity identity : signalProtocolStore.getIdentities()) { + identity.setAddress(recipientStore.resolveServiceAddress(identity.getAddress())); + } + } + JsonNode threadStoreNode = rootNode.get("threadStore"); if (threadStoreNode != null) { LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class); @@ -244,6 +280,7 @@ public class SignalAccount { .putPOJO("axolotlStore", signalProtocolStore) .putPOJO("groupStore", groupStore) .putPOJO("contactStore", contactStore) + .putPOJO("recipientStore", recipientStore) ; try { synchronized (fileChannel) { @@ -302,6 +339,10 @@ public class SignalAccount { return contactStore; } + public RecipientStore getRecipientStore() { + return recipientStore; + } + public String getUsername() { return username; } diff --git a/src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java b/src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java index 86514bc1..bb81b0c9 100644 --- a/src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java +++ b/src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java @@ -27,6 +27,12 @@ public class JsonContactsStore { public ContactInfo getContact(SignalServiceAddress address) { for (ContactInfo contact : contacts) { if (contact.getAddress().matches(address)) { + if (contact.uuid == null) { + contact.uuid = address.getUuid().orNull(); + } else if (contact.number == null) { + contact.number = address.getNumber().orNull(); + } + return contact; } } diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java b/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java index e3a45420..4b0adcd0 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java +++ b/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java @@ -103,6 +103,9 @@ public class GroupInfo { public void addMembers(Collection addresses) { for (SignalServiceAddress address : addresses) { + if (this.members.contains(address)) { + continue; + } removeMember(address); this.members.add(address); } diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java b/src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java index 90229575..5ce99742 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java +++ b/src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java @@ -62,6 +62,10 @@ class JsonSessionStore implements SessionStore { return new SessionRecord(); } + public synchronized List getSessions() { + return sessions; + } + @Override public synchronized List getSubDeviceSessions(String name) { SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name); @@ -158,7 +162,7 @@ class JsonSessionStore implements SessionStore { } } - public static class JsonPreKeyStoreSerializer extends JsonSerializer { + public static class JsonSessionStoreSerializer extends JsonSerializer { @Override public void serialize(JsonSessionStore jsonSessionStore, JsonGenerator json, SerializerProvider serializerProvider) throws IOException { diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java b/src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java index c7079078..3dc15cca 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java +++ b/src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java @@ -26,7 +26,7 @@ public class JsonSignalProtocolStore implements SignalProtocolStore { @JsonProperty("sessionStore") @JsonDeserialize(using = JsonSessionStore.JsonSessionStoreDeserializer.class) - @JsonSerialize(using = JsonSessionStore.JsonPreKeyStoreSerializer.class) + @JsonSerialize(using = JsonSessionStore.JsonSessionStoreSerializer.class) private JsonSessionStore sessionStore; @JsonProperty("signedPreKeyStore") @@ -131,6 +131,10 @@ public class JsonSignalProtocolStore implements SignalProtocolStore { return sessionStore.loadSession(address); } + public List getSessions() { + return sessionStore.getSessions(); + } + @Override public List getSubDeviceSessions(String name) { return sessionStore.getSubDeviceSessions(name); diff --git a/src/main/java/org/asamk/signal/storage/protocol/RecipientStore.java b/src/main/java/org/asamk/signal/storage/protocol/RecipientStore.java new file mode 100644 index 00000000..4943bd36 --- /dev/null +++ b/src/main/java/org/asamk/signal/storage/protocol/RecipientStore.java @@ -0,0 +1,87 @@ +package org.asamk.signal.storage.protocol; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.UuidUtil; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class RecipientStore { + + @JsonProperty("recipientStore") + @JsonDeserialize(using = RecipientStoreDeserializer.class) + @JsonSerialize(using = RecipientStoreSerializer.class) + private final Set addresses = new HashSet<>(); + + public RecipientStore() { + } + + public SignalServiceAddress resolveServiceAddress(SignalServiceAddress serviceAddress) { + if (addresses.contains(serviceAddress)) { + // If the Set already contains the exact address with UUID and Number, + // we can just return it here. + return serviceAddress; + } + + for (SignalServiceAddress address : addresses) { + if (address.matches(serviceAddress)) { + return address; + } + } + + if (serviceAddress.getNumber().isPresent() && serviceAddress.getUuid().isPresent()) { + addresses.add(serviceAddress); + } + + return serviceAddress; + } + + public static class RecipientStoreDeserializer extends JsonDeserializer> { + + @Override + public Set deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + Set addresses = new HashSet<>(); + + if (node.isArray()) { + for (JsonNode recipient : node) { + String recipientName = recipient.get("name").asText(); + UUID uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText()); + final SignalServiceAddress serviceAddress = new SignalServiceAddress(uuid, recipientName); + addresses.add(serviceAddress); + } + } + + return addresses; + } + } + + public static class RecipientStoreSerializer extends JsonSerializer> { + + @Override + public void serialize(Set addresses, JsonGenerator json, SerializerProvider serializerProvider) throws IOException { + json.writeStartArray(); + for (SignalServiceAddress address : addresses) { + json.writeStartObject(); + json.writeStringField("name", address.getNumber().get()); + json.writeStringField("uuid", address.getUuid().get().toString()); + json.writeEndObject(); + } + json.writeEndArray(); + } + } +}