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