]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java
5cf3c675e2275d8b4d201f93cbe943d37ef089ff
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / recipients / MergeRecipientHelper.java
1 package org.asamk.signal.manager.storage.recipients;
2
3 import org.asamk.signal.manager.api.Pair;
4 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory;
6
7 import java.sql.SQLException;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Set;
11 import java.util.stream.Collectors;
12
13 public class MergeRecipientHelper {
14
15 private static final Logger logger = LoggerFactory.getLogger(MergeRecipientHelper.class);
16
17 static Pair<RecipientId, List<RecipientId>> resolveRecipientTrustedLocked(
18 Store store, RecipientAddress address
19 ) throws SQLException {
20 // address has serviceId and number, optionally also pni
21
22 final var recipients = store.findAllByAddress(address);
23
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());
27 }
28
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());
33 }
34
35 if (recipient.address().serviceId().isEmpty() || (
36 recipient.address().serviceId().equals(address.serviceId())
37 ) || (
38 recipient.address().pni().isPresent() && recipient.address().pni().equals(address.serviceId())
39 ) || (
40 recipient.address().serviceId().equals(address.pni())
41 ) || (
42 address.pni().isPresent() && address.pni().equals(recipient.address().pni())
43 )) {
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());
47 }
48
49 logger.debug(
50 "Got recipient {} existing with number/pni, but different serviceId, so stripping its number and adding new recipient",
51 recipient.id());
52 store.updateRecipientAddress(recipient.id(), recipient.address().removeIdentifiersFrom(address));
53
54 return new Pair<>(store.addNewRecipient(address), List.of());
55 }
56
57 var resultingRecipient = recipients.stream()
58 .filter(r -> r.address().serviceId().equals(address.serviceId()) || r.address()
59 .pni()
60 .equals(address.serviceId()))
61 .findFirst();
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())
65 )).findFirst();
66 }
67
68 final Set<RecipientWithAddress> remainingRecipients;
69 if (resultingRecipient.isEmpty()) {
70 remainingRecipients = recipients;
71 } else {
72 remainingRecipients = new HashSet<>(recipients);
73 remainingRecipients.remove(resultingRecipient.get());
74 }
75
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);
81 continue;
82 }
83
84 if (recipient.address().hasOnlyPniAndNumber()) {
85 // PNI and phone number are linked by the server
86 recipientsToBeMerged.add(recipient);
87 continue;
88 }
89
90 recipientsToBeStripped.add(recipient);
91 }
92
93 logger.debug("Got separate recipients for high trust identifiers {}, need to merge ({}) and strip ({})",
94 address,
95 recipientsToBeMerged.stream().map(r -> r.id().toString()).collect(Collectors.joining(", ")),
96 recipientsToBeStripped.stream().map(r -> r.id().toString()).collect(Collectors.joining(", ")));
97
98 RecipientAddress finalAddress = resultingRecipient.map(RecipientWithAddress::address).orElse(null);
99 for (final var recipient : recipientsToBeMerged) {
100 if (finalAddress == null) {
101 finalAddress = recipient.address();
102 } else {
103 finalAddress = finalAddress.withIdentifiersFrom(recipient.address());
104 }
105 store.removeRecipientAddress(recipient.id());
106 }
107 if (finalAddress == null) {
108 finalAddress = address;
109 } else {
110 finalAddress = finalAddress.withIdentifiersFrom(address);
111 }
112
113 for (final var recipient : recipientsToBeStripped) {
114 store.updateRecipientAddress(recipient.id(), recipient.address().removeIdentifiersFrom(address));
115 }
116
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))
120 .toList();
121
122 if (resultingRecipient.isPresent()) {
123 store.updateRecipientAddress(resultingRecipient.get().id(), finalAddress);
124 return new Pair<>(resultingRecipient.get().id(), toBeMergedRecipientIds);
125 }
126
127 return new Pair<>(store.addNewRecipient(finalAddress), toBeMergedRecipientIds);
128 }
129
130 public interface Store {
131
132 Set<RecipientWithAddress> findAllByAddress(final RecipientAddress address) throws SQLException;
133
134 RecipientId addNewRecipient(final RecipientAddress address) throws SQLException;
135
136 void updateRecipientAddress(RecipientId recipientId, final RecipientAddress address) throws SQLException;
137
138 void removeRecipientAddress(RecipientId recipientId) throws SQLException;
139 }
140 }