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 final static Logger logger
= LoggerFactory
.getLogger(MergeRecipientHelper
.class);
17 static Pair
<RecipientId
, List
<RecipientId
>> resolveRecipientTrustedLocked(
18 Store store
, RecipientAddress address
19 ) throws SQLException
{
20 // address has serviceId and number, optionally also pni
22 final var recipients
= store
.findAllByAddress(address
);
24 if (recipients
.isEmpty()) {
25 logger
.debug("Got new recipient, serviceId, PNI and number 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().serviceId().isEmpty() || (
36 recipient
.address().serviceId().equals(address
.serviceId())
38 recipient
.address().pni().isPresent() && recipient
.address().pni().equals(address
.serviceId())
40 recipient
.address().serviceId().equals(address
.pni())
42 address
.pni().isPresent() && address
.pni().equals(recipient
.address().pni())
44 logger
.debug("Got existing recipient {}, updating with high trust address", recipient
.id());
45 store
.updateRecipientAddress(recipient
.id(), recipient
.address().withIdentifiersFrom(address
));
46 return new Pair
<>(recipient
.id(), List
.of());
50 "Got recipient {} existing with number/pni, but different serviceId, so stripping its number and adding new recipient",
52 store
.updateRecipientAddress(recipient
.id(), recipient
.address().removeIdentifiersFrom(address
));
54 return new Pair
<>(store
.addNewRecipient(address
), List
.of());
57 var resultingRecipient
= recipients
.stream()
58 .filter(r
-> r
.address().serviceId().equals(address
.serviceId()) || r
.address()
60 .equals(address
.serviceId()))
62 if (resultingRecipient
.isEmpty() && address
.pni().isPresent()) {
63 resultingRecipient
= recipients
.stream().filter(r
-> r
.address().serviceId().equals(address
.pni()) || (
64 address
.serviceId().equals(address
.pni()) && r
.address().pni().equals(address
.pni())
68 final Set
<RecipientWithAddress
> remainingRecipients
;
69 if (resultingRecipient
.isEmpty()) {
70 remainingRecipients
= recipients
;
72 remainingRecipients
= new HashSet
<>(recipients
);
73 remainingRecipients
.remove(resultingRecipient
.get());
76 final var recipientsToBeMerged
= new HashSet
<RecipientWithAddress
>();
77 final var recipientsToBeStripped
= new HashSet
<RecipientWithAddress
>();
78 for (final var recipient
: remainingRecipients
) {
79 if (!recipient
.address().hasAdditionalIdentifiersThan(address
)) {
80 recipientsToBeMerged
.add(recipient
);
84 if (recipient
.address().hasOnlyPniAndNumber()) {
85 // PNI and phone number are linked by the server
86 recipientsToBeMerged
.add(recipient
);
90 recipientsToBeStripped
.add(recipient
);
93 logger
.debug("Got separate recipients for high trust identifiers {}, need to merge ({}) and strip ({})",
95 recipientsToBeMerged
.stream().map(r
-> r
.id().toString()).collect(Collectors
.joining(", ")),
96 recipientsToBeStripped
.stream().map(r
-> r
.id().toString()).collect(Collectors
.joining(", ")));
98 RecipientAddress finalAddress
= resultingRecipient
.map(RecipientWithAddress
::address
).orElse(null);
99 for (final var recipient
: recipientsToBeMerged
) {
100 if (finalAddress
== null) {
101 finalAddress
= recipient
.address();
103 finalAddress
= finalAddress
.withIdentifiersFrom(recipient
.address());
105 store
.removeRecipientAddress(recipient
.id());
107 if (finalAddress
== null) {
108 finalAddress
= address
;
110 finalAddress
= finalAddress
.withIdentifiersFrom(address
);
113 for (final var recipient
: recipientsToBeStripped
) {
114 store
.updateRecipientAddress(recipient
.id(), recipient
.address().removeIdentifiersFrom(address
));
117 // Create fixed RecipientIds that won't update its id after merged
118 final var toBeMergedRecipientIds
= recipientsToBeMerged
.stream()
119 .map(r
-> new RecipientId(r
.id().id(), null))
122 if (resultingRecipient
.isPresent()) {
123 store
.updateRecipientAddress(resultingRecipient
.get().id(), finalAddress
);
124 return new Pair
<>(resultingRecipient
.get().id(), toBeMergedRecipientIds
);
127 return new Pair
<>(store
.addNewRecipient(finalAddress
), toBeMergedRecipientIds
);
130 public interface Store
{
132 Set
<RecipientWithAddress
> findAllByAddress(final RecipientAddress address
) throws SQLException
;
134 RecipientId
addNewRecipient(final RecipientAddress address
) throws SQLException
;
136 void updateRecipientAddress(RecipientId recipientId
, final RecipientAddress address
) throws SQLException
;
138 void removeRecipientAddress(RecipientId recipientId
) throws SQLException
;