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