]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java
Add UnregisteredRecipientException
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / SendHelper.java
1 package org.asamk.signal.manager.helper;
2
3 import org.asamk.signal.manager.SignalDependencies;
4 import org.asamk.signal.manager.api.UnregisteredRecipientException;
5 import org.asamk.signal.manager.groups.GroupId;
6 import org.asamk.signal.manager.groups.GroupNotFoundException;
7 import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
8 import org.asamk.signal.manager.groups.GroupUtils;
9 import org.asamk.signal.manager.groups.NotAGroupMemberException;
10 import org.asamk.signal.manager.storage.SignalAccount;
11 import org.asamk.signal.manager.storage.groups.GroupInfo;
12 import org.asamk.signal.manager.storage.recipients.Profile;
13 import org.asamk.signal.manager.storage.recipients.RecipientId;
14 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import org.whispersystems.libsignal.InvalidKeyException;
18 import org.whispersystems.libsignal.InvalidRegistrationIdException;
19 import org.whispersystems.libsignal.NoSessionException;
20 import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
21 import org.whispersystems.libsignal.util.guava.Optional;
22 import org.whispersystems.signalservice.api.SignalServiceMessageSender;
23 import org.whispersystems.signalservice.api.crypto.ContentHint;
24 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
25 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
26 import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
27 import org.whispersystems.signalservice.api.messages.SendMessageResult;
28 import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
29 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
30 import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
31 import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
32 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
33 import org.whispersystems.signalservice.api.push.DistributionId;
34 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
35 import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
36 import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
37 import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
38 import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
39 import org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException;
40
41 import java.io.IOException;
42 import java.util.ArrayList;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 import java.util.concurrent.TimeUnit;
48 import java.util.stream.Collectors;
49
50 public class SendHelper {
51
52 private final static Logger logger = LoggerFactory.getLogger(SendHelper.class);
53
54 private final SignalAccount account;
55 private final SignalDependencies dependencies;
56 private final UnidentifiedAccessHelper unidentifiedAccessHelper;
57 private final SignalServiceAddressResolver addressResolver;
58 private final RecipientResolver recipientResolver;
59 private final IdentityFailureHandler identityFailureHandler;
60 private final GroupProvider groupProvider;
61 private final ProfileHelper profileHelper;
62 private final RecipientRegistrationRefresher recipientRegistrationRefresher;
63
64 public SendHelper(
65 final SignalAccount account,
66 final SignalDependencies dependencies,
67 final UnidentifiedAccessHelper unidentifiedAccessHelper,
68 final SignalServiceAddressResolver addressResolver,
69 final RecipientResolver recipientResolver,
70 final IdentityFailureHandler identityFailureHandler,
71 final GroupProvider groupProvider,
72 final ProfileHelper profileHelper,
73 final RecipientRegistrationRefresher recipientRegistrationRefresher
74 ) {
75 this.account = account;
76 this.dependencies = dependencies;
77 this.unidentifiedAccessHelper = unidentifiedAccessHelper;
78 this.addressResolver = addressResolver;
79 this.recipientResolver = recipientResolver;
80 this.identityFailureHandler = identityFailureHandler;
81 this.groupProvider = groupProvider;
82 this.profileHelper = profileHelper;
83 this.recipientRegistrationRefresher = recipientRegistrationRefresher;
84 }
85
86 /**
87 * Send a single message to one recipient.
88 * The message is extended with the current expiration timer.
89 */
90 public SendMessageResult sendMessage(
91 final SignalServiceDataMessage.Builder messageBuilder, final RecipientId recipientId
92 ) throws IOException {
93 final var contact = account.getContactStore().getContact(recipientId);
94 final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
95 messageBuilder.withExpiration(expirationTime);
96 messageBuilder.withProfileKey(account.getProfileKey().serialize());
97
98 final var message = messageBuilder.build();
99 final var result = sendMessage(message, recipientId);
100 handleSendMessageResult(result);
101 return result;
102 }
103
104 /**
105 * Send a group message to the given group
106 * The message is extended with the current expiration timer for the group and the group context.
107 */
108 public List<SendMessageResult> sendAsGroupMessage(
109 SignalServiceDataMessage.Builder messageBuilder, GroupId groupId
110 ) throws IOException, GroupNotFoundException, NotAGroupMemberException, GroupSendingNotAllowedException {
111 final var g = getGroupForSending(groupId);
112 return sendAsGroupMessage(messageBuilder, g);
113 }
114
115 private List<SendMessageResult> sendAsGroupMessage(
116 final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo g
117 ) throws IOException, GroupSendingNotAllowedException {
118 GroupUtils.setGroupContext(messageBuilder, g);
119 messageBuilder.withExpiration(g.getMessageExpirationTimer());
120
121 final var message = messageBuilder.build();
122 final var recipients = g.getMembersWithout(account.getSelfRecipientId());
123
124 if (g.isAnnouncementGroup() && !g.isAdmin(account.getSelfRecipientId())) {
125 if (message.getBody().isPresent()
126 || message.getAttachments().isPresent()
127 || message.getQuote().isPresent()
128 || message.getPreviews().isPresent()
129 || message.getMentions().isPresent()
130 || message.getSticker().isPresent()) {
131 throw new GroupSendingNotAllowedException(g.getGroupId(), g.getTitle());
132 }
133 }
134
135 return sendGroupMessage(message, recipients, g.getDistributionId());
136 }
137
138 /**
139 * Send a complete group message to the given recipients (should be current/old/new members)
140 * This method should only be used for create/update/quit group messages.
141 */
142 public List<SendMessageResult> sendGroupMessage(
143 final SignalServiceDataMessage message,
144 final Set<RecipientId> recipientIds,
145 final DistributionId distributionId
146 ) throws IOException {
147 final var messageSender = dependencies.getMessageSender();
148 final var results = sendGroupMessageInternal((recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendDataMessage(
149 recipients,
150 unidentifiedAccess,
151 isRecipientUpdate,
152 ContentHint.DEFAULT,
153 message,
154 SignalServiceMessageSender.LegacyGroupEvents.EMPTY,
155 sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()),
156 () -> false),
157 (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupDataMessage(distId,
158 recipients,
159 unidentifiedAccess,
160 isRecipientUpdate,
161 ContentHint.DEFAULT,
162 message,
163 SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY),
164 recipientIds,
165 distributionId);
166
167 for (var r : results) {
168 handleSendMessageResult(r);
169 }
170
171 return results;
172 }
173
174 public SendMessageResult sendDeliveryReceipt(
175 RecipientId recipientId, List<Long> messageIds
176 ) {
177 var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
178 messageIds,
179 System.currentTimeMillis());
180
181 return sendReceiptMessage(receiptMessage, recipientId);
182 }
183
184 public SendMessageResult sendReceiptMessage(
185 final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId
186 ) {
187 return handleSendMessage(recipientId,
188 (messageSender, address, unidentifiedAccess) -> messageSender.sendReceipt(address,
189 unidentifiedAccess,
190 receiptMessage));
191 }
192
193 public SendMessageResult sendRetryReceipt(
194 DecryptionErrorMessage errorMessage, RecipientId recipientId, Optional<GroupId> groupId
195 ) {
196 logger.debug("Sending retry receipt for {} to {}, device: {}",
197 errorMessage.getTimestamp(),
198 recipientId,
199 errorMessage.getDeviceId());
200 return handleSendMessage(recipientId,
201 (messageSender, address, unidentifiedAccess) -> messageSender.sendRetryReceipt(address,
202 unidentifiedAccess,
203 groupId.transform(GroupId::serialize),
204 errorMessage));
205 }
206
207 public SendMessageResult sendNullMessage(RecipientId recipientId) {
208 return handleSendMessage(recipientId, SignalServiceMessageSender::sendNullMessage);
209 }
210
211 public SendMessageResult sendSelfMessage(
212 SignalServiceDataMessage.Builder messageBuilder
213 ) {
214 final var recipientId = account.getSelfRecipientId();
215 final var contact = account.getContactStore().getContact(recipientId);
216 final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
217 messageBuilder.withExpiration(expirationTime);
218
219 var message = messageBuilder.build();
220 return sendSelfMessage(message);
221 }
222
223 public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) {
224 var messageSender = dependencies.getMessageSender();
225 try {
226 return messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync());
227 } catch (UnregisteredUserException e) {
228 var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
229 return SendMessageResult.unregisteredFailure(address);
230 } catch (ProofRequiredException e) {
231 var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
232 return SendMessageResult.proofRequiredFailure(address, e);
233 } catch (RateLimitException e) {
234 var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
235 logger.warn("Sending failed due to rate limiting from the signal server: {}", e.getMessage());
236 return SendMessageResult.networkFailure(address);
237 } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
238 var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
239 return SendMessageResult.identityFailure(address, e.getIdentityKey());
240 } catch (IOException e) {
241 var address = addressResolver.resolveSignalServiceAddress(account.getSelfRecipientId());
242 logger.warn("Failed to send message due to IO exception: {}", e.getMessage());
243 return SendMessageResult.networkFailure(address);
244 }
245 }
246
247 public SendMessageResult sendTypingMessage(
248 SignalServiceTypingMessage message, RecipientId recipientId
249 ) {
250 return handleSendMessage(recipientId,
251 (messageSender, address, unidentifiedAccess) -> messageSender.sendTyping(address,
252 unidentifiedAccess,
253 message));
254 }
255
256 public List<SendMessageResult> sendGroupTypingMessage(
257 SignalServiceTypingMessage message, GroupId groupId
258 ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
259 final var g = getGroupForSending(groupId);
260 if (g.isAnnouncementGroup() && !g.isAdmin(account.getSelfRecipientId())) {
261 throw new GroupSendingNotAllowedException(groupId, g.getTitle());
262 }
263 final var distributionId = g.getDistributionId();
264 final var recipientIds = g.getMembersWithout(account.getSelfRecipientId());
265
266 return sendGroupTypingMessage(message, recipientIds, distributionId);
267 }
268
269 private List<SendMessageResult> sendGroupTypingMessage(
270 final SignalServiceTypingMessage message,
271 final Set<RecipientId> recipientIds,
272 final DistributionId distributionId
273 ) throws IOException {
274 final var messageSender = dependencies.getMessageSender();
275 final var results = sendGroupMessageInternal((recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendTyping(
276 recipients,
277 unidentifiedAccess,
278 message,
279 () -> false),
280 (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupTyping(distId,
281 recipients,
282 unidentifiedAccess,
283 message),
284 recipientIds,
285 distributionId);
286
287 for (var r : results) {
288 handleSendMessageResult(r);
289 }
290
291 return results;
292 }
293
294 private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
295 var g = groupProvider.getGroup(groupId);
296 if (g == null) {
297 throw new GroupNotFoundException(groupId);
298 }
299 if (!g.isMember(account.getSelfRecipientId())) {
300 throw new NotAGroupMemberException(groupId, g.getTitle());
301 }
302 return g;
303 }
304
305 private List<SendMessageResult> sendGroupMessageInternal(
306 final LegacySenderHandler legacySender,
307 final SenderKeySenderHandler senderKeySender,
308 final Set<RecipientId> recipientIds,
309 final DistributionId distributionId
310 ) throws IOException {
311 // isRecipientUpdate is true if we've already sent this message to some recipients in the past, otherwise false.
312 final var isRecipientUpdate = false;
313 Set<RecipientId> senderKeyTargets = distributionId == null
314 ? Set.of()
315 : getSenderKeyCapableRecipientIds(recipientIds);
316 final var allResults = new ArrayList<SendMessageResult>(recipientIds.size());
317
318 if (senderKeyTargets.size() > 0) {
319 final var results = sendGroupMessageInternalWithSenderKey(senderKeySender,
320 senderKeyTargets,
321 distributionId,
322 isRecipientUpdate);
323
324 if (results == null) {
325 senderKeyTargets = Set.of();
326 } else {
327 results.stream().filter(SendMessageResult::isSuccess).forEach(allResults::add);
328 final var failedTargets = results.stream()
329 .filter(r -> !r.isSuccess())
330 .map(r -> recipientResolver.resolveRecipient(r.getAddress()))
331 .toList();
332 if (failedTargets.size() > 0) {
333 senderKeyTargets = new HashSet<>(senderKeyTargets);
334 failedTargets.forEach(senderKeyTargets::remove);
335 }
336 }
337 }
338
339 final var legacyTargets = new HashSet<>(recipientIds);
340 legacyTargets.removeAll(senderKeyTargets);
341 final boolean onlyTargetIsSelfWithLinkedDevice = recipientIds.isEmpty() && account.isMultiDevice();
342
343 if (legacyTargets.size() > 0 || onlyTargetIsSelfWithLinkedDevice) {
344 if (legacyTargets.size() > 0) {
345 logger.debug("Need to do {} legacy sends.", legacyTargets.size());
346 } else {
347 logger.debug("Need to do a legacy send to send a sync message for a group of only ourselves.");
348 }
349
350 final List<SendMessageResult> results = sendGroupMessageInternalWithLegacy(legacySender,
351 legacyTargets,
352 isRecipientUpdate || allResults.size() > 0);
353 allResults.addAll(results);
354 }
355
356 return allResults;
357 }
358
359 private Set<RecipientId> getSenderKeyCapableRecipientIds(final Set<RecipientId> recipientIds) {
360 final var selfProfile = profileHelper.getRecipientProfile(account.getSelfRecipientId());
361 if (selfProfile == null || !selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) {
362 logger.debug("Not all of our devices support sender key. Using legacy.");
363 return Set.of();
364 }
365
366 final var senderKeyTargets = new HashSet<RecipientId>();
367 final var recipientList = new ArrayList<>(recipientIds);
368 final var profiles = profileHelper.getRecipientProfile(recipientList).iterator();
369 for (final var recipientId : recipientList) {
370 final var profile = profiles.next();
371 if (profile == null || !profile.getCapabilities().contains(Profile.Capability.senderKey)) {
372 continue;
373 }
374
375 final var access = unidentifiedAccessHelper.getAccessFor(recipientId);
376 if (!access.isPresent() || !access.get().getTargetUnidentifiedAccess().isPresent()) {
377 continue;
378 }
379
380 final var identity = account.getIdentityKeyStore().getIdentity(recipientId);
381 if (identity == null || !identity.getTrustLevel().isTrusted()) {
382 continue;
383 }
384
385 senderKeyTargets.add(recipientId);
386 }
387
388 if (senderKeyTargets.size() < 2) {
389 logger.debug("Too few sender-key-capable users ({}). Doing all legacy sends.", senderKeyTargets.size());
390 return Set.of();
391 }
392
393 logger.debug("Can use sender key for {}/{} recipients.", senderKeyTargets.size(), recipientIds.size());
394 return senderKeyTargets;
395 }
396
397 private List<SendMessageResult> sendGroupMessageInternalWithLegacy(
398 final LegacySenderHandler sender, final Set<RecipientId> recipientIds, final boolean isRecipientUpdate
399 ) throws IOException {
400 final var recipientIdList = new ArrayList<>(recipientIds);
401 final var addresses = recipientIdList.stream().map(addressResolver::resolveSignalServiceAddress).toList();
402 final var unidentifiedAccesses = unidentifiedAccessHelper.getAccessFor(recipientIdList);
403 try {
404 final var results = sender.send(addresses, unidentifiedAccesses, isRecipientUpdate);
405
406 final var successCount = results.stream().filter(SendMessageResult::isSuccess).count();
407 logger.debug("Successfully sent using 1:1 to {}/{} legacy targets.", successCount, recipientIdList.size());
408 return results;
409 } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
410 return List.of();
411 }
412 }
413
414 private List<SendMessageResult> sendGroupMessageInternalWithSenderKey(
415 final SenderKeySenderHandler sender,
416 final Set<RecipientId> recipientIds,
417 final DistributionId distributionId,
418 final boolean isRecipientUpdate
419 ) throws IOException {
420 final var recipientIdList = new ArrayList<>(recipientIds);
421
422 long keyCreateTime = account.getSenderKeyStore()
423 .getCreateTimeForOurKey(account.getSelfRecipientId(), account.getDeviceId(), distributionId);
424 long keyAge = System.currentTimeMillis() - keyCreateTime;
425
426 if (keyCreateTime != -1 && keyAge > TimeUnit.DAYS.toMillis(14)) {
427 logger.debug("DistributionId {} was created at {} and is {} ms old (~{} days). Rotating.",
428 distributionId,
429 keyCreateTime,
430 keyAge,
431 TimeUnit.MILLISECONDS.toDays(keyAge));
432 account.getSenderKeyStore().deleteOurKey(account.getSelfRecipientId(), distributionId);
433 }
434
435 List<SignalServiceAddress> addresses = recipientIdList.stream()
436 .map(addressResolver::resolveSignalServiceAddress)
437 .collect(Collectors.toList());
438 List<UnidentifiedAccess> unidentifiedAccesses = unidentifiedAccessHelper.getAccessFor(recipientIdList)
439 .stream()
440 .map(Optional::get)
441 .map(UnidentifiedAccessPair::getTargetUnidentifiedAccess)
442 .map(Optional::get)
443 .collect(Collectors.toList());
444
445 try {
446 List<SendMessageResult> results = sender.send(distributionId,
447 addresses,
448 unidentifiedAccesses,
449 isRecipientUpdate);
450
451 final var successCount = results.stream().filter(SendMessageResult::isSuccess).count();
452 logger.debug("Successfully sent using sender key to {}/{} sender key targets.",
453 successCount,
454 addresses.size());
455
456 return results;
457 } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
458 return null;
459 } catch (InvalidUnidentifiedAccessHeaderException e) {
460 logger.warn("Someone had a bad UD header. Falling back to legacy sends.", e);
461 return null;
462 } catch (NoSessionException e) {
463 logger.warn("No session. Falling back to legacy sends.", e);
464 account.getSenderKeyStore().deleteOurKey(account.getSelfRecipientId(), distributionId);
465 return null;
466 } catch (InvalidKeyException e) {
467 logger.warn("Invalid key. Falling back to legacy sends.", e);
468 account.getSenderKeyStore().deleteOurKey(account.getSelfRecipientId(), distributionId);
469 return null;
470 } catch (InvalidRegistrationIdException e) {
471 logger.warn("Invalid registrationId. Falling back to legacy sends.", e);
472 return null;
473 } catch (NotFoundException e) {
474 logger.warn("Someone was unregistered. Falling back to legacy sends.", e);
475 return null;
476 }
477 }
478
479 private SendMessageResult sendMessage(
480 SignalServiceDataMessage message, RecipientId recipientId
481 ) {
482 return handleSendMessage(recipientId,
483 (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address,
484 unidentifiedAccess,
485 ContentHint.DEFAULT,
486 message,
487 SignalServiceMessageSender.IndividualSendEvents.EMPTY));
488 }
489
490 private SendMessageResult handleSendMessage(RecipientId recipientId, SenderHandler s) {
491 var messageSender = dependencies.getMessageSender();
492
493 var address = addressResolver.resolveSignalServiceAddress(recipientId);
494 try {
495 try {
496 return s.send(messageSender, address, unidentifiedAccessHelper.getAccessFor(recipientId));
497 } catch (UnregisteredUserException e) {
498 final RecipientId newRecipientId;
499 try {
500 newRecipientId = recipientRegistrationRefresher.refreshRecipientRegistration(recipientId);
501 } catch (UnregisteredRecipientException ex) {
502 return SendMessageResult.unregisteredFailure(address);
503 }
504 address = addressResolver.resolveSignalServiceAddress(newRecipientId);
505 return s.send(messageSender, address, unidentifiedAccessHelper.getAccessFor(newRecipientId));
506 }
507 } catch (UnregisteredUserException e) {
508 return SendMessageResult.unregisteredFailure(address);
509 } catch (ProofRequiredException e) {
510 return SendMessageResult.proofRequiredFailure(address, e);
511 } catch (RateLimitException e) {
512 logger.warn("Sending failed due to rate limiting from the signal server: {}", e.getMessage());
513 return SendMessageResult.networkFailure(address);
514 } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
515 return SendMessageResult.identityFailure(address, e.getIdentityKey());
516 } catch (IOException e) {
517 logger.warn("Failed to send message due to IO exception: {}", e.getMessage());
518 return SendMessageResult.networkFailure(address);
519 }
520 }
521
522 private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) {
523 var address = account.getSelfAddress();
524 var transcript = new SentTranscriptMessage(Optional.of(address),
525 message.getTimestamp(),
526 message,
527 message.getExpiresInSeconds(),
528 Map.of(address, true),
529 false);
530 var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);
531
532 return sendSyncMessage(syncMessage);
533 }
534
535 private void handleSendMessageResult(final SendMessageResult r) {
536 if (r.isSuccess() && !r.getSuccess().isUnidentified()) {
537 final var recipientId = recipientResolver.resolveRecipient(r.getAddress());
538 final var profile = account.getRecipientStore().getProfile(recipientId);
539 if (profile != null && (
540 profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.ENABLED
541 || profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED
542 )) {
543 account.getRecipientStore()
544 .storeProfile(recipientId,
545 Profile.newBuilder(profile)
546 .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN)
547 .build());
548 }
549 }
550 if (r.isUnregisteredFailure()) {
551 final var recipientId = recipientResolver.resolveRecipient(r.getAddress());
552 final var profile = account.getRecipientStore().getProfile(recipientId);
553 if (profile != null && (
554 profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.ENABLED
555 || profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED
556 )) {
557 account.getRecipientStore()
558 .storeProfile(recipientId,
559 Profile.newBuilder(profile)
560 .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN)
561 .build());
562 }
563 }
564 if (r.getIdentityFailure() != null) {
565 final var recipientId = recipientResolver.resolveRecipient(r.getAddress());
566 identityFailureHandler.handleIdentityFailure(recipientId, r.getIdentityFailure());
567 }
568 }
569
570 interface SenderHandler {
571
572 SendMessageResult send(
573 SignalServiceMessageSender messageSender,
574 SignalServiceAddress address,
575 Optional<UnidentifiedAccessPair> unidentifiedAccess
576 ) throws IOException, UnregisteredUserException, ProofRequiredException, RateLimitException, org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
577 }
578
579 interface SenderKeySenderHandler {
580
581 List<SendMessageResult> send(
582 DistributionId distributionId,
583 List<SignalServiceAddress> recipients,
584 List<UnidentifiedAccess> unidentifiedAccess,
585 boolean isRecipientUpdate
586 ) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException;
587 }
588
589 interface LegacySenderHandler {
590
591 List<SendMessageResult> send(
592 List<SignalServiceAddress> recipients,
593 List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
594 boolean isRecipientUpdate
595 ) throws IOException, UntrustedIdentityException;
596 }
597 }