1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.Manager
;
4 import org
.asamk
.signal
.manager
.actions
.HandleAction
;
5 import org
.asamk
.signal
.manager
.actions
.RefreshPreKeysAction
;
6 import org
.asamk
.signal
.manager
.actions
.RenewSessionAction
;
7 import org
.asamk
.signal
.manager
.actions
.ResendMessageAction
;
8 import org
.asamk
.signal
.manager
.actions
.RetrieveProfileAction
;
9 import org
.asamk
.signal
.manager
.actions
.SendGroupInfoAction
;
10 import org
.asamk
.signal
.manager
.actions
.SendGroupInfoRequestAction
;
11 import org
.asamk
.signal
.manager
.actions
.SendProfileKeyAction
;
12 import org
.asamk
.signal
.manager
.actions
.SendReceiptAction
;
13 import org
.asamk
.signal
.manager
.actions
.SendRetryMessageRequestAction
;
14 import org
.asamk
.signal
.manager
.actions
.SendSyncBlockedListAction
;
15 import org
.asamk
.signal
.manager
.actions
.SendSyncConfigurationAction
;
16 import org
.asamk
.signal
.manager
.actions
.SendSyncContactsAction
;
17 import org
.asamk
.signal
.manager
.actions
.SendSyncGroupsAction
;
18 import org
.asamk
.signal
.manager
.actions
.SendSyncKeysAction
;
19 import org
.asamk
.signal
.manager
.actions
.SyncStorageDataAction
;
20 import org
.asamk
.signal
.manager
.actions
.UpdateAccountAttributesAction
;
21 import org
.asamk
.signal
.manager
.api
.GroupId
;
22 import org
.asamk
.signal
.manager
.api
.GroupNotFoundException
;
23 import org
.asamk
.signal
.manager
.api
.MessageEnvelope
;
24 import org
.asamk
.signal
.manager
.api
.Pair
;
25 import org
.asamk
.signal
.manager
.api
.ReceiveConfig
;
26 import org
.asamk
.signal
.manager
.api
.StickerPackId
;
27 import org
.asamk
.signal
.manager
.api
.TrustLevel
;
28 import org
.asamk
.signal
.manager
.api
.UntrustedIdentityException
;
29 import org
.asamk
.signal
.manager
.groups
.GroupUtils
;
30 import org
.asamk
.signal
.manager
.internal
.SignalDependencies
;
31 import org
.asamk
.signal
.manager
.jobs
.RetrieveStickerPackJob
;
32 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
33 import org
.asamk
.signal
.manager
.storage
.groups
.GroupInfoV1
;
34 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientId
;
35 import org
.asamk
.signal
.manager
.storage
.stickers
.StickerPack
;
36 import org
.signal
.libsignal
.metadata
.ProtocolInvalidKeyException
;
37 import org
.signal
.libsignal
.metadata
.ProtocolInvalidKeyIdException
;
38 import org
.signal
.libsignal
.metadata
.ProtocolInvalidMessageException
;
39 import org
.signal
.libsignal
.metadata
.ProtocolNoSessionException
;
40 import org
.signal
.libsignal
.metadata
.ProtocolUntrustedIdentityException
;
41 import org
.signal
.libsignal
.metadata
.SelfSendException
;
42 import org
.signal
.libsignal
.protocol
.InvalidMessageException
;
43 import org
.signal
.libsignal
.protocol
.groups
.GroupSessionBuilder
;
44 import org
.signal
.libsignal
.protocol
.message
.DecryptionErrorMessage
;
45 import org
.signal
.libsignal
.zkgroup
.InvalidInputException
;
46 import org
.signal
.libsignal
.zkgroup
.profiles
.ProfileKey
;
47 import org
.slf4j
.Logger
;
48 import org
.slf4j
.LoggerFactory
;
49 import org
.whispersystems
.signalservice
.api
.InvalidMessageStructureException
;
50 import org
.whispersystems
.signalservice
.api
.crypto
.SignalGroupSessionBuilder
;
51 import org
.whispersystems
.signalservice
.api
.crypto
.SignalServiceCipherResult
;
52 import org
.whispersystems
.signalservice
.api
.messages
.EnvelopeContentValidator
;
53 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceContent
;
54 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceDataMessage
;
55 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
56 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroup
;
57 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupContext
;
58 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupV2
;
59 import org
.whispersystems
.signalservice
.api
.messages
.SignalServicePniSignatureMessage
;
60 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceReceiptMessage
;
61 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceStoryMessage
;
62 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SignalServiceSyncMessage
;
63 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.StickerPackOperationMessage
;
64 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
;
65 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
.ACI
;
66 import org
.whispersystems
.signalservice
.api
.push
.ServiceIdType
;
67 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
68 import org
.whispersystems
.signalservice
.internal
.push
.Envelope
;
69 import org
.whispersystems
.signalservice
.internal
.push
.UnsupportedDataMessageException
;
71 import java
.util
.ArrayList
;
72 import java
.util
.List
;
73 import java
.util
.Optional
;
74 import java
.util
.stream
.Collectors
;
76 public final class IncomingMessageHandler
{
78 private static final Logger logger
= LoggerFactory
.getLogger(IncomingMessageHandler
.class);
80 private final SignalAccount account
;
81 private final SignalDependencies dependencies
;
82 private final Context context
;
84 public IncomingMessageHandler(final Context context
) {
85 this.account
= context
.getAccount();
86 this.dependencies
= context
.getDependencies();
87 this.context
= context
;
90 public Pair
<List
<HandleAction
>, Exception
> handleRetryEnvelope(
91 final SignalServiceEnvelope envelope
,
92 final ReceiveConfig receiveConfig
,
93 final Manager
.ReceiveMessageHandler handler
95 final List
<HandleAction
> actions
= new ArrayList
<>();
96 if (envelope
.isPreKeySignalMessage()) {
97 actions
.add(RefreshPreKeysAction
.create());
100 SignalServiceContent content
= null;
101 if (!envelope
.isReceipt()) {
102 account
.getIdentityKeyStore().setRetryingDecryption(true);
103 final var destination
= getDestination(envelope
).serviceId();
105 final var cipherResult
= dependencies
.getCipher(destination
== null
106 || destination
.equals(account
.getAci()) ? ServiceIdType
.ACI
: ServiceIdType
.PNI
)
107 .decrypt(envelope
.getProto(), envelope
.getServerDeliveredTimestamp());
108 content
= validate(envelope
.getProto(), cipherResult
, envelope
.getServerDeliveredTimestamp());
109 if (content
== null) {
110 return new Pair
<>(List
.of(), null);
112 } catch (ProtocolUntrustedIdentityException e
) {
113 final var recipientId
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
114 final var exception
= new UntrustedIdentityException(account
.getRecipientAddressResolver()
115 .resolveRecipientAddress(recipientId
)
116 .toApiRecipientAddress(), e
.getSenderDevice());
117 return new Pair
<>(List
.of(), exception
);
118 } catch (Exception e
) {
119 return new Pair
<>(List
.of(), e
);
121 account
.getIdentityKeyStore().setRetryingDecryption(false);
124 actions
.addAll(checkAndHandleMessage(envelope
, content
, receiveConfig
, handler
, null));
125 return new Pair
<>(actions
, null);
128 public Pair
<List
<HandleAction
>, Exception
> handleEnvelope(
129 final SignalServiceEnvelope envelope
,
130 final ReceiveConfig receiveConfig
,
131 final Manager
.ReceiveMessageHandler handler
133 final var actions
= new ArrayList
<HandleAction
>();
134 SignalServiceContent content
= null;
135 Exception exception
= null;
136 envelope
.getSourceServiceId().map(ServiceId
::parseOrNull
)
137 // Store uuid if we don't have it already
138 // uuid in envelope is sent by server
139 .ifPresent(serviceId
-> account
.getRecipientResolver().resolveRecipient(serviceId
));
140 if (!envelope
.isReceipt()) {
141 final var destination
= getDestination(envelope
).serviceId();
143 final var cipherResult
= dependencies
.getCipher(destination
== null
144 || destination
.equals(account
.getAci()) ? ServiceIdType
.ACI
: ServiceIdType
.PNI
)
145 .decrypt(envelope
.getProto(), envelope
.getServerDeliveredTimestamp());
146 content
= validate(envelope
.getProto(), cipherResult
, envelope
.getServerDeliveredTimestamp());
147 if (content
== null) {
148 return new Pair
<>(List
.of(), null);
150 } catch (ProtocolUntrustedIdentityException e
) {
151 final var recipientId
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
152 actions
.add(new RetrieveProfileAction(recipientId
));
153 exception
= new UntrustedIdentityException(account
.getRecipientAddressResolver()
154 .resolveRecipientAddress(recipientId
)
155 .toApiRecipientAddress(), e
.getSenderDevice());
156 } catch (ProtocolInvalidKeyIdException
| ProtocolInvalidKeyException
| ProtocolNoSessionException
|
157 ProtocolInvalidMessageException e
) {
158 logger
.debug("Failed to decrypt incoming message", e
);
159 final var sender
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
160 if (context
.getContactHelper().isContactBlocked(sender
)) {
161 logger
.debug("Received invalid message from blocked contact, ignoring.");
163 var serviceId
= ServiceId
.parseOrNull(e
.getSender());
164 if (serviceId
!= null) {
165 final var isSelf
= sender
.equals(account
.getSelfRecipientId())
166 && e
.getSenderDevice() == account
.getDeviceId();
168 logger
.debug("Received invalid message, requesting message resend.");
169 actions
.add(new SendRetryMessageRequestAction(sender
, serviceId
, e
, envelope
, destination
));
171 logger
.debug("Received invalid message, queuing renew session action.");
172 actions
.add(new RenewSessionAction(sender
, serviceId
, destination
));
175 logger
.debug("Received invalid message from invalid sender: {}", e
.getSender());
179 } catch (SelfSendException e
) {
180 logger
.debug("Dropping unidentified message from self.");
181 return new Pair
<>(List
.of(), null);
182 } catch (Exception e
) {
183 logger
.debug("Failed to handle incoming message", e
);
188 actions
.addAll(checkAndHandleMessage(envelope
, content
, receiveConfig
, handler
, exception
));
189 return new Pair
<>(actions
, exception
);
192 private SignalServiceContent
validate(
193 Envelope envelope
, SignalServiceCipherResult cipherResult
, long serverDeliveredTimestamp
194 ) throws ProtocolInvalidKeyException
, ProtocolInvalidMessageException
, UnsupportedDataMessageException
, InvalidMessageStructureException
{
195 final var content
= cipherResult
.getContent();
196 final var envelopeMetadata
= cipherResult
.getMetadata();
197 final var validationResult
= EnvelopeContentValidator
.INSTANCE
.validate(envelope
, content
, account
.getAci());
199 if (validationResult
instanceof EnvelopeContentValidator
.Result
.Invalid v
) {
200 logger
.warn("Invalid content! {}", v
.getReason(), v
.getThrowable());
204 if (validationResult
instanceof EnvelopeContentValidator
.Result
.UnsupportedDataMessage v
) {
205 logger
.warn("Unsupported DataMessage! Our version: {}, their version: {}",
207 v
.getTheirVersion());
211 return SignalServiceContent
.Companion
.createFrom(account
.getNumber(),
215 serverDeliveredTimestamp
);
218 private List
<HandleAction
> checkAndHandleMessage(
219 final SignalServiceEnvelope envelope
,
220 final SignalServiceContent content
,
221 final ReceiveConfig receiveConfig
,
222 final Manager
.ReceiveMessageHandler handler
,
223 final Exception exception
225 if (content
!= null) {
226 // Store uuid if we don't have it already
227 // address/uuid is validated by unidentified sender certificate
229 boolean handledPniSignature
= false;
230 if (content
.getPniSignatureMessage().isPresent()) {
231 final var message
= content
.getPniSignatureMessage().get();
232 final var senderAddress
= getSenderAddress(envelope
, content
);
233 if (senderAddress
!= null) {
234 handledPniSignature
= handlePniSignatureMessage(message
, senderAddress
);
237 if (!handledPniSignature
) {
238 account
.getRecipientTrustedResolver().resolveRecipientTrusted(content
.getSender());
241 if (envelope
.isReceipt()) {
242 final var senderDeviceAddress
= getSender(envelope
, content
);
243 final var sender
= senderDeviceAddress
.serviceId();
244 final var senderDeviceId
= senderDeviceAddress
.deviceId();
245 account
.getMessageSendLogStore().deleteEntryForRecipient(envelope
.getTimestamp(), sender
, senderDeviceId
);
248 var notAllowedToSendToGroup
= isNotAllowedToSendToGroup(envelope
, content
);
249 final var groupContext
= getGroupContext(content
);
250 if (groupContext
!= null && groupContext
.getGroupV2().isPresent()) {
251 handleGroupV2Context(groupContext
.getGroupV2().get());
253 // Check again in case the user just joined the group
254 notAllowedToSendToGroup
= notAllowedToSendToGroup
&& isNotAllowedToSendToGroup(envelope
, content
);
256 if (isMessageBlocked(envelope
, content
)) {
257 logger
.info("Ignoring a message from blocked user/group: {}", envelope
.getTimestamp());
259 } else if (notAllowedToSendToGroup
) {
260 final var senderAddress
= getSenderAddress(envelope
, content
);
261 logger
.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
262 senderAddress
== null ?
null : senderAddress
.getIdentifier(),
263 envelope
.getTimestamp());
266 List
<HandleAction
> actions
;
267 if (content
!= null) {
268 actions
= handleMessage(envelope
, content
, receiveConfig
);
272 handler
.handleMessage(MessageEnvelope
.from(envelope
,
274 account
.getRecipientResolver(),
275 account
.getRecipientAddressResolver(),
276 context
.getAttachmentHelper()::getAttachmentFile
,
277 exception
), exception
);
282 public List
<HandleAction
> handleMessage(
283 SignalServiceEnvelope envelope
, SignalServiceContent content
, ReceiveConfig receiveConfig
285 var actions
= new ArrayList
<HandleAction
>();
286 final var senderDeviceAddress
= getSender(envelope
, content
);
287 final var sender
= senderDeviceAddress
.recipientId();
288 final var senderServiceId
= senderDeviceAddress
.serviceId();
289 final var senderDeviceId
= senderDeviceAddress
.deviceId();
290 final var destination
= getDestination(envelope
);
292 if (account
.getPni().equals(destination
.serviceId
)) {
293 account
.getRecipientStore().markNeedsPniSignature(destination
.recipientId
, true);
294 } else if (account
.getAci().equals(destination
.serviceId
)) {
295 account
.getRecipientStore().markNeedsPniSignature(destination
.recipientId
, false);
298 if (content
.getReceiptMessage().isPresent()) {
299 final var message
= content
.getReceiptMessage().get();
300 if (message
.isDeliveryReceipt()) {
301 account
.getMessageSendLogStore()
302 .deleteEntriesForRecipient(message
.getTimestamps(), senderServiceId
, senderDeviceId
);
306 if (content
.getSenderKeyDistributionMessage().isPresent()) {
307 final var message
= content
.getSenderKeyDistributionMessage().get();
308 final var protocolAddress
= senderServiceId
.toProtocolAddress(senderDeviceId
);
309 logger
.debug("Received a sender key distribution message for distributionId {} from {}",
310 message
.getDistributionId(),
312 new SignalGroupSessionBuilder(dependencies
.getSessionLock(),
313 new GroupSessionBuilder(account
.getSenderKeyStore())).process(protocolAddress
, message
);
316 if (content
.getDecryptionErrorMessage().isPresent()) {
317 var message
= content
.getDecryptionErrorMessage().get();
318 logger
.debug("Received a decryption error message from {}.{} (resend request for {})",
321 message
.getTimestamp());
322 if (message
.getDeviceId() == account
.getDeviceId()) {
323 handleDecryptionErrorMessage(actions
,
328 destination
.serviceId());
330 logger
.debug("Request is for another one of our devices");
334 if (content
.getDataMessage().isPresent() || content
.getEditMessage().isPresent()) {
335 var message
= content
.getDataMessage().isPresent()
336 ? content
.getDataMessage().get()
337 : content
.getEditMessage().get().getDataMessage();
339 if (content
.isNeedsReceipt()) {
340 actions
.add(new SendReceiptAction(sender
,
341 SignalServiceReceiptMessage
.Type
.DELIVERY
,
342 message
.getTimestamp()));
344 // Message wasn't sent as unidentified sender message
345 final var contact
= context
.getAccount().getContactStore().getContact(sender
);
346 if (account
.isPrimaryDevice()
348 && !contact
.isBlocked()
349 && contact
.isProfileSharingEnabled()) {
350 actions
.add(UpdateAccountAttributesAction
.create());
351 actions
.add(new SendProfileKeyAction(sender
));
354 if (receiveConfig
.sendReadReceipts()) {
355 actions
.add(new SendReceiptAction(sender
,
356 SignalServiceReceiptMessage
.Type
.READ
,
357 message
.getTimestamp()));
360 actions
.addAll(handleSignalServiceDataMessage(message
,
364 receiveConfig
.ignoreAttachments()));
367 if (content
.getStoryMessage().isPresent()) {
368 final var message
= content
.getStoryMessage().get();
369 actions
.addAll(handleSignalServiceStoryMessage(message
, sender
, receiveConfig
.ignoreAttachments()));
372 if (content
.getSyncMessage().isPresent()) {
373 var syncMessage
= content
.getSyncMessage().get();
374 actions
.addAll(handleSyncMessage(envelope
,
377 receiveConfig
.ignoreAttachments()));
383 private boolean handlePniSignatureMessage(
384 final SignalServicePniSignatureMessage message
, final SignalServiceAddress senderAddress
386 final var aci
= senderAddress
.getServiceId();
387 final var aciIdentity
= account
.getIdentityKeyStore().getIdentityInfo(aci
);
388 final var pni
= message
.getPni();
389 final var pniIdentity
= account
.getIdentityKeyStore().getIdentityInfo(pni
);
391 if (aciIdentity
== null || pniIdentity
== null || aci
.equals(pni
)) {
395 final var verified
= pniIdentity
.getIdentityKey()
396 .verifyAlternateIdentity(aciIdentity
.getIdentityKey(), message
.getSignature());
399 logger
.debug("Invalid PNI signature of ACI {} with PNI {}", aci
, pni
);
403 logger
.debug("Verified association of ACI {} with PNI {}", aci
, pni
);
404 account
.getRecipientTrustedResolver()
405 .resolveRecipientTrusted(Optional
.of(ACI
.from(aci
.getRawUuid())),
407 senderAddress
.getNumber());
411 private void handleDecryptionErrorMessage(
412 final List
<HandleAction
> actions
,
413 final RecipientId sender
,
414 final ServiceId senderServiceId
,
415 final int senderDeviceId
,
416 final DecryptionErrorMessage message
,
417 final ServiceId destination
419 final var logEntries
= account
.getMessageSendLogStore()
420 .findMessages(senderServiceId
,
422 message
.getTimestamp(),
423 message
.getRatchetKey().isEmpty());
425 for (final var logEntry
: logEntries
) {
426 actions
.add(new ResendMessageAction(sender
, message
.getTimestamp(), logEntry
));
429 if (message
.getRatchetKey().isPresent()) {
430 final var sessionStore
= account
.getAccountData(destination
).getSessionStore();
431 if (sessionStore
.isCurrentRatchetKey(senderServiceId
, senderDeviceId
, message
.getRatchetKey().get())) {
432 if (logEntries
.isEmpty()) {
433 logger
.debug("Renewing the session with sender");
434 actions
.add(new RenewSessionAction(sender
, senderServiceId
, destination
));
436 logger
.trace("Archiving the session with sender, a resend message has already been queued");
437 sessionStore
.archiveSessions(senderServiceId
);
444 for (final var logEntry
: logEntries
) {
445 if (logEntry
.groupId().isEmpty()) {
448 final var group
= account
.getGroupStore().getGroup(logEntry
.groupId().get());
453 logger
.trace("Deleting shared sender key with {} ({}): {}",
456 group
.getDistributionId());
457 account
.getSenderKeyStore().deleteSharedWith(senderServiceId
, senderDeviceId
, group
.getDistributionId());
460 logger
.debug("Reset all shared sender keys with this recipient, no related message found in send log");
461 account
.getSenderKeyStore().deleteSharedWith(senderServiceId
);
465 private List
<HandleAction
> handleSyncMessage(
466 final SignalServiceEnvelope envelope
,
467 final SignalServiceSyncMessage syncMessage
,
468 final DeviceAddress sender
,
469 final boolean ignoreAttachments
471 var actions
= new ArrayList
<HandleAction
>();
472 account
.setMultiDevice(true);
473 if (syncMessage
.getSent().isPresent()) {
474 var message
= syncMessage
.getSent().get();
475 final var destination
= message
.getDestination().orElse(null);
476 if (message
.getDataMessage().isPresent()) {
477 actions
.addAll(handleSignalServiceDataMessage(message
.getDataMessage().get(),
482 : new DeviceAddress(account
.getRecipientResolver().resolveRecipient(destination
),
483 destination
.getServiceId(),
487 if (message
.getStoryMessage().isPresent()) {
488 actions
.addAll(handleSignalServiceStoryMessage(message
.getStoryMessage().get(),
489 sender
.recipientId(),
493 if (syncMessage
.getRequest().isPresent() && account
.isPrimaryDevice()) {
494 var rm
= syncMessage
.getRequest().get();
495 if (rm
.isContactsRequest()) {
496 actions
.add(SendSyncContactsAction
.create());
498 if (rm
.isGroupsRequest()) {
499 actions
.add(SendSyncGroupsAction
.create());
501 if (rm
.isBlockedListRequest()) {
502 actions
.add(SendSyncBlockedListAction
.create());
504 if (rm
.isKeysRequest()) {
505 actions
.add(SendSyncKeysAction
.create());
507 if (rm
.isConfigurationRequest()) {
508 actions
.add(SendSyncConfigurationAction
.create());
510 actions
.add(SyncStorageDataAction
.create());
512 if (syncMessage
.getGroups().isPresent()) {
514 final var groupsMessage
= syncMessage
.getGroups().get();
515 context
.getAttachmentHelper()
516 .retrieveAttachment(groupsMessage
, context
.getSyncHelper()::handleSyncDeviceGroups
);
517 } catch (Exception e
) {
518 logger
.warn("Failed to handle received sync groups, ignoring: {}", e
.getMessage());
521 if (syncMessage
.getBlockedList().isPresent()) {
522 final var blockedListMessage
= syncMessage
.getBlockedList().get();
523 for (var address
: blockedListMessage
.getAddresses()) {
524 context
.getContactHelper()
525 .setContactBlocked(account
.getRecipientResolver().resolveRecipient(address
), true);
527 for (var groupId
: blockedListMessage
.getGroupIds()
529 .map(GroupId
::unknownVersion
)
530 .collect(Collectors
.toSet())) {
532 context
.getGroupHelper().setGroupBlocked(groupId
, true);
533 } catch (GroupNotFoundException e
) {
534 logger
.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
539 if (syncMessage
.getContacts().isPresent()) {
541 final var contactsMessage
= syncMessage
.getContacts().get();
542 context
.getAttachmentHelper()
543 .retrieveAttachment(contactsMessage
.getContactsStream(),
544 context
.getSyncHelper()::handleSyncDeviceContacts
);
545 } catch (Exception e
) {
546 logger
.warn("Failed to handle received sync contacts, ignoring: {}", e
.getMessage());
549 if (syncMessage
.getVerified().isPresent()) {
550 final var verifiedMessage
= syncMessage
.getVerified().get();
551 account
.getIdentityKeyStore()
552 .setIdentityTrustLevel(verifiedMessage
.getDestination().getServiceId(),
553 verifiedMessage
.getIdentityKey(),
554 TrustLevel
.fromVerifiedState(verifiedMessage
.getVerified()));
556 if (syncMessage
.getStickerPackOperations().isPresent()) {
557 final var stickerPackOperationMessages
= syncMessage
.getStickerPackOperations().get();
558 for (var m
: stickerPackOperationMessages
) {
559 if (m
.getPackId().isEmpty()) {
562 final var stickerPackId
= StickerPackId
.deserialize(m
.getPackId().get());
563 final var stickerPackKey
= m
.getPackKey().orElse(null);
564 final var installed
= m
.getType().isEmpty()
565 || m
.getType().get() == StickerPackOperationMessage
.Type
.INSTALL
;
567 final var sticker
= context
.getStickerHelper()
568 .addOrUpdateStickerPack(stickerPackId
, stickerPackKey
, installed
);
570 if (sticker
!= null && installed
) {
571 context
.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId
, sticker
.packKey()));
575 if (syncMessage
.getFetchType().isPresent()) {
576 switch (syncMessage
.getFetchType().get()) {
577 case LOCAL_PROFILE
-> actions
.add(new RetrieveProfileAction(account
.getSelfRecipientId()));
578 case STORAGE_MANIFEST
-> actions
.add(SyncStorageDataAction
.create());
581 if (syncMessage
.getKeys().isPresent()) {
582 final var keysMessage
= syncMessage
.getKeys().get();
583 if (keysMessage
.getStorageService().isPresent()) {
584 final var storageKey
= keysMessage
.getStorageService().get();
585 account
.setStorageKey(storageKey
);
586 actions
.add(SyncStorageDataAction
.create());
588 if (keysMessage
.getMaster().isPresent()) {
589 final var masterKey
= keysMessage
.getMaster().get();
590 account
.setMasterKey(masterKey
);
591 actions
.add(SyncStorageDataAction
.create());
594 if (syncMessage
.getConfiguration().isPresent()) {
595 final var configurationMessage
= syncMessage
.getConfiguration().get();
596 final var configurationStore
= account
.getConfigurationStore();
597 if (configurationMessage
.getReadReceipts().isPresent()) {
598 configurationStore
.setReadReceipts(configurationMessage
.getReadReceipts().get());
600 if (configurationMessage
.getLinkPreviews().isPresent()) {
601 configurationStore
.setLinkPreviews(configurationMessage
.getLinkPreviews().get());
603 if (configurationMessage
.getTypingIndicators().isPresent()) {
604 configurationStore
.setTypingIndicators(configurationMessage
.getTypingIndicators().get());
606 if (configurationMessage
.getUnidentifiedDeliveryIndicators().isPresent()) {
607 configurationStore
.setUnidentifiedDeliveryIndicators(configurationMessage
.getUnidentifiedDeliveryIndicators()
611 if (syncMessage
.getPniChangeNumber().isPresent()) {
612 final var pniChangeNumber
= syncMessage
.getPniChangeNumber().get();
613 logger
.debug("Received PNI change number sync message, applying.");
614 final var updatedPniString
= envelope
.getUpdatedPni();
615 if (updatedPniString
!= null && !updatedPniString
.isEmpty()) {
616 final var updatedPni
= ServiceId
.PNI
.parseOrThrow(updatedPniString
);
617 context
.getAccountHelper().handlePniChangeNumberMessage(pniChangeNumber
, updatedPni
);
623 private SignalServiceGroupContext
getGroupContext(SignalServiceContent content
) {
624 if (content
== null) {
628 if (content
.getDataMessage().isPresent()) {
629 var message
= content
.getDataMessage().get();
630 if (message
.getGroupContext().isPresent()) {
631 return message
.getGroupContext().get();
635 if (content
.getStoryMessage().isPresent()) {
636 var message
= content
.getStoryMessage().get();
637 if (message
.getGroupContext().isPresent()) {
639 return SignalServiceGroupContext
.create(null, message
.getGroupContext().get());
640 } catch (InvalidMessageException e
) {
641 throw new AssertionError(e
);
649 private boolean isMessageBlocked(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
650 SignalServiceAddress source
= getSenderAddress(envelope
, content
);
651 if (source
== null) {
654 final var recipientId
= account
.getRecipientResolver().resolveRecipient(source
);
655 if (context
.getContactHelper().isContactBlocked(recipientId
)) {
659 final var groupContext
= getGroupContext(content
);
660 if (groupContext
!= null) {
661 var groupId
= GroupUtils
.getGroupId(groupContext
);
662 return context
.getGroupHelper().isGroupBlocked(groupId
);
668 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
669 SignalServiceAddress source
= getSenderAddress(envelope
, content
);
670 if (source
== null) {
674 final var groupContext
= getGroupContext(content
);
675 if (groupContext
== null) {
679 if (groupContext
.getGroupV1().isPresent()) {
680 var groupInfo
= groupContext
.getGroupV1().get();
681 if (groupInfo
.getType() == SignalServiceGroup
.Type
.QUIT
) {
686 var groupId
= GroupUtils
.getGroupId(groupContext
);
687 var group
= context
.getGroupHelper().getGroup(groupId
);
692 final var message
= content
.getDataMessage().orElse(null);
694 final var recipientId
= account
.getRecipientResolver().resolveRecipient(source
);
695 if (!group
.isMember(recipientId
) && !(
696 group
.isPendingMember(recipientId
) && message
!= null && message
.isGroupV2Update()
701 if (group
.isAnnouncementGroup() && !group
.isAdmin(recipientId
)) {
702 return message
== null
703 || message
.getBody().isPresent()
704 || message
.getAttachments().isPresent()
705 || message
.getQuote().isPresent()
706 || message
.getPreviews().isPresent()
707 || message
.getMentions().isPresent()
708 || message
.getSticker().isPresent();
713 private List
<HandleAction
> handleSignalServiceDataMessage(
714 SignalServiceDataMessage message
,
716 DeviceAddress source
,
717 DeviceAddress destination
,
718 boolean ignoreAttachments
720 var actions
= new ArrayList
<HandleAction
>();
721 if (message
.getGroupContext().isPresent()) {
722 final var groupContext
= message
.getGroupContext().get();
723 if (groupContext
.getGroupV1().isPresent()) {
724 var groupInfo
= groupContext
.getGroupV1().get();
725 var groupId
= GroupId
.v1(groupInfo
.getGroupId());
726 var group
= context
.getGroupHelper().getGroup(groupId
);
727 if (group
== null || group
instanceof GroupInfoV1
) {
728 var groupV1
= (GroupInfoV1
) group
;
729 switch (groupInfo
.getType()) {
731 if (groupV1
== null) {
732 groupV1
= new GroupInfoV1(groupId
);
735 if (groupInfo
.getAvatar().isPresent()) {
736 var avatar
= groupInfo
.getAvatar().get();
737 context
.getGroupHelper().downloadGroupAvatar(groupV1
.getGroupId(), avatar
);
740 if (groupInfo
.getName().isPresent()) {
741 groupV1
.name
= groupInfo
.getName().get();
744 if (groupInfo
.getMembers().isPresent()) {
745 final var recipientResolver
= account
.getRecipientResolver();
746 groupV1
.addMembers(groupInfo
.getMembers()
749 .map(recipientResolver
::resolveRecipient
)
750 .collect(Collectors
.toSet()));
753 account
.getGroupStore().updateGroup(groupV1
);
756 if (groupV1
== null && !isSync
) {
757 actions
.add(new SendGroupInfoRequestAction(source
.recipientId(), groupId
));
761 if (groupV1
!= null) {
762 groupV1
.removeMember(source
.recipientId());
763 account
.getGroupStore().updateGroup(groupV1
);
766 case REQUEST_INFO
-> {
767 if (groupV1
!= null && !isSync
) {
768 actions
.add(new SendGroupInfoAction(source
.recipientId(), groupV1
.getGroupId()));
773 // Received a group v1 message for a v2 group
776 if (groupContext
.getGroupV2().isPresent()) {
777 handleGroupV2Context(groupContext
.getGroupV2().get());
781 final var selfAddress
= isSync ? source
: destination
;
782 final var conversationPartnerAddress
= isSync ? destination
: source
;
783 if (conversationPartnerAddress
!= null && message
.isEndSession()) {
784 account
.getAccountData(selfAddress
.serviceId())
786 .deleteAllSessions(conversationPartnerAddress
.serviceId());
788 if (message
.isExpirationUpdate() || message
.getBody().isPresent()) {
789 if (message
.getGroupContext().isPresent()) {
790 final var groupContext
= message
.getGroupContext().get();
791 if (groupContext
.getGroupV1().isPresent()) {
792 var groupInfo
= groupContext
.getGroupV1().get();
793 var group
= account
.getGroupStore().getOrCreateGroupV1(GroupId
.v1(groupInfo
.getGroupId()));
795 if (group
.messageExpirationTime
!= message
.getExpiresInSeconds()) {
796 group
.messageExpirationTime
= message
.getExpiresInSeconds();
797 account
.getGroupStore().updateGroup(group
);
800 } else if (groupContext
.getGroupV2().isPresent()) {
801 // disappearing message timer already stored in the DecryptedGroup
803 } else if (conversationPartnerAddress
!= null) {
804 context
.getContactHelper()
805 .setExpirationTimer(conversationPartnerAddress
.recipientId(),
806 message
.getExpiresInSeconds(),
807 message
.getExpireTimerVersion());
810 if (!ignoreAttachments
) {
811 if (message
.getAttachments().isPresent()) {
812 for (var attachment
: message
.getAttachments().get()) {
813 context
.getAttachmentHelper().downloadAttachment(attachment
);
816 if (message
.getSharedContacts().isPresent()) {
817 for (var contact
: message
.getSharedContacts().get()) {
818 if (contact
.getAvatar().isPresent()) {
819 context
.getAttachmentHelper().downloadAttachment(contact
.getAvatar().get().getAttachment());
823 if (message
.getPreviews().isPresent()) {
824 final var previews
= message
.getPreviews().get();
825 for (var preview
: previews
) {
826 if (preview
.getImage().isPresent()) {
827 context
.getAttachmentHelper().downloadAttachment(preview
.getImage().get());
831 if (message
.getQuote().isPresent()) {
832 final var quote
= message
.getQuote().get();
834 if (quote
.getAttachments() != null) {
835 for (var quotedAttachment
: quote
.getAttachments()) {
836 final var thumbnail
= quotedAttachment
.getThumbnail();
837 if (thumbnail
!= null) {
838 context
.getAttachmentHelper().downloadAttachment(thumbnail
);
844 if (message
.getGiftBadge().isPresent()) {
845 handleIncomingGiftBadge(message
.getGiftBadge().get());
847 if (message
.getProfileKey().isPresent()) {
848 handleIncomingProfileKey(message
.getProfileKey().get(), source
.recipientId());
850 if (message
.getSticker().isPresent()) {
851 final var messageSticker
= message
.getSticker().get();
852 final var stickerPackId
= StickerPackId
.deserialize(messageSticker
.getPackId());
853 var sticker
= account
.getStickerStore().getStickerPack(stickerPackId
);
854 if (sticker
== null) {
855 sticker
= new StickerPack(stickerPackId
, messageSticker
.getPackKey());
856 account
.getStickerStore().addStickerPack(sticker
);
858 context
.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId
, messageSticker
.getPackKey()));
863 private void handleIncomingGiftBadge(final SignalServiceDataMessage
.GiftBadge giftBadge
) {
867 private List
<HandleAction
> handleSignalServiceStoryMessage(
868 SignalServiceStoryMessage message
, RecipientId source
, boolean ignoreAttachments
870 var actions
= new ArrayList
<HandleAction
>();
871 if (message
.getGroupContext().isPresent()) {
872 handleGroupV2Context(message
.getGroupContext().get());
875 if (!ignoreAttachments
) {
876 if (message
.getFileAttachment().isPresent()) {
877 context
.getAttachmentHelper().downloadAttachment(message
.getFileAttachment().get());
879 if (message
.getTextAttachment().isPresent()) {
880 final var textAttachment
= message
.getTextAttachment().get();
881 if (textAttachment
.getPreview().isPresent()) {
882 final var preview
= textAttachment
.getPreview().get();
883 if (preview
.getImage().isPresent()) {
884 context
.getAttachmentHelper().downloadAttachment(preview
.getImage().get());
890 if (message
.getProfileKey().isPresent()) {
891 handleIncomingProfileKey(message
.getProfileKey().get(), source
);
897 private void handleGroupV2Context(final SignalServiceGroupV2 groupContext
) {
898 final var groupMasterKey
= groupContext
.getMasterKey();
900 context
.getGroupHelper()
901 .getOrMigrateGroup(groupMasterKey
,
902 groupContext
.getRevision(),
903 groupContext
.hasSignedGroupChange() ? groupContext
.getSignedGroupChange() : null);
906 private void handleIncomingProfileKey(final byte[] profileKeyBytes
, final RecipientId source
) {
907 if (profileKeyBytes
.length
!= 32) {
908 logger
.debug("Received invalid profile key of length {}", profileKeyBytes
.length
);
911 final ProfileKey profileKey
;
913 profileKey
= new ProfileKey(profileKeyBytes
);
914 } catch (InvalidInputException e
) {
915 throw new AssertionError(e
);
917 if (account
.getSelfRecipientId().equals(source
)) {
918 this.account
.setProfileKey(profileKey
);
920 this.account
.getProfileStore().storeProfileKey(source
, profileKey
);
923 private SignalServiceAddress
getSenderAddress(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
924 final var serviceId
= envelope
.getSourceServiceId().map(ServiceId
::parseOrNull
).orElse(null);
925 if (!envelope
.isUnidentifiedSender() && serviceId
!= null) {
926 return new SignalServiceAddress(serviceId
);
927 } else if (content
!= null) {
928 return content
.getSender();
934 private DeviceAddress
getSender(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
935 final var serviceId
= envelope
.getSourceServiceId().map(ServiceId
::parseOrNull
).orElse(null);
936 if (!envelope
.isUnidentifiedSender() && serviceId
!= null) {
937 return new DeviceAddress(account
.getRecipientResolver().resolveRecipient(serviceId
),
939 envelope
.getSourceDevice());
941 return new DeviceAddress(account
.getRecipientResolver().resolveRecipient(content
.getSender()),
942 content
.getSender().getServiceId(),
943 content
.getSenderDevice());
947 private DeviceAddress
getDestination(SignalServiceEnvelope envelope
) {
948 final var destination
= envelope
.getDestinationServiceId();
949 if (destination
== null) {
950 return new DeviceAddress(account
.getSelfRecipientId(), account
.getAci(), account
.getDeviceId());
952 return new DeviceAddress(account
.getRecipientResolver().resolveRecipient(destination
),
954 account
.getDeviceId());
957 private record DeviceAddress(RecipientId recipientId
, ServiceId serviceId
, int deviceId
) {}