From: AsamK Date: Mon, 6 Dec 2021 23:02:24 +0000 (+0100) Subject: Improve behavior of changed recipient id X-Git-Tag: v0.10.0~15 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/10df4338b1a810797c17fffb8d859c4654adbb93 Improve behavior of changed recipient id --- diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java index cfa39bc9..5d8bf2c1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java @@ -36,6 +36,7 @@ import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; public class GroupStore { @@ -82,8 +83,8 @@ public class GroupStore { m.number)); } - return RecipientId.of(m.recipientId); - }).collect(Collectors.toSet()); + return recipientResolver.resolveRecipient(m.recipientId); + }).filter(Objects::nonNull).collect(Collectors.toSet()); return new GroupInfoV1(GroupIdV1.fromBase64(g1.groupId), g1.expectedV2Id == null ? null : GroupIdV2.fromBase64(g1.expectedV2Id), diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java index 9e89861a..fef06c5b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java @@ -26,6 +26,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -161,7 +162,8 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden } return Arrays.stream(files) .filter(f -> identityFileNamePattern.matcher(f.getName()).matches()) - .map(f -> RecipientId.of(Integer.parseInt(f.getName()))) + .map(f -> resolver.resolveRecipient(Long.parseLong(f.getName()))) + .filter(Objects::nonNull) .map(this::loadIdentityLocked) .collect(Collectors.toList()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientId.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientId.java index d7aa373d..cea637ec 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientId.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientId.java @@ -1,8 +1,42 @@ package org.asamk.signal.manager.storage.recipients; -public record RecipientId(long id) { +import java.util.Objects; - public static RecipientId of(long id) { - return new RecipientId(id); +public final class RecipientId { + + private long id; + private final RecipientStore recipientStore; + + RecipientId(long id, final RecipientStore recipientStore) { + this.id = id; + this.recipientStore = recipientStore; + } + + public long id() { + if (recipientStore != null) { + final var actualRecipientId = recipientStore.getActualRecipientId(this.id); + if (actualRecipientId != this.id) { + this.id = actualRecipientId; + } + } + return this.id; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (RecipientId) obj; + return this.id() == that.id(); + } + + @Override + public int hashCode() { + return Objects.hash(id()); + } + + @Override + public String toString() { + return "RecipientId[" + "id=" + id() + ']'; } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java index 06747684..487001a0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java @@ -12,4 +12,6 @@ public interface RecipientResolver { RecipientId resolveRecipient(SignalServiceAddress address); RecipientId resolveRecipient(ACI aci); + + RecipientId resolveRecipient(long recipientId); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java index 24f36b25..706110db 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java @@ -44,7 +44,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile private final RecipientMergeHandler recipientMergeHandler; private final Map recipients; - private final Map recipientsMerged = new HashMap<>(); + private final Map recipientsMerged = new HashMap<>(); private long lastId; @@ -52,8 +52,14 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile final var objectMapper = Utils.createStorageObjectMapper(); try (var inputStream = new FileInputStream(file)) { final var storage = objectMapper.readValue(inputStream, Storage.class); + + final var recipientStore = new RecipientStore(objectMapper, + file, + recipientMergeHandler, + new HashMap<>(), + storage.lastId); final var recipients = storage.recipients.stream().map(r -> { - final var recipientId = new RecipientId(r.id); + final var recipientId = new RecipientId(r.id, recipientStore); final var address = new RecipientAddress(Optional.ofNullable(r.uuid).map(UuidUtil::parseOrThrow), Optional.ofNullable(r.number)); @@ -101,7 +107,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return new Recipient(recipientId, address, contact, profileKey, profileKeyCredential, profile); }).collect(Collectors.toMap(Recipient::getRecipientId, r -> r)); - return new RecipientStore(objectMapper, file, recipientMergeHandler, recipients, storage.lastId); + recipientStore.addRecipients(recipients); + + return recipientStore; } catch (FileNotFoundException e) { logger.debug("Creating new recipient store."); return new RecipientStore(objectMapper, file, recipientMergeHandler, new HashMap<>(), 0); @@ -130,7 +138,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile public Recipient getRecipient(RecipientId recipientId) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); return recipients.get(recipientId); } } @@ -140,6 +147,12 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return resolveRecipient(new RecipientAddress(aci == null ? null : aci.uuid()), false); } + @Override + public RecipientId resolveRecipient(final long recipientId) { + final var recipient = getRecipient(new RecipientId(recipientId, this)); + return recipient == null ? null : recipient.getRecipientId(); + } + @Override public RecipientId resolveRecipient(final String identifier) { return resolveRecipient(Utils.getRecipientAddressFromIdentifier(identifier), false); @@ -201,7 +214,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public void storeContact(RecipientId recipientId, final Contact contact) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withContact(contact).build()); } @@ -225,7 +237,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public void deleteContact(RecipientId recipientId) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withContact(null).build()); } @@ -233,7 +244,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile public void deleteRecipientData(RecipientId recipientId) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); logger.debug("Deleting recipient data for {}", recipientId); final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, @@ -265,7 +275,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public void storeProfile(RecipientId recipientId, final Profile profile) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withProfile(profile).build()); } @@ -274,7 +283,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public void storeProfileKey(RecipientId recipientId, final ProfileKey profileKey) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); final var recipient = recipients.get(recipientId); if (profileKey != null && profileKey.equals(recipient.getProfileKey())) { return; @@ -294,7 +302,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public void storeProfileKeyCredential(RecipientId recipientId, final ProfileKeyCredential profileKeyCredential) { synchronized (recipients) { - recipientId = getActualRecipientId(recipientId); final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withProfileKeyCredential(profileKeyCredential).build()); @@ -307,6 +314,10 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } } + private void addRecipients(final Map recipients) { + this.recipients.putAll(recipients); + } + /** * @param isHighTrust true, if the number/uuid connection was obtained from a trusted source. * Has no effect, if the address contains only a number or a uuid. @@ -391,8 +402,10 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile byNumberRecipient.getRecipientId(), byUuidRecipient.getRecipientId()); updateRecipientAddressLocked(byUuidRecipient.getRecipientId(), address); - mergeRecipientsLocked(byUuidRecipient.getRecipientId(), byNumberRecipient.getRecipientId()); - return new Pair<>(byUuidRecipient.getRecipientId(), byNumber.map(Recipient::getRecipientId)); + // Create a fixed RecipientId that won't update its id after merge + final var toBeMergedRecipientId = new RecipientId(byNumberRecipient.getRecipientId().id(), null); + mergeRecipientsLocked(byUuidRecipient.getRecipientId(), toBeMergedRecipientId); + return new Pair<>(byUuidRecipient.getRecipientId(), Optional.of(toBeMergedRecipientId)); } private RecipientId addNewRecipientLocked(final RecipientAddress address) { @@ -403,12 +416,11 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } private void updateRecipientAddressLocked(RecipientId recipientId, final RecipientAddress address) { - recipientId = getActualRecipientId(recipientId); final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withAddress(address).build()); } - private RecipientId getActualRecipientId(RecipientId recipientId) { + long getActualRecipientId(long recipientId) { while (recipientsMerged.containsKey(recipientId)) { final var newRecipientId = recipientsMerged.get(recipientId); logger.debug("Using {} instead of {}, because recipients have been merged", newRecipientId, recipientId); @@ -440,7 +452,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile : toBeMergedRecipient.getProfileKeyCredential(), recipient.getProfile() != null ? recipient.getProfile() : toBeMergedRecipient.getProfile())); recipients.remove(toBeMergedRecipientId); - recipientsMerged.put(toBeMergedRecipientId, recipientId); + recipientsMerged.put(toBeMergedRecipientId.id(), recipientId.id()); saveLocked(); } @@ -467,7 +479,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } private RecipientId nextIdLocked() { - return new RecipientId(++this.lastId); + return new RecipientId(++this.lastId, this); } private void saveLocked() { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java index fb26fa00..caddebcc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -139,9 +140,14 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups return Arrays.stream(files) .map(f -> senderKeyFileNamePattern.matcher(f.getName())) .filter(Matcher::matches) - .map(matcher -> new Key(RecipientId.of(Long.parseLong(matcher.group(1))), - Integer.parseInt(matcher.group(2)), - UUID.fromString(matcher.group(3)))) + .map(matcher -> { + final var recipientId = resolver.resolveRecipient(Long.parseLong(matcher.group(1))); + if (recipientId == null) { + return null; + } + return new Key(recipientId, Integer.parseInt(matcher.group(2)), UUID.fromString(matcher.group(3))); + }) + .filter(Objects::nonNull) .collect(Collectors.toList()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java index 08197aec..ba852338 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java @@ -47,7 +47,11 @@ public class SenderKeySharedStore { final var storage = objectMapper.readValue(inputStream, Storage.class); final var sharedSenderKeys = new HashMap>(); for (final var senderKey : storage.sharedSenderKeys) { - final var entry = new SenderKeySharedEntry(RecipientId.of(senderKey.recipientId), senderKey.deviceId); + final var recipientId = resolver.resolveRecipient(senderKey.recipientId); + if (recipientId == null) { + continue; + } + final var entry = new SenderKeySharedEntry(recipientId, senderKey.deviceId); final var uuid = UuidUtil.parseOrNull(senderKey.distributionId); if (uuid == null) { logger.warn("Read invalid distribution id from storage {}, ignoring", senderKey.distributionId); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java index 627ef45e..8011a69d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java @@ -238,8 +238,14 @@ public class SessionStore implements SignalServiceSessionStore { return Arrays.stream(files) .map(f -> sessionFileNamePattern.matcher(f.getName())) .filter(Matcher::matches) - .map(matcher -> new Key(RecipientId.of(Long.parseLong(matcher.group(1))), - Integer.parseInt(matcher.group(2)))) + .map(matcher -> { + final var recipientId = resolver.resolveRecipient(Long.parseLong(matcher.group(1))); + if (recipientId == null) { + return null; + } + return new Key(recipientId, Integer.parseInt(matcher.group(2))); + }) + .filter(Objects::nonNull) .collect(Collectors.toList()); }