]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java
Extract RecipientHelper
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / RecipientHelper.java
1 package org.asamk.signal.manager.helper;
2
3 import org.asamk.signal.manager.SignalDependencies;
4 import org.asamk.signal.manager.api.RecipientIdentifier;
5 import org.asamk.signal.manager.config.ServiceConfig;
6 import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
7 import org.asamk.signal.manager.storage.SignalAccount;
8 import org.asamk.signal.manager.storage.recipients.RecipientId;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.whispersystems.libsignal.InvalidKeyException;
12 import org.whispersystems.signalservice.api.push.ACI;
13 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
14 import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
15 import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
16 import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
17 import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
18
19 import java.io.IOException;
20 import java.security.SignatureException;
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25
26 public class RecipientHelper {
27
28 private final static Logger logger = LoggerFactory.getLogger(RecipientHelper.class);
29
30 private final SignalAccount account;
31 private final SignalDependencies dependencies;
32 private final ServiceEnvironmentConfig serviceEnvironmentConfig;
33
34 public RecipientHelper(
35 final SignalAccount account,
36 final SignalDependencies dependencies,
37 final ServiceEnvironmentConfig serviceEnvironmentConfig
38 ) {
39 this.account = account;
40 this.dependencies = dependencies;
41 this.serviceEnvironmentConfig = serviceEnvironmentConfig;
42 }
43
44 public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) {
45 final var address = account.getRecipientStore().resolveRecipientAddress(recipientId);
46 if (address.number().isEmpty() || address.uuid().isPresent()) {
47 return address.toSignalServiceAddress();
48 }
49
50 // Address in recipient store doesn't have a uuid, this shouldn't happen
51 // Try to retrieve the uuid from the server
52 final var number = address.number().get();
53 final ACI aci;
54 try {
55 aci = getRegisteredUser(number);
56 } catch (IOException e) {
57 logger.warn("Failed to get uuid for e164 number: {}", number, e);
58 // Return SignalServiceAddress with unknown UUID
59 return address.toSignalServiceAddress();
60 }
61 return account.getRecipientStore()
62 .resolveRecipientAddress(account.getRecipientStore().resolveRecipient(aci))
63 .toSignalServiceAddress();
64 }
65
66 public Set<RecipientId> resolveRecipients(Collection<RecipientIdentifier.Single> recipients) throws IOException {
67 final var recipientIds = new HashSet<RecipientId>(recipients.size());
68 for (var number : recipients) {
69 final var recipientId = resolveRecipient(number);
70 recipientIds.add(recipientId);
71 }
72 return recipientIds;
73 }
74
75 public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws IOException {
76 if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) {
77 return account.getRecipientStore().resolveRecipient(ACI.from(uuidRecipient.uuid()));
78 } else {
79 final var number = ((RecipientIdentifier.Number) recipient).number();
80 return account.getRecipientStore().resolveRecipient(number, () -> {
81 try {
82 return getRegisteredUser(number);
83 } catch (IOException e) {
84 return null;
85 }
86 });
87 }
88 }
89
90 public RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException {
91 final var address = resolveSignalServiceAddress(recipientId);
92 if (!address.getNumber().isPresent()) {
93 return recipientId;
94 }
95 final var number = address.getNumber().get();
96 final var uuid = getRegisteredUser(number);
97 return account.getRecipientStore().resolveRecipientTrusted(new SignalServiceAddress(uuid, number));
98 }
99
100 public Map<String, ACI> getRegisteredUsers(final Set<String> numbers) throws IOException {
101 final Map<String, ACI> registeredUsers;
102 try {
103 registeredUsers = dependencies.getAccountManager()
104 .getRegisteredUsers(ServiceConfig.getIasKeyStore(),
105 numbers,
106 serviceEnvironmentConfig.getCdsMrenclave());
107 } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
108 throw new IOException(e);
109 }
110
111 // Store numbers as recipients, so we have the number/uuid association
112 registeredUsers.forEach((number, aci) -> account.getRecipientStore()
113 .resolveRecipientTrusted(new SignalServiceAddress(aci, number)));
114
115 return registeredUsers;
116 }
117
118 private ACI getRegisteredUser(final String number) throws IOException {
119 final Map<String, ACI> aciMap;
120 try {
121 aciMap = getRegisteredUsers(Set.of(number));
122 } catch (NumberFormatException e) {
123 throw new UnregisteredUserException(number, e);
124 }
125 final var uuid = aciMap.get(number);
126 if (uuid == null) {
127 throw new UnregisteredUserException(number, null);
128 }
129 return uuid;
130 }
131 }