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