From b62694dbc75c2b52ce5fc946805f61f244f43dc0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 Mar 2020 20:28:15 +0100 Subject: [PATCH] Remove ThreadStore and store message expiration time in group/contact store To match the implemenation of Signal-Android --- .../org/asamk/signal/manager/Manager.java | 98 +++++++++---------- .../asamk/signal/storage/SignalAccount.java | 35 ++++--- .../signal/storage/contacts/ContactInfo.java | 3 + .../signal/storage/groups/GroupInfo.java | 5 +- ...dStore.java => LegacyJsonThreadStore.java} | 4 +- 5 files changed, 75 insertions(+), 70 deletions(-) rename src/main/java/org/asamk/signal/storage/threads/{JsonThreadStore.java => LegacyJsonThreadStore.java} (95%) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 7a28f7bc..976d62a9 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -31,7 +31,6 @@ import org.asamk.signal.storage.contacts.ContactInfo; 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.threads.ThreadInfo; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.signal.libsignal.metadata.InvalidMetadataMessageException; @@ -527,13 +526,11 @@ public class Manager implements Signal { .build(); messageBuilder.asGroupMessage(group); } - ThreadInfo thread = account.getThreadStore().getThread(Base64.encodeBytes(groupId)); - if (thread != null) { - messageBuilder.withExpiration(thread.messageExpirationTime); - } final GroupInfo g = getGroupForSending(groupId); + messageBuilder.withExpiration(g.messageExpirationTime); + sendMessageLegacy(messageBuilder, g.getMembersWithout(account.getSelfAddress())); } @@ -651,15 +648,9 @@ public class Manager implements Signal { } } - SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .asGroupMessage(group.build()); - - ThreadInfo thread = account.getThreadStore().getThread(Base64.encodeBytes(g.groupId)); - if (thread != null) { - messageBuilder.withExpiration(thread.messageExpirationTime); - } - - return messageBuilder; + return SignalServiceDataMessage.newBuilder() + .asGroupMessage(group.build()) + .withExpiration(g.messageExpirationTime); } private void sendGroupInfoRequest(byte[] groupId, SignalServiceAddress recipient) throws IOException, EncapsulatedExceptions { @@ -673,11 +664,6 @@ public class Manager implements Signal { SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() .asGroupMessage(group.build()); - ThreadInfo thread = account.getThreadStore().getThread(Base64.encodeBytes(groupId)); - if (thread != null) { - messageBuilder.withExpiration(thread.messageExpirationTime); - } - // Send group info request message to the recipient who sent us a message with this groupId sendMessageLegacy(messageBuilder, Collections.singleton(recipient)); } @@ -837,12 +823,21 @@ public class Manager implements Signal { } /** - * Change the expiration timer for a thread (number of groupId) + * Change the expiration timer for a contact */ - public void setExpirationTimer(String numberOrGroupId, int messageExpirationTimer) { - ThreadInfo thread = account.getThreadStore().getThread(numberOrGroupId); - thread.messageExpirationTime = messageExpirationTimer; - account.getThreadStore().updateThread(thread); + public void setExpirationTimer(SignalServiceAddress address, int messageExpirationTimer) { + ContactInfo c = account.getContactStore().getContact(address); + c.messageExpirationTime = messageExpirationTimer; + account.getContactStore().updateContact(c); + } + + /** + * Change the expiration timer for a group + */ + public void setExpirationTimer(byte[] groupId, int messageExpirationTimer) { + GroupInfo g = account.getGroupStore().getGroup(groupId); + g.messageExpirationTime = messageExpirationTimer; + account.getGroupStore().updateGroup(g); } /** @@ -1186,9 +1181,9 @@ public class Manager implements Signal { // Send to all individually, so sync messages are sent correctly List results = new ArrayList<>(recipients.size()); for (SignalServiceAddress address : recipients) { - ThreadInfo thread = account.getThreadStore().getThread(address.getNumber().get()); - if (thread != null) { - messageBuilder.withExpiration(thread.messageExpirationTime); + ContactInfo contact = account.getContactStore().getContact(address); + if (contact != null) { + messageBuilder.withExpiration(contact.messageExpirationTime); } else { messageBuilder.withExpiration(0); } @@ -1229,10 +1224,8 @@ public class Manager implements Signal { } private void handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, SignalServiceAddress source, SignalServiceAddress destination, boolean ignoreAttachments) { - String threadId; if (message.getGroupInfo().isPresent()) { SignalServiceGroup groupInfo = message.getGroupInfo().get(); - threadId = Base64.encodeBytes(groupInfo.getGroupId()); GroupInfo group = account.getGroupStore().getGroup(groupInfo.getGroupId()); switch (groupInfo.getType()) { case UPDATE: @@ -1294,25 +1287,30 @@ public class Manager implements Signal { } break; } - } else { - if (isSync) { - threadId = destination.getNumber().get(); - } else { - threadId = source.getNumber().get(); - } } if (message.isEndSession()) { handleEndSession(isSync ? destination.getNumber().get() : source.getNumber().get()); } if (message.isExpirationUpdate() || message.getBody().isPresent()) { - ThreadInfo thread = account.getThreadStore().getThread(threadId); - if (thread == null) { - thread = new ThreadInfo(); - thread.id = threadId; - } - if (thread.messageExpirationTime != message.getExpiresInSeconds()) { - thread.messageExpirationTime = message.getExpiresInSeconds(); - account.getThreadStore().updateThread(thread); + if (message.getGroupInfo().isPresent()) { + SignalServiceGroup groupInfo = message.getGroupInfo().get(); + GroupInfo group = account.getGroupStore().getGroup(groupInfo.getGroupId()); + if (group == null) { + group = new GroupInfo(groupInfo.getGroupId()); + } + if (group.messageExpirationTime != message.getExpiresInSeconds()) { + group.messageExpirationTime = message.getExpiresInSeconds(); + account.getGroupStore().updateGroup(group); + } + } else { + ContactInfo contact = account.getContactStore().getContact(isSync ? destination : source); + if (contact == null) { + contact = new ContactInfo(isSync ? destination : source); + } + if (contact.messageExpirationTime != message.getExpiresInSeconds()) { + contact.messageExpirationTime = message.getExpiresInSeconds(); + account.getContactStore().updateContact(contact); + } } } if (message.getAttachments().isPresent() && !ignoreAttachments) { @@ -1635,13 +1633,7 @@ public class Manager implements Signal { account.getSignalProtocolStore().saveIdentity(verifiedMessage.getDestination().getNumber().get(), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } if (c.getExpirationTimer().isPresent()) { - ThreadInfo thread = account.getThreadStore().getThread(c.getAddress().getNumber().get()); - if (thread == null) { - thread = new ThreadInfo(); - thread.id = c.getAddress().getNumber().get(); - } - thread.messageExpirationTime = c.getExpirationTimer().get(); - account.getThreadStore().updateThread(thread); + contact.messageExpirationTime = c.getExpirationTimer().get(); } contact.blocked = c.isBlocked(); contact.inboxPosition = c.getInboxPosition().orNull(); @@ -1769,10 +1761,9 @@ public class Manager implements Signal { try (OutputStream fos = new FileOutputStream(groupsFile)) { DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(fos); for (GroupInfo record : account.getGroupStore().getGroups()) { - ThreadInfo info = account.getThreadStore().getThread(Base64.encodeBytes(record.groupId)); out.write(new DeviceGroup(record.groupId, Optional.fromNullable(record.name), new ArrayList<>(record.getMembers()), createGroupAvatarAttachment(record.groupId), - record.isMember(account.getSelfAddress()), Optional.fromNullable(info != null ? info.messageExpirationTime : null), + record.isMember(account.getSelfAddress()), Optional.of(record.messageExpirationTime), Optional.fromNullable(record.color), record.blocked, Optional.fromNullable(record.inboxPosition), record.archived)); } } @@ -1805,7 +1796,6 @@ public class Manager implements Signal { DeviceContactsOutputStream out = new DeviceContactsOutputStream(fos); for (ContactInfo record : account.getContactStore().getContacts()) { VerifiedMessage verifiedMessage = null; - ThreadInfo info = account.getThreadStore().getThread(record.number); if (getIdentities().containsKey(record.number)) { JsonIdentityKeyStore.Identity currentIdentity = null; for (JsonIdentityKeyStore.Identity id : getIdentities().get(record.number)) { @@ -1826,7 +1816,7 @@ public class Manager implements Signal { out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name), createContactAvatarAttachment(record.number), Optional.fromNullable(record.color), Optional.fromNullable(verifiedMessage), Optional.fromNullable(profileKey), record.blocked, - Optional.fromNullable(info != null ? info.messageExpirationTime : null), + Optional.of(record.messageExpirationTime), Optional.fromNullable(record.inboxPosition), record.archived)); } diff --git a/src/main/java/org/asamk/signal/storage/SignalAccount.java b/src/main/java/org/asamk/signal/storage/SignalAccount.java index 08ed5139..6d83f78f 100644 --- a/src/main/java/org/asamk/signal/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/storage/SignalAccount.java @@ -10,10 +10,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; +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.JsonSignalProtocolStore; -import org.asamk.signal.storage.threads.JsonThreadStore; +import org.asamk.signal.storage.threads.LegacyJsonThreadStore; +import org.asamk.signal.storage.threads.ThreadInfo; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.signal.zkgroup.InvalidInputException; @@ -55,7 +58,6 @@ public class SignalAccount { private JsonSignalProtocolStore signalProtocolStore; private JsonGroupStore groupStore; private JsonContactsStore contactStore; - private JsonThreadStore threadStore; private SignalAccount() { jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); // disable autodetect @@ -84,7 +86,6 @@ public class SignalAccount { account.profileKey = profileKey; account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId); account.groupStore = new JsonGroupStore(); - account.threadStore = new JsonThreadStore(); account.contactStore = new JsonContactsStore(); account.registered = false; @@ -104,7 +105,6 @@ public class SignalAccount { account.signalingKey = signalingKey; account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId); account.groupStore = new JsonGroupStore(); - account.threadStore = new JsonThreadStore(); account.contactStore = new JsonContactsStore(); account.registered = true; account.isMultiDevice = true; @@ -191,10 +191,24 @@ public class SignalAccount { } JsonNode threadStoreNode = rootNode.get("threadStore"); if (threadStoreNode != null) { - threadStore = jsonProcessor.convertValue(threadStoreNode, JsonThreadStore.class); - } - if (threadStore == null) { - threadStore = new JsonThreadStore(); + LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class); + // Migrate thread info to group and contact store + for (ThreadInfo thread : threadStore.getThreads()) { + try { + ContactInfo contactInfo = contactStore.getContact(new SignalServiceAddress(null, thread.id)); + if (contactInfo != null) { + contactInfo.messageExpirationTime = thread.messageExpirationTime; + contactStore.updateContact(contactInfo); + } else { + GroupInfo groupInfo = groupStore.getGroup(Base64.decode(thread.id)); + if (groupInfo != null) { + groupInfo.messageExpirationTime = thread.messageExpirationTime; + groupStore.updateGroup(groupInfo); + } + } + } catch (Exception ignored) { + } + } } } @@ -216,7 +230,6 @@ public class SignalAccount { .putPOJO("axolotlStore", signalProtocolStore) .putPOJO("groupStore", groupStore) .putPOJO("contactStore", contactStore) - .putPOJO("threadStore", threadStore) ; try { synchronized (fileChannel) { @@ -271,10 +284,6 @@ public class SignalAccount { return contactStore; } - public JsonThreadStore getThreadStore() { - return threadStore; - } - public String getUsername() { return username; } diff --git a/src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java b/src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java index b5dadd1a..4d3a5e95 100644 --- a/src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java +++ b/src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java @@ -21,6 +21,9 @@ public class ContactInfo { @JsonProperty public String color; + @JsonProperty(defaultValue = "0") + public int messageExpirationTime; + @JsonProperty public String profileKey; 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 21ba910f..e3a45420 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java +++ b/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java @@ -37,6 +37,8 @@ public class GroupInfo { public Set members = new HashSet<>(); @JsonProperty public String color; + @JsonProperty(defaultValue = "0") + public int messageExpirationTime; @JsonProperty(defaultValue = "false") public boolean blocked; @JsonProperty @@ -54,7 +56,7 @@ public class GroupInfo { this.groupId = groupId; } - public GroupInfo(@JsonProperty("groupId") byte[] groupId, @JsonProperty("name") String name, @JsonProperty("members") Collection members, @JsonProperty("avatarId") long avatarId, @JsonProperty("color") String color, @JsonProperty("blocked") boolean blocked, @JsonProperty("inboxPosition") Integer inboxPosition, @JsonProperty("archived") boolean archived) { + public GroupInfo(@JsonProperty("groupId") byte[] groupId, @JsonProperty("name") String name, @JsonProperty("members") Collection members, @JsonProperty("avatarId") long avatarId, @JsonProperty("color") String color, @JsonProperty("blocked") boolean blocked, @JsonProperty("inboxPosition") Integer inboxPosition, @JsonProperty("archived") boolean archived, @JsonProperty("messageExpirationTime") int messageExpirationTime) { this.groupId = groupId; this.name = name; this.members.addAll(members); @@ -63,6 +65,7 @@ public class GroupInfo { this.blocked = blocked; this.inboxPosition = inboxPosition; this.archived = archived; + this.messageExpirationTime = messageExpirationTime; } @JsonIgnore diff --git a/src/main/java/org/asamk/signal/storage/threads/JsonThreadStore.java b/src/main/java/org/asamk/signal/storage/threads/LegacyJsonThreadStore.java similarity index 95% rename from src/main/java/org/asamk/signal/storage/threads/JsonThreadStore.java rename to src/main/java/org/asamk/signal/storage/threads/LegacyJsonThreadStore.java index a4a89ccd..267d712c 100644 --- a/src/main/java/org/asamk/signal/storage/threads/JsonThreadStore.java +++ b/src/main/java/org/asamk/signal/storage/threads/LegacyJsonThreadStore.java @@ -18,12 +18,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class JsonThreadStore { +public class LegacyJsonThreadStore { private static final ObjectMapper jsonProcessor = new ObjectMapper(); @JsonProperty("threads") - @JsonSerialize(using = JsonThreadStore.MapToListSerializer.class) + @JsonSerialize(using = MapToListSerializer.class) @JsonDeserialize(using = ThreadsDeserializer.class) private Map threads = new HashMap<>(); -- 2.50.1