1 package org
.asamk
.signal
.manager
.storage
.recipients
;
3 import org
.asamk
.signal
.manager
.api
.Pair
;
4 import org
.slf4j
.Logger
;
5 import org
.slf4j
.LoggerFactory
;
7 import java
.sql
.SQLException
;
8 import java
.util
.HashSet
;
11 import java
.util
.stream
.Collectors
;
13 public class MergeRecipientHelper
{
15 private static final Logger logger
= LoggerFactory
.getLogger(MergeRecipientHelper
.class);
17 static Pair
<RecipientId
, List
<RecipientId
>> resolveRecipientTrustedLocked(
19 RecipientAddress address
20 ) throws SQLException
{
21 // address has at least one of serviceId/pni and optionally number/username
23 final var recipients
= store
.findAllByAddress(address
);
25 if (recipients
.isEmpty()) {
26 logger
.debug("Got new recipient, serviceId, PNI, number, username are unknown");
27 return new Pair
<>(store
.addNewRecipient(address
), List
.of());
30 if (recipients
.size() == 1) {
31 final var recipient
= recipients
.stream().findFirst().get();
32 if (recipient
.address().hasIdentifiersOf(address
)) {
33 return new Pair
<>(recipient
.id(), List
.of());
36 if (recipient
.address().aci().isEmpty() || (
37 address
.aci().isEmpty() && (
38 address
.pni().isEmpty() || recipient
.address().pni().equals(address
.pni())
40 ) || recipient
.address().aci().equals(address
.aci())) {
41 logger
.debug("Got existing recipient {}, updating with high trust address", recipient
.id());
42 store
.updateRecipientAddress(recipient
.id(), address
.withOtherIdentifiersFrom(recipient
.address()));
43 return new Pair
<>(recipient
.id(), List
.of());
47 "Got recipient {} existing with number/pni/username, but different aci, so stripping its number and adding new recipient",
49 store
.updateRecipientAddress(recipient
.id(), recipient
.address().removeIdentifiersFrom(address
));
51 return new Pair
<>(store
.addNewRecipient(address
), List
.of());
54 var resultingRecipient
= recipients
.stream()
55 .filter(r
-> r
.address().aci().isPresent() && r
.address().aci().equals(address
.aci()))
57 if (resultingRecipient
.isEmpty() && address
.pni().isPresent()) {
58 resultingRecipient
= recipients
.stream().filter(r
-> r
.address().pni().equals(address
.pni())).findFirst();
61 final Set
<RecipientWithAddress
> remainingRecipients
;
62 if (resultingRecipient
.isEmpty()) {
63 remainingRecipients
= recipients
;
65 remainingRecipients
= new HashSet
<>(recipients
);
66 remainingRecipients
.remove(resultingRecipient
.get());
69 final var recipientsToBeMerged
= new HashSet
<RecipientWithAddress
>();
70 final var recipientsToBeStripped
= new HashSet
<RecipientWithAddress
>();
71 for (final var recipient
: remainingRecipients
) {
72 if (!recipient
.address().hasAdditionalIdentifiersThan(address
)) {
73 recipientsToBeMerged
.add(recipient
);
77 if (recipient
.address().hasOnlyPniAndNumber()) {
78 // PNI and phone number are linked by the server
79 recipientsToBeMerged
.add(recipient
);
83 recipientsToBeStripped
.add(recipient
);
86 logger
.debug("Got separate recipients for high trust identifiers {}, need to merge ({}) and strip ({})",
88 recipientsToBeMerged
.stream().map(r
-> r
.id().toString()).collect(Collectors
.joining(", ")),
89 recipientsToBeStripped
.stream().map(r
-> r
.id().toString()).collect(Collectors
.joining(", ")));
91 RecipientAddress finalAddress
= resultingRecipient
.map(RecipientWithAddress
::address
).orElse(null);
92 for (final var recipient
: recipientsToBeMerged
) {
93 if (finalAddress
== null) {
94 finalAddress
= recipient
.address();
96 finalAddress
= finalAddress
.withOtherIdentifiersFrom(recipient
.address());
98 store
.removeRecipientAddress(recipient
.id());
100 if (finalAddress
== null) {
101 finalAddress
= address
;
103 finalAddress
= address
.withOtherIdentifiersFrom(finalAddress
);
106 for (final var recipient
: recipientsToBeStripped
) {
107 store
.updateRecipientAddress(recipient
.id(), recipient
.address().removeIdentifiersFrom(finalAddress
));
110 // Create fixed RecipientIds that won't update its id after merged
111 final var toBeMergedRecipientIds
= recipientsToBeMerged
.stream()
112 .map(r
-> new RecipientId(r
.id().id(), null))
115 if (resultingRecipient
.isPresent()) {
116 store
.updateRecipientAddress(resultingRecipient
.get().id(), finalAddress
);
117 return new Pair
<>(resultingRecipient
.get().id(), toBeMergedRecipientIds
);
120 return new Pair
<>(store
.addNewRecipient(finalAddress
), toBeMergedRecipientIds
);
123 public interface Store
{
125 Set
<RecipientWithAddress
> findAllByAddress(final RecipientAddress address
) throws SQLException
;
127 RecipientId
addNewRecipient(final RecipientAddress address
) throws SQLException
;
129 void updateRecipientAddress(RecipientId recipientId
, final RecipientAddress address
) throws SQLException
;
131 void removeRecipientAddress(RecipientId recipientId
) throws SQLException
;