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(
18 Store store
, RecipientAddress address
19 ) throws SQLException
{
20 // address has at least one of serviceId/pni and optionally number/username
22 final var recipients
= store
.findAllByAddress(address
);
24 if (recipients
.isEmpty()) {
25 logger
.debug("Got new recipient, serviceId, PNI, number, username are unknown");
26 return new Pair
<>(store
.addNewRecipient(address
), List
.of());
29 if (recipients
.size() == 1) {
30 final var recipient
= recipients
.stream().findFirst().get();
31 if (recipient
.address().hasIdentifiersOf(address
)) {
32 return new Pair
<>(recipient
.id(), List
.of());
35 if (recipient
.address().aci().isEmpty() || (
36 address
.aci().isEmpty() && (
37 address
.pni().isEmpty() || recipient
.address().pni().equals(address
.pni())
39 ) || recipient
.address().aci().equals(address
.aci())) {
40 logger
.debug("Got existing recipient {}, updating with high trust address", recipient
.id());
41 store
.updateRecipientAddress(recipient
.id(), recipient
.address().withIdentifiersFrom(address
));
42 return new Pair
<>(recipient
.id(), List
.of());
46 "Got recipient {} existing with number/pni/username, but different aci, so stripping its number and adding new recipient",
48 store
.updateRecipientAddress(recipient
.id(), recipient
.address().removeIdentifiersFrom(address
));
50 return new Pair
<>(store
.addNewRecipient(address
), List
.of());
53 var resultingRecipient
= recipients
.stream()
54 .filter(r
-> r
.address().aci().isPresent() && r
.address().aci().equals(address
.aci()))
56 if (resultingRecipient
.isEmpty() && address
.pni().isPresent()) {
57 resultingRecipient
= recipients
.stream().filter(r
-> r
.address().pni().equals(address
.pni())).findFirst();
60 final Set
<RecipientWithAddress
> remainingRecipients
;
61 if (resultingRecipient
.isEmpty()) {
62 remainingRecipients
= recipients
;
64 remainingRecipients
= new HashSet
<>(recipients
);
65 remainingRecipients
.remove(resultingRecipient
.get());
68 final var recipientsToBeMerged
= new HashSet
<RecipientWithAddress
>();
69 final var recipientsToBeStripped
= new HashSet
<RecipientWithAddress
>();
70 for (final var recipient
: remainingRecipients
) {
71 if (!recipient
.address().hasAdditionalIdentifiersThan(address
)) {
72 recipientsToBeMerged
.add(recipient
);
76 if (recipient
.address().hasOnlyPniAndNumber()) {
77 // PNI and phone number are linked by the server
78 recipientsToBeMerged
.add(recipient
);
82 recipientsToBeStripped
.add(recipient
);
85 logger
.debug("Got separate recipients for high trust identifiers {}, need to merge ({}) and strip ({})",
87 recipientsToBeMerged
.stream().map(r
-> r
.id().toString()).collect(Collectors
.joining(", ")),
88 recipientsToBeStripped
.stream().map(r
-> r
.id().toString()).collect(Collectors
.joining(", ")));
90 RecipientAddress finalAddress
= resultingRecipient
.map(RecipientWithAddress
::address
).orElse(null);
91 for (final var recipient
: recipientsToBeMerged
) {
92 if (finalAddress
== null) {
93 finalAddress
= recipient
.address();
95 finalAddress
= finalAddress
.withIdentifiersFrom(recipient
.address());
97 store
.removeRecipientAddress(recipient
.id());
99 if (finalAddress
== null) {
100 finalAddress
= address
;
102 finalAddress
= finalAddress
.withIdentifiersFrom(address
);
105 for (final var recipient
: recipientsToBeStripped
) {
106 store
.updateRecipientAddress(recipient
.id(), recipient
.address().removeIdentifiersFrom(finalAddress
));
109 // Create fixed RecipientIds that won't update its id after merged
110 final var toBeMergedRecipientIds
= recipientsToBeMerged
.stream()
111 .map(r
-> new RecipientId(r
.id().id(), null))
114 if (resultingRecipient
.isPresent()) {
115 store
.updateRecipientAddress(resultingRecipient
.get().id(), finalAddress
);
116 return new Pair
<>(resultingRecipient
.get().id(), toBeMergedRecipientIds
);
119 return new Pair
<>(store
.addNewRecipient(finalAddress
), toBeMergedRecipientIds
);
122 public interface Store
{
124 Set
<RecipientWithAddress
> findAllByAddress(final RecipientAddress address
) throws SQLException
;
126 RecipientId
addNewRecipient(final RecipientAddress address
) throws SQLException
;
128 void updateRecipientAddress(RecipientId recipientId
, final RecipientAddress address
) throws SQLException
;
130 void removeRecipientAddress(RecipientId recipientId
) throws SQLException
;