Improve behavior of changed recipient id

This commit is contained in:
AsamK 2021-12-07 00:02:24 +01:00
parent bbe74ef020
commit 10df4338b1
8 changed files with 95 additions and 28 deletions

View file

@ -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),

View file

@ -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());
}

View file

@ -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() + ']';
}
}

View file

@ -12,4 +12,6 @@ public interface RecipientResolver {
RecipientId resolveRecipient(SignalServiceAddress address);
RecipientId resolveRecipient(ACI aci);
RecipientId resolveRecipient(long recipientId);
}

View file

@ -44,7 +44,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
private final RecipientMergeHandler recipientMergeHandler;
private final Map<RecipientId, Recipient> recipients;
private final Map<RecipientId, RecipientId> recipientsMerged = new HashMap<>();
private final Map<Long, Long> 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<RecipientId, Recipient> 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() {

View file

@ -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());
}

View file

@ -47,7 +47,11 @@ public class SenderKeySharedStore {
final var storage = objectMapper.readValue(inputStream, Storage.class);
final var sharedSenderKeys = new HashMap<DistributionId, Set<SenderKeySharedEntry>>();
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);

View file

@ -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());
}