mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Improve behavior of changed recipient id
This commit is contained in:
parent
bbe74ef020
commit
10df4338b1
8 changed files with 95 additions and 28 deletions
|
@ -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),
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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() + ']';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,4 +12,6 @@ public interface RecipientResolver {
|
|||
RecipientId resolveRecipient(SignalServiceAddress address);
|
||||
|
||||
RecipientId resolveRecipient(ACI aci);
|
||||
|
||||
RecipientId resolveRecipient(long recipientId);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue