1 package org
.asamk
.signal
.manager
.helper
;
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
;
41 import java
.io
.IOException
;
42 import java
.util
.ArrayList
;
43 import java
.util
.HashSet
;
44 import java
.util
.List
;
47 import java
.util
.concurrent
.TimeUnit
;
48 import java
.util
.stream
.Collectors
;
50 public class SendHelper
{
52 private final static Logger logger
= LoggerFactory
.getLogger(SendHelper
.class);
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
;
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
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
;
87 * Send a single message to one recipient.
88 * The message is extended with the current expiration timer.
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());
98 final var message
= messageBuilder
.build();
99 final var result
= sendMessage(message
, recipientId
);
100 handleSendMessageResult(result
);
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.
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
);
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());
121 final var message
= messageBuilder
.build();
122 final var recipients
= g
.getMembersWithout(account
.getSelfRecipientId());
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());
135 return sendGroupMessage(message
, recipients
, g
.getDistributionId());
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.
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(
154 SignalServiceMessageSender
.LegacyGroupEvents
.EMPTY
,
155 sendResult
-> logger
.trace("Partial message send result: {}", sendResult
.isSuccess()),
157 (distId
, recipients
, unidentifiedAccess
, isRecipientUpdate
) -> messageSender
.sendGroupDataMessage(distId
,
163 SignalServiceMessageSender
.SenderKeyGroupEvents
.EMPTY
),
167 for (var r
: results
) {
168 handleSendMessageResult(r
);
174 public SendMessageResult
sendDeliveryReceipt(
175 RecipientId recipientId
, List
<Long
> messageIds
177 var receiptMessage
= new SignalServiceReceiptMessage(SignalServiceReceiptMessage
.Type
.DELIVERY
,
179 System
.currentTimeMillis());
181 return sendReceiptMessage(receiptMessage
, recipientId
);
184 public SendMessageResult
sendReceiptMessage(
185 final SignalServiceReceiptMessage receiptMessage
, final RecipientId recipientId
187 return handleSendMessage(recipientId
,
188 (messageSender
, address
, unidentifiedAccess
) -> messageSender
.sendReceipt(address
,
193 public SendMessageResult
sendRetryReceipt(
194 DecryptionErrorMessage errorMessage
, RecipientId recipientId
, Optional
<GroupId
> groupId
196 logger
.debug("Sending retry receipt for {} to {}, device: {}",
197 errorMessage
.getTimestamp(),
199 errorMessage
.getDeviceId());
200 return handleSendMessage(recipientId
,
201 (messageSender
, address
, unidentifiedAccess
) -> messageSender
.sendRetryReceipt(address
,
203 groupId
.transform(GroupId
::serialize
),
207 public SendMessageResult
sendNullMessage(RecipientId recipientId
) {
208 return handleSendMessage(recipientId
, SignalServiceMessageSender
::sendNullMessage
);
211 public SendMessageResult
sendSelfMessage(
212 SignalServiceDataMessage
.Builder messageBuilder
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
);
219 var message
= messageBuilder
.build();
220 return sendSelfMessage(message
);
223 public SendMessageResult
sendSyncMessage(SignalServiceSyncMessage message
) {
224 var messageSender
= dependencies
.getMessageSender();
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
);
247 public SendMessageResult
sendTypingMessage(
248 SignalServiceTypingMessage message
, RecipientId recipientId
250 return handleSendMessage(recipientId
,
251 (messageSender
, address
, unidentifiedAccess
) -> messageSender
.sendTyping(address
,
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());
263 final var distributionId
= g
.getDistributionId();
264 final var recipientIds
= g
.getMembersWithout(account
.getSelfRecipientId());
266 return sendGroupTypingMessage(message
, recipientIds
, distributionId
);
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(
280 (distId
, recipients
, unidentifiedAccess
, isRecipientUpdate
) -> messageSender
.sendGroupTyping(distId
,
287 for (var r
: results
) {
288 handleSendMessageResult(r
);
294 private GroupInfo
getGroupForSending(GroupId groupId
) throws GroupNotFoundException
, NotAGroupMemberException
{
295 var g
= groupProvider
.getGroup(groupId
);
297 throw new GroupNotFoundException(groupId
);
299 if (!g
.isMember(account
.getSelfRecipientId())) {
300 throw new NotAGroupMemberException(groupId
, g
.getTitle());
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
315 : getSenderKeyCapableRecipientIds(recipientIds
);
316 final var allResults
= new ArrayList
<SendMessageResult
>(recipientIds
.size());
318 if (senderKeyTargets
.size() > 0) {
319 final var results
= sendGroupMessageInternalWithSenderKey(senderKeySender
,
324 if (results
== null) {
325 senderKeyTargets
= Set
.of();
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()))
332 if (failedTargets
.size() > 0) {
333 senderKeyTargets
= new HashSet
<>(senderKeyTargets
);
334 failedTargets
.forEach(senderKeyTargets
::remove
);
339 final var legacyTargets
= new HashSet
<>(recipientIds
);
340 legacyTargets
.removeAll(senderKeyTargets
);
341 final boolean onlyTargetIsSelfWithLinkedDevice
= recipientIds
.isEmpty() && account
.isMultiDevice();
343 if (legacyTargets
.size() > 0 || onlyTargetIsSelfWithLinkedDevice
) {
344 if (legacyTargets
.size() > 0) {
345 logger
.debug("Need to do {} legacy sends.", legacyTargets
.size());
347 logger
.debug("Need to do a legacy send to send a sync message for a group of only ourselves.");
350 final List
<SendMessageResult
> results
= sendGroupMessageInternalWithLegacy(legacySender
,
352 isRecipientUpdate
|| allResults
.size() > 0);
353 allResults
.addAll(results
);
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.");
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
)) {
375 final var access
= unidentifiedAccessHelper
.getAccessFor(recipientId
);
376 if (!access
.isPresent() || !access
.get().getTargetUnidentifiedAccess().isPresent()) {
380 final var identity
= account
.getIdentityKeyStore().getIdentity(recipientId
);
381 if (identity
== null || !identity
.getTrustLevel().isTrusted()) {
385 senderKeyTargets
.add(recipientId
);
388 if (senderKeyTargets
.size() < 2) {
389 logger
.debug("Too few sender-key-capable users ({}). Doing all legacy sends.", senderKeyTargets
.size());
393 logger
.debug("Can use sender key for {}/{} recipients.", senderKeyTargets
.size(), recipientIds
.size());
394 return senderKeyTargets
;
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
);
404 final var results
= sender
.send(addresses
, unidentifiedAccesses
, isRecipientUpdate
);
406 final var successCount
= results
.stream().filter(SendMessageResult
::isSuccess
).count();
407 logger
.debug("Successfully sent using 1:1 to {}/{} legacy targets.", successCount
, recipientIdList
.size());
409 } catch (org
.whispersystems
.signalservice
.api
.crypto
.UntrustedIdentityException e
) {
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
);
422 long keyCreateTime
= account
.getSenderKeyStore()
423 .getCreateTimeForOurKey(account
.getSelfRecipientId(), account
.getDeviceId(), distributionId
);
424 long keyAge
= System
.currentTimeMillis() - keyCreateTime
;
426 if (keyCreateTime
!= -1 && keyAge
> TimeUnit
.DAYS
.toMillis(14)) {
427 logger
.debug("DistributionId {} was created at {} and is {} ms old (~{} days). Rotating.",
431 TimeUnit
.MILLISECONDS
.toDays(keyAge
));
432 account
.getSenderKeyStore().deleteOurKey(account
.getSelfRecipientId(), distributionId
);
435 List
<SignalServiceAddress
> addresses
= recipientIdList
.stream()
436 .map(addressResolver
::resolveSignalServiceAddress
)
437 .collect(Collectors
.toList());
438 List
<UnidentifiedAccess
> unidentifiedAccesses
= unidentifiedAccessHelper
.getAccessFor(recipientIdList
)
441 .map(UnidentifiedAccessPair
::getTargetUnidentifiedAccess
)
443 .collect(Collectors
.toList());
446 List
<SendMessageResult
> results
= sender
.send(distributionId
,
448 unidentifiedAccesses
,
451 final var successCount
= results
.stream().filter(SendMessageResult
::isSuccess
).count();
452 logger
.debug("Successfully sent using sender key to {}/{} sender key targets.",
457 } catch (org
.whispersystems
.signalservice
.api
.crypto
.UntrustedIdentityException e
) {
459 } catch (InvalidUnidentifiedAccessHeaderException e
) {
460 logger
.warn("Someone had a bad UD header. Falling back to legacy sends.", e
);
462 } catch (NoSessionException e
) {
463 logger
.warn("No session. Falling back to legacy sends.", e
);
464 account
.getSenderKeyStore().deleteOurKey(account
.getSelfRecipientId(), distributionId
);
466 } catch (InvalidKeyException e
) {
467 logger
.warn("Invalid key. Falling back to legacy sends.", e
);
468 account
.getSenderKeyStore().deleteOurKey(account
.getSelfRecipientId(), distributionId
);
470 } catch (InvalidRegistrationIdException e
) {
471 logger
.warn("Invalid registrationId. Falling back to legacy sends.", e
);
473 } catch (NotFoundException e
) {
474 logger
.warn("Someone was unregistered. Falling back to legacy sends.", e
);
479 private SendMessageResult
sendMessage(
480 SignalServiceDataMessage message
, RecipientId recipientId
482 return handleSendMessage(recipientId
,
483 (messageSender
, address
, unidentifiedAccess
) -> messageSender
.sendDataMessage(address
,
487 SignalServiceMessageSender
.IndividualSendEvents
.EMPTY
));
490 private SendMessageResult
handleSendMessage(RecipientId recipientId
, SenderHandler s
) {
491 var messageSender
= dependencies
.getMessageSender();
493 var address
= addressResolver
.resolveSignalServiceAddress(recipientId
);
496 return s
.send(messageSender
, address
, unidentifiedAccessHelper
.getAccessFor(recipientId
));
497 } catch (UnregisteredUserException e
) {
498 final RecipientId newRecipientId
;
500 newRecipientId
= recipientRegistrationRefresher
.refreshRecipientRegistration(recipientId
);
501 } catch (UnregisteredRecipientException ex
) {
502 return SendMessageResult
.unregisteredFailure(address
);
504 address
= addressResolver
.resolveSignalServiceAddress(newRecipientId
);
505 return s
.send(messageSender
, address
, unidentifiedAccessHelper
.getAccessFor(newRecipientId
));
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
);
522 private SendMessageResult
sendSelfMessage(SignalServiceDataMessage message
) {
523 var address
= account
.getSelfAddress();
524 var transcript
= new SentTranscriptMessage(Optional
.of(address
),
525 message
.getTimestamp(),
527 message
.getExpiresInSeconds(),
528 Map
.of(address
, true),
530 var syncMessage
= SignalServiceSyncMessage
.forSentTranscript(transcript
);
532 return sendSyncMessage(syncMessage
);
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
543 account
.getRecipientStore()
544 .storeProfile(recipientId
,
545 Profile
.newBuilder(profile
)
546 .withUnidentifiedAccessMode(Profile
.UnidentifiedAccessMode
.UNKNOWN
)
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
557 account
.getRecipientStore()
558 .storeProfile(recipientId
,
559 Profile
.newBuilder(profile
)
560 .withUnidentifiedAccessMode(Profile
.UnidentifiedAccessMode
.UNKNOWN
)
564 if (r
.getIdentityFailure() != null) {
565 final var recipientId
= recipientResolver
.resolveRecipient(r
.getAddress());
566 identityFailureHandler
.handleIdentityFailure(recipientId
, r
.getIdentityFailure());
570 interface SenderHandler
{
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
;
579 interface SenderKeySenderHandler
{
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
;
589 interface LegacySenderHandler
{
591 List
<SendMessageResult
> send(
592 List
<SignalServiceAddress
> recipients
,
593 List
<Optional
<UnidentifiedAccessPair
>> unidentifiedAccess
,
594 boolean isRecipientUpdate
595 ) throws IOException
, UntrustedIdentityException
;