1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.Manager
;
4 import org
.asamk
.signal
.manager
.SignalDependencies
;
5 import org
.asamk
.signal
.manager
.actions
.HandleAction
;
6 import org
.asamk
.signal
.manager
.actions
.RefreshPreKeysAction
;
7 import org
.asamk
.signal
.manager
.actions
.RenewSessionAction
;
8 import org
.asamk
.signal
.manager
.actions
.ResendMessageAction
;
9 import org
.asamk
.signal
.manager
.actions
.RetrieveProfileAction
;
10 import org
.asamk
.signal
.manager
.actions
.RetrieveStorageDataAction
;
11 import org
.asamk
.signal
.manager
.actions
.SendGroupInfoAction
;
12 import org
.asamk
.signal
.manager
.actions
.SendGroupInfoRequestAction
;
13 import org
.asamk
.signal
.manager
.actions
.SendPniIdentityKeyAction
;
14 import org
.asamk
.signal
.manager
.actions
.SendProfileKeyAction
;
15 import org
.asamk
.signal
.manager
.actions
.SendReceiptAction
;
16 import org
.asamk
.signal
.manager
.actions
.SendRetryMessageRequestAction
;
17 import org
.asamk
.signal
.manager
.actions
.SendSyncBlockedListAction
;
18 import org
.asamk
.signal
.manager
.actions
.SendSyncConfigurationAction
;
19 import org
.asamk
.signal
.manager
.actions
.SendSyncContactsAction
;
20 import org
.asamk
.signal
.manager
.actions
.SendSyncGroupsAction
;
21 import org
.asamk
.signal
.manager
.actions
.SendSyncKeysAction
;
22 import org
.asamk
.signal
.manager
.actions
.UpdateAccountAttributesAction
;
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
.GroupId
;
30 import org
.asamk
.signal
.manager
.groups
.GroupNotFoundException
;
31 import org
.asamk
.signal
.manager
.groups
.GroupUtils
;
32 import org
.asamk
.signal
.manager
.jobs
.RetrieveStickerPackJob
;
33 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
34 import org
.asamk
.signal
.manager
.storage
.groups
.GroupInfoV1
;
35 import org
.asamk
.signal
.manager
.storage
.recipients
.Profile
;
36 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientId
;
37 import org
.asamk
.signal
.manager
.storage
.stickers
.StickerPack
;
38 import org
.asamk
.signal
.manager
.util
.KeyUtils
;
39 import org
.signal
.libsignal
.metadata
.ProtocolInvalidKeyException
;
40 import org
.signal
.libsignal
.metadata
.ProtocolInvalidKeyIdException
;
41 import org
.signal
.libsignal
.metadata
.ProtocolInvalidMessageException
;
42 import org
.signal
.libsignal
.metadata
.ProtocolNoSessionException
;
43 import org
.signal
.libsignal
.metadata
.ProtocolUntrustedIdentityException
;
44 import org
.signal
.libsignal
.metadata
.SelfSendException
;
45 import org
.signal
.libsignal
.protocol
.IdentityKeyPair
;
46 import org
.signal
.libsignal
.protocol
.InvalidMessageException
;
47 import org
.signal
.libsignal
.protocol
.groups
.GroupSessionBuilder
;
48 import org
.signal
.libsignal
.protocol
.message
.DecryptionErrorMessage
;
49 import org
.signal
.libsignal
.protocol
.state
.SignedPreKeyRecord
;
50 import org
.signal
.libsignal
.zkgroup
.InvalidInputException
;
51 import org
.signal
.libsignal
.zkgroup
.profiles
.ProfileKey
;
52 import org
.slf4j
.Logger
;
53 import org
.slf4j
.LoggerFactory
;
54 import org
.whispersystems
.signalservice
.api
.crypto
.SignalGroupSessionBuilder
;
55 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceContent
;
56 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceDataMessage
;
57 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
58 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroup
;
59 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupContext
;
60 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupV2
;
61 import org
.whispersystems
.signalservice
.api
.messages
.SignalServicePniSignatureMessage
;
62 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceReceiptMessage
;
63 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceStoryMessage
;
64 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SignalServiceSyncMessage
;
65 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.StickerPackOperationMessage
;
66 import org
.whispersystems
.signalservice
.api
.push
.ACI
;
67 import org
.whispersystems
.signalservice
.api
.push
.PNI
;
68 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
;
69 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
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 final static 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);
104 content
= dependencies
.getCipher().decrypt(envelope
);
105 } catch (ProtocolUntrustedIdentityException e
) {
106 final var recipientId
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
107 final var exception
= new UntrustedIdentityException(account
.getRecipientAddressResolver()
108 .resolveRecipientAddress(recipientId
)
109 .toApiRecipientAddress(), e
.getSenderDevice());
110 return new Pair
<>(List
.of(), exception
);
111 } catch (Exception e
) {
112 return new Pair
<>(List
.of(), e
);
114 account
.getIdentityKeyStore().setRetryingDecryption(false);
117 actions
.addAll(checkAndHandleMessage(envelope
, content
, receiveConfig
, handler
, null));
118 return new Pair
<>(actions
, null);
121 public Pair
<List
<HandleAction
>, Exception
> handleEnvelope(
122 final SignalServiceEnvelope envelope
,
123 final ReceiveConfig receiveConfig
,
124 final Manager
.ReceiveMessageHandler handler
126 final var actions
= new ArrayList
<HandleAction
>();
127 if (envelope
.hasSourceUuid()) {
128 // Store uuid if we don't have it already
129 // address/uuid in envelope is sent by server
130 account
.getRecipientTrustedResolver().resolveRecipientTrusted(envelope
.getSourceAddress());
132 SignalServiceContent content
= null;
133 Exception exception
= null;
134 if (!envelope
.isReceipt()) {
136 content
= dependencies
.getCipher().decrypt(envelope
);
137 } catch (ProtocolUntrustedIdentityException e
) {
138 final var recipientId
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
139 actions
.add(new RetrieveProfileAction(recipientId
));
140 exception
= new UntrustedIdentityException(account
.getRecipientAddressResolver()
141 .resolveRecipientAddress(recipientId
)
142 .toApiRecipientAddress(), e
.getSenderDevice());
143 } catch (ProtocolInvalidKeyIdException
| ProtocolInvalidKeyException
| ProtocolNoSessionException
|
144 ProtocolInvalidMessageException e
) {
145 logger
.debug("Failed to decrypt incoming message", e
);
146 final var sender
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
147 if (context
.getContactHelper().isContactBlocked(sender
)) {
148 logger
.debug("Received invalid message from blocked contact, ignoring.");
150 final var senderProfile
= context
.getProfileHelper().getRecipientProfile(sender
);
151 final var selfProfile
= context
.getProfileHelper().getSelfProfile();
152 var serviceId
= ServiceId
.parseOrNull(e
.getSender());
153 if (serviceId
== null) {
154 // Workaround for libsignal-client issue #492
155 serviceId
= account
.getRecipientAddressResolver()
156 .resolveRecipientAddress(sender
)
160 if (serviceId
!= null) {
161 final var isSelf
= sender
.equals(account
.getSelfRecipientId())
162 && e
.getSenderDevice() == account
.getDeviceId();
163 final var isSenderSenderKeyCapable
= senderProfile
!= null && senderProfile
.getCapabilities()
164 .contains(Profile
.Capability
.senderKey
);
165 final var isSelfSenderKeyCapable
= selfProfile
!= null && selfProfile
.getCapabilities()
166 .contains(Profile
.Capability
.senderKey
);
167 if (!isSelf
&& isSenderSenderKeyCapable
&& isSelfSenderKeyCapable
) {
168 logger
.debug("Received invalid message, requesting message resend.");
169 actions
.add(new SendRetryMessageRequestAction(sender
, serviceId
, e
, envelope
));
171 logger
.debug("Received invalid message, queuing renew session action.");
172 actions
.add(new RenewSessionAction(sender
, serviceId
));
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 List
<HandleAction
> checkAndHandleMessage(
193 final SignalServiceEnvelope envelope
,
194 final SignalServiceContent content
,
195 final ReceiveConfig receiveConfig
,
196 final Manager
.ReceiveMessageHandler handler
,
197 final Exception exception
199 if (content
!= null) {
200 // Store uuid if we don't have it already
201 // address/uuid is validated by unidentified sender certificate
203 boolean handledPniSignature
= false;
204 if (content
.getPniSignatureMessage().isPresent()) {
205 final var message
= content
.getPniSignatureMessage().get();
206 final var senderAddress
= getSenderAddress(envelope
, content
);
207 if (senderAddress
!= null) {
208 handledPniSignature
= handlePniSignatureMessage(message
, senderAddress
);
211 if (!handledPniSignature
) {
212 account
.getRecipientTrustedResolver().resolveRecipientTrusted(content
.getSender());
215 if (envelope
.isReceipt()) {
216 final var senderDeviceAddress
= getSender(envelope
, content
);
217 final var sender
= senderDeviceAddress
.serviceId();
218 final var senderDeviceId
= senderDeviceAddress
.deviceId();
219 account
.getMessageSendLogStore().deleteEntryForRecipient(envelope
.getTimestamp(), sender
, senderDeviceId
);
222 var notAllowedToSendToGroup
= isNotAllowedToSendToGroup(envelope
, content
);
223 final var groupContext
= getGroupContext(content
);
224 if (groupContext
!= null && groupContext
.getGroupV2().isPresent()) {
225 handleGroupV2Context(groupContext
.getGroupV2().get());
227 // Check again in case the user just joined the group
228 notAllowedToSendToGroup
= notAllowedToSendToGroup
&& isNotAllowedToSendToGroup(envelope
, content
);
230 if (isMessageBlocked(envelope
, content
)) {
231 logger
.info("Ignoring a message from blocked user/group: {}", envelope
.getTimestamp());
233 } else if (notAllowedToSendToGroup
) {
234 final var senderAddress
= getSenderAddress(envelope
, content
);
235 logger
.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
236 senderAddress
== null ?
null : senderAddress
.getIdentifier(),
237 envelope
.getTimestamp());
240 List
<HandleAction
> actions
;
241 if (content
!= null) {
242 actions
= handleMessage(envelope
, content
, receiveConfig
);
246 handler
.handleMessage(MessageEnvelope
.from(envelope
,
248 account
.getRecipientResolver(),
249 account
.getRecipientAddressResolver(),
250 context
.getAttachmentHelper()::getAttachmentFile
,
251 exception
), exception
);
256 public List
<HandleAction
> handleMessage(
257 SignalServiceEnvelope envelope
, SignalServiceContent content
, ReceiveConfig receiveConfig
259 var actions
= new ArrayList
<HandleAction
>();
260 final var senderDeviceAddress
= getSender(envelope
, content
);
261 final var sender
= senderDeviceAddress
.recipientId();
262 final var senderServiceId
= senderDeviceAddress
.serviceId();
263 final var senderDeviceId
= senderDeviceAddress
.deviceId();
264 final var destination
= getDestination(envelope
);
266 if (content
.getReceiptMessage().isPresent()) {
267 final var message
= content
.getReceiptMessage().get();
268 if (message
.isDeliveryReceipt()) {
269 account
.getMessageSendLogStore()
270 .deleteEntriesForRecipient(message
.getTimestamps(), senderServiceId
, senderDeviceId
);
274 if (content
.getSenderKeyDistributionMessage().isPresent()) {
275 final var message
= content
.getSenderKeyDistributionMessage().get();
276 final var protocolAddress
= senderServiceId
.toProtocolAddress(senderDeviceId
);
277 logger
.debug("Received a sender key distribution message for distributionId {} from {}",
278 message
.getDistributionId(),
280 new SignalGroupSessionBuilder(dependencies
.getSessionLock(),
281 new GroupSessionBuilder(account
.getSenderKeyStore())).process(protocolAddress
, message
);
284 if (content
.getDecryptionErrorMessage().isPresent()) {
285 var message
= content
.getDecryptionErrorMessage().get();
286 logger
.debug("Received a decryption error message from {}.{} (resend request for {})",
289 message
.getTimestamp());
290 if (message
.getDeviceId() == account
.getDeviceId()) {
291 handleDecryptionErrorMessage(actions
, sender
, senderServiceId
, senderDeviceId
, message
);
293 logger
.debug("Request is for another one of our devices");
297 if (content
.getDataMessage().isPresent()) {
298 var message
= content
.getDataMessage().get();
300 if (content
.isNeedsReceipt()) {
301 actions
.add(new SendReceiptAction(sender
,
302 SignalServiceReceiptMessage
.Type
.DELIVERY
,
303 message
.getTimestamp()));
305 // Message wasn't sent as unidentified sender message
306 final var contact
= context
.getAccount().getContactStore().getContact(sender
);
307 if (account
.isPrimaryDevice()
309 && !contact
.isBlocked()
310 && contact
.isProfileSharingEnabled()) {
311 actions
.add(UpdateAccountAttributesAction
.create());
312 actions
.add(new SendProfileKeyAction(sender
));
315 if (receiveConfig
.sendReadReceipts()) {
316 actions
.add(new SendReceiptAction(sender
,
317 SignalServiceReceiptMessage
.Type
.READ
,
318 message
.getTimestamp()));
321 actions
.addAll(handleSignalServiceDataMessage(message
,
325 receiveConfig
.ignoreAttachments()));
328 if (content
.getStoryMessage().isPresent()) {
329 final var message
= content
.getStoryMessage().get();
330 actions
.addAll(handleSignalServiceStoryMessage(message
, sender
, receiveConfig
.ignoreAttachments()));
333 if (content
.getSyncMessage().isPresent()) {
334 var syncMessage
= content
.getSyncMessage().get();
335 actions
.addAll(handleSyncMessage(envelope
,
338 receiveConfig
.ignoreAttachments()));
344 private boolean handlePniSignatureMessage(
345 final SignalServicePniSignatureMessage message
, final SignalServiceAddress senderAddress
347 final var aci
= ACI
.from(senderAddress
.getServiceId());
348 final var aciIdentity
= account
.getIdentityKeyStore().getIdentityInfo(aci
);
349 final var pni
= message
.getPni();
350 final var pniIdentity
= account
.getIdentityKeyStore().getIdentityInfo(pni
);
352 if (aciIdentity
== null || pniIdentity
== null || aci
.equals(pni
)) {
356 final var verified
= pniIdentity
.getIdentityKey()
357 .verifyAlternateIdentity(aciIdentity
.getIdentityKey(), message
.getSignature());
360 logger
.debug("Invalid PNI signature of ACI {} with PNI {}", aci
, pni
);
364 logger
.debug("Verified association of ACI {} with PNI {}", aci
, pni
);
365 account
.getRecipientTrustedResolver()
366 .resolveRecipientTrusted(Optional
.of(aci
), Optional
.of(pni
), senderAddress
.getNumber());
370 private void handleDecryptionErrorMessage(
371 final List
<HandleAction
> actions
,
372 final RecipientId sender
,
373 final ServiceId senderServiceId
,
374 final int senderDeviceId
,
375 final DecryptionErrorMessage message
377 final var logEntries
= account
.getMessageSendLogStore()
378 .findMessages(senderServiceId
,
380 message
.getTimestamp(),
381 message
.getRatchetKey().isEmpty());
383 for (final var logEntry
: logEntries
) {
384 actions
.add(new ResendMessageAction(sender
, message
.getTimestamp(), logEntry
));
387 if (message
.getRatchetKey().isPresent()) {
388 if (account
.getAciSessionStore()
389 .isCurrentRatchetKey(senderServiceId
, senderDeviceId
, message
.getRatchetKey().get())) {
390 if (logEntries
.isEmpty()) {
391 logger
.debug("Renewing the session with sender");
392 actions
.add(new RenewSessionAction(sender
, senderServiceId
));
394 logger
.trace("Archiving the session with sender, a resend message has already been queued");
395 context
.getAccount().getAciSessionStore().archiveSessions(senderServiceId
);
402 for (final var logEntry
: logEntries
) {
403 if (logEntry
.groupId().isEmpty()) {
406 final var group
= account
.getGroupStore().getGroup(logEntry
.groupId().get());
411 logger
.trace("Deleting shared sender key with {} ({}): {}",
414 group
.getDistributionId());
415 account
.getSenderKeyStore().deleteSharedWith(senderServiceId
, senderDeviceId
, group
.getDistributionId());
418 logger
.debug("Reset all shared sender keys with this recipient, no related message found in send log");
419 account
.getSenderKeyStore().deleteSharedWith(senderServiceId
);
423 private List
<HandleAction
> handleSyncMessage(
424 final SignalServiceEnvelope envelope
,
425 final SignalServiceSyncMessage syncMessage
,
426 final DeviceAddress sender
,
427 final boolean ignoreAttachments
429 var actions
= new ArrayList
<HandleAction
>();
430 account
.setMultiDevice(true);
431 if (syncMessage
.getSent().isPresent()) {
432 var message
= syncMessage
.getSent().get();
433 final var destination
= message
.getDestination().orElse(null);
434 if (message
.getDataMessage().isPresent()) {
435 actions
.addAll(handleSignalServiceDataMessage(message
.getDataMessage().get(),
440 : new DeviceAddress(context
.getRecipientHelper().resolveRecipient(destination
),
441 destination
.getServiceId(),
445 if (message
.getStoryMessage().isPresent()) {
446 actions
.addAll(handleSignalServiceStoryMessage(message
.getStoryMessage().get(),
447 sender
.recipientId(),
451 if (syncMessage
.getRequest().isPresent() && account
.isPrimaryDevice()) {
452 var rm
= syncMessage
.getRequest().get();
453 if (rm
.isContactsRequest()) {
454 actions
.add(SendSyncContactsAction
.create());
456 if (rm
.isGroupsRequest()) {
457 actions
.add(SendSyncGroupsAction
.create());
459 if (rm
.isBlockedListRequest()) {
460 actions
.add(SendSyncBlockedListAction
.create());
462 if (rm
.isKeysRequest()) {
463 actions
.add(SendSyncKeysAction
.create());
465 if (rm
.isConfigurationRequest()) {
466 actions
.add(SendSyncConfigurationAction
.create());
468 if (rm
.isPniIdentityRequest()) {
469 actions
.add(SendPniIdentityKeyAction
.create());
472 if (syncMessage
.getGroups().isPresent()) {
474 final var groupsMessage
= syncMessage
.getGroups().get();
475 context
.getAttachmentHelper()
476 .retrieveAttachment(groupsMessage
, context
.getSyncHelper()::handleSyncDeviceGroups
);
477 } catch (Exception e
) {
478 logger
.warn("Failed to handle received sync groups, ignoring: {}", e
.getMessage());
481 if (syncMessage
.getBlockedList().isPresent()) {
482 final var blockedListMessage
= syncMessage
.getBlockedList().get();
483 for (var address
: blockedListMessage
.getAddresses()) {
484 context
.getContactHelper()
485 .setContactBlocked(context
.getRecipientHelper().resolveRecipient(address
), true);
487 for (var groupId
: blockedListMessage
.getGroupIds()
489 .map(GroupId
::unknownVersion
)
490 .collect(Collectors
.toSet())) {
492 context
.getGroupHelper().setGroupBlocked(groupId
, true);
493 } catch (GroupNotFoundException e
) {
494 logger
.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
499 if (syncMessage
.getContacts().isPresent()) {
501 final var contactsMessage
= syncMessage
.getContacts().get();
502 context
.getAttachmentHelper()
503 .retrieveAttachment(contactsMessage
.getContactsStream(),
504 context
.getSyncHelper()::handleSyncDeviceContacts
);
505 } catch (Exception e
) {
506 logger
.warn("Failed to handle received sync contacts, ignoring: {}", e
.getMessage());
509 if (syncMessage
.getVerified().isPresent()) {
510 final var verifiedMessage
= syncMessage
.getVerified().get();
511 account
.getIdentityKeyStore()
512 .setIdentityTrustLevel(verifiedMessage
.getDestination().getServiceId(),
513 verifiedMessage
.getIdentityKey(),
514 TrustLevel
.fromVerifiedState(verifiedMessage
.getVerified()));
516 if (syncMessage
.getStickerPackOperations().isPresent()) {
517 final var stickerPackOperationMessages
= syncMessage
.getStickerPackOperations().get();
518 for (var m
: stickerPackOperationMessages
) {
519 if (m
.getPackId().isEmpty()) {
522 final var stickerPackId
= StickerPackId
.deserialize(m
.getPackId().get());
523 final var installed
= m
.getType().isEmpty()
524 || m
.getType().get() == StickerPackOperationMessage
.Type
.INSTALL
;
526 var sticker
= account
.getStickerStore().getStickerPack(stickerPackId
);
527 if (m
.getPackKey().isPresent()) {
528 if (sticker
== null) {
529 sticker
= new StickerPack(-1, stickerPackId
, m
.getPackKey().get(), installed
);
530 account
.getStickerStore().addStickerPack(sticker
);
533 context
.getJobExecutor()
534 .enqueueJob(new RetrieveStickerPackJob(stickerPackId
, m
.getPackKey().get()));
538 if (sticker
!= null && sticker
.isInstalled() != installed
) {
539 account
.getStickerStore().updateStickerPackInstalled(sticker
.packId(), installed
);
543 if (syncMessage
.getFetchType().isPresent()) {
544 switch (syncMessage
.getFetchType().get()) {
545 case LOCAL_PROFILE
-> actions
.add(new RetrieveProfileAction(account
.getSelfRecipientId()));
546 case STORAGE_MANIFEST
-> actions
.add(RetrieveStorageDataAction
.create());
549 if (syncMessage
.getKeys().isPresent()) {
550 final var keysMessage
= syncMessage
.getKeys().get();
551 if (keysMessage
.getStorageService().isPresent()) {
552 final var storageKey
= keysMessage
.getStorageService().get();
553 account
.setStorageKey(storageKey
);
554 actions
.add(RetrieveStorageDataAction
.create());
557 if (syncMessage
.getConfiguration().isPresent()) {
558 final var configurationMessage
= syncMessage
.getConfiguration().get();
559 final var configurationStore
= account
.getConfigurationStore();
560 if (configurationMessage
.getReadReceipts().isPresent()) {
561 configurationStore
.setReadReceipts(configurationMessage
.getReadReceipts().get());
563 if (configurationMessage
.getLinkPreviews().isPresent()) {
564 configurationStore
.setLinkPreviews(configurationMessage
.getLinkPreviews().get());
566 if (configurationMessage
.getTypingIndicators().isPresent()) {
567 configurationStore
.setTypingIndicators(configurationMessage
.getTypingIndicators().get());
569 if (configurationMessage
.getUnidentifiedDeliveryIndicators().isPresent()) {
570 configurationStore
.setUnidentifiedDeliveryIndicators(configurationMessage
.getUnidentifiedDeliveryIndicators()
574 if (syncMessage
.getPniIdentity().isPresent()) {
575 final var pniIdentity
= syncMessage
.getPniIdentity().get();
576 account
.setPniIdentityKeyPair(KeyUtils
.getIdentityKeyPair(pniIdentity
.getPublicKey().toByteArray(),
577 pniIdentity
.getPrivateKey().toByteArray()));
578 actions
.add(RefreshPreKeysAction
.create());
580 if (syncMessage
.getPniChangeNumber().isPresent()) {
581 final var pniChangeNumber
= syncMessage
.getPniChangeNumber().get();
582 logger
.debug("Received PNI change number sync message, applying.");
583 if (pniChangeNumber
.hasIdentityKeyPair()
584 && pniChangeNumber
.hasRegistrationId()
585 && pniChangeNumber
.hasSignedPreKey()
586 && !envelope
.getUpdatedPni().isEmpty()) {
587 logger
.debug("New PNI: {}", envelope
.getUpdatedPni());
589 final var updatedPni
= PNI
.parseOrThrow(envelope
.getUpdatedPni());
590 context
.getAccountHelper()
592 new IdentityKeyPair(pniChangeNumber
.getIdentityKeyPair().toByteArray()),
593 new SignedPreKeyRecord(pniChangeNumber
.getSignedPreKey().toByteArray()),
594 pniChangeNumber
.getRegistrationId());
595 } catch (Exception e
) {
596 logger
.warn("Failed to handle change number message", e
);
603 private SignalServiceGroupContext
getGroupContext(SignalServiceContent content
) {
604 if (content
== null) {
608 if (content
.getDataMessage().isPresent()) {
609 var message
= content
.getDataMessage().get();
610 if (message
.getGroupContext().isPresent()) {
611 return message
.getGroupContext().get();
615 if (content
.getStoryMessage().isPresent()) {
616 var message
= content
.getStoryMessage().get();
617 if (message
.getGroupContext().isPresent()) {
619 return SignalServiceGroupContext
.create(null, message
.getGroupContext().get());
620 } catch (InvalidMessageException e
) {
621 throw new AssertionError(e
);
629 private boolean isMessageBlocked(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
630 SignalServiceAddress source
= getSenderAddress(envelope
, content
);
631 if (source
== null) {
634 final var recipientId
= context
.getRecipientHelper().resolveRecipient(source
);
635 if (context
.getContactHelper().isContactBlocked(recipientId
)) {
639 final var groupContext
= getGroupContext(content
);
640 if (groupContext
!= null) {
641 var groupId
= GroupUtils
.getGroupId(groupContext
);
642 return context
.getGroupHelper().isGroupBlocked(groupId
);
648 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
649 SignalServiceAddress source
= getSenderAddress(envelope
, content
);
650 if (source
== null) {
654 final var groupContext
= getGroupContext(content
);
655 if (groupContext
== null) {
659 if (groupContext
.getGroupV1().isPresent()) {
660 var groupInfo
= groupContext
.getGroupV1().get();
661 if (groupInfo
.getType() == SignalServiceGroup
.Type
.QUIT
) {
666 var groupId
= GroupUtils
.getGroupId(groupContext
);
667 var group
= context
.getGroupHelper().getGroup(groupId
);
672 final var message
= content
.getDataMessage().orElse(null);
674 final var recipientId
= context
.getRecipientHelper().resolveRecipient(source
);
675 if (!group
.isMember(recipientId
) && !(
676 group
.isPendingMember(recipientId
) && message
!= null && message
.isGroupV2Update()
681 if (group
.isAnnouncementGroup() && !group
.isAdmin(recipientId
)) {
682 return message
== null
683 || message
.getBody().isPresent()
684 || message
.getAttachments().isPresent()
685 || message
.getQuote().isPresent()
686 || message
.getPreviews().isPresent()
687 || message
.getMentions().isPresent()
688 || message
.getSticker().isPresent();
693 private List
<HandleAction
> handleSignalServiceDataMessage(
694 SignalServiceDataMessage message
,
696 DeviceAddress source
,
697 DeviceAddress destination
,
698 boolean ignoreAttachments
700 var actions
= new ArrayList
<HandleAction
>();
701 if (message
.getGroupContext().isPresent()) {
702 final var groupContext
= message
.getGroupContext().get();
703 if (groupContext
.getGroupV1().isPresent()) {
704 var groupInfo
= groupContext
.getGroupV1().get();
705 var groupId
= GroupId
.v1(groupInfo
.getGroupId());
706 var group
= context
.getGroupHelper().getGroup(groupId
);
707 if (group
== null || group
instanceof GroupInfoV1
) {
708 var groupV1
= (GroupInfoV1
) group
;
709 switch (groupInfo
.getType()) {
711 if (groupV1
== null) {
712 groupV1
= new GroupInfoV1(groupId
);
715 if (groupInfo
.getAvatar().isPresent()) {
716 var avatar
= groupInfo
.getAvatar().get();
717 context
.getGroupHelper().downloadGroupAvatar(groupV1
.getGroupId(), avatar
);
720 if (groupInfo
.getName().isPresent()) {
721 groupV1
.name
= groupInfo
.getName().get();
724 if (groupInfo
.getMembers().isPresent()) {
725 groupV1
.addMembers(groupInfo
.getMembers()
728 .map(context
.getRecipientHelper()::resolveRecipient
)
729 .collect(Collectors
.toSet()));
732 account
.getGroupStore().updateGroup(groupV1
);
735 if (groupV1
== null && !isSync
) {
736 actions
.add(new SendGroupInfoRequestAction(source
.recipientId(), groupId
));
740 if (groupV1
!= null) {
741 groupV1
.removeMember(source
.recipientId());
742 account
.getGroupStore().updateGroup(groupV1
);
745 case REQUEST_INFO
-> {
746 if (groupV1
!= null && !isSync
) {
747 actions
.add(new SendGroupInfoAction(source
.recipientId(), groupV1
.getGroupId()));
752 // Received a group v1 message for a v2 group
755 if (groupContext
.getGroupV2().isPresent()) {
756 handleGroupV2Context(groupContext
.getGroupV2().get());
760 final var conversationPartnerAddress
= isSync ? destination
: source
;
761 if (conversationPartnerAddress
!= null && message
.isEndSession()) {
762 account
.getAciSessionStore().deleteAllSessions(conversationPartnerAddress
.serviceId());
764 if (message
.isExpirationUpdate() || message
.getBody().isPresent()) {
765 if (message
.getGroupContext().isPresent()) {
766 final var groupContext
= message
.getGroupContext().get();
767 if (groupContext
.getGroupV1().isPresent()) {
768 var groupInfo
= groupContext
.getGroupV1().get();
769 var group
= account
.getGroupStore().getOrCreateGroupV1(GroupId
.v1(groupInfo
.getGroupId()));
771 if (group
.messageExpirationTime
!= message
.getExpiresInSeconds()) {
772 group
.messageExpirationTime
= message
.getExpiresInSeconds();
773 account
.getGroupStore().updateGroup(group
);
776 } else if (groupContext
.getGroupV2().isPresent()) {
777 // disappearing message timer already stored in the DecryptedGroup
779 } else if (conversationPartnerAddress
!= null) {
780 context
.getContactHelper()
781 .setExpirationTimer(conversationPartnerAddress
.recipientId(), message
.getExpiresInSeconds());
784 if (!ignoreAttachments
) {
785 if (message
.getAttachments().isPresent()) {
786 for (var attachment
: message
.getAttachments().get()) {
787 context
.getAttachmentHelper().downloadAttachment(attachment
);
790 if (message
.getSharedContacts().isPresent()) {
791 for (var contact
: message
.getSharedContacts().get()) {
792 if (contact
.getAvatar().isPresent()) {
793 context
.getAttachmentHelper().downloadAttachment(contact
.getAvatar().get().getAttachment());
797 if (message
.getPreviews().isPresent()) {
798 final var previews
= message
.getPreviews().get();
799 for (var preview
: previews
) {
800 if (preview
.getImage().isPresent()) {
801 context
.getAttachmentHelper().downloadAttachment(preview
.getImage().get());
805 if (message
.getQuote().isPresent()) {
806 final var quote
= message
.getQuote().get();
808 for (var quotedAttachment
: quote
.getAttachments()) {
809 final var thumbnail
= quotedAttachment
.getThumbnail();
810 if (thumbnail
!= null) {
811 context
.getAttachmentHelper().downloadAttachment(thumbnail
);
816 if (message
.getGiftBadge().isPresent()) {
817 handleIncomingGiftBadge(message
.getGiftBadge().get());
819 if (message
.getProfileKey().isPresent()) {
820 handleIncomingProfileKey(message
.getProfileKey().get(), source
.recipientId());
822 if (message
.getSticker().isPresent()) {
823 final var messageSticker
= message
.getSticker().get();
824 final var stickerPackId
= StickerPackId
.deserialize(messageSticker
.getPackId());
825 var sticker
= account
.getStickerStore().getStickerPack(stickerPackId
);
826 if (sticker
== null) {
827 sticker
= new StickerPack(stickerPackId
, messageSticker
.getPackKey());
828 account
.getStickerStore().addStickerPack(sticker
);
830 context
.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId
, messageSticker
.getPackKey()));
835 private void handleIncomingGiftBadge(final SignalServiceDataMessage
.GiftBadge giftBadge
) {
839 private List
<HandleAction
> handleSignalServiceStoryMessage(
840 SignalServiceStoryMessage message
, RecipientId source
, boolean ignoreAttachments
842 var actions
= new ArrayList
<HandleAction
>();
843 if (message
.getGroupContext().isPresent()) {
844 handleGroupV2Context(message
.getGroupContext().get());
847 if (!ignoreAttachments
) {
848 if (message
.getFileAttachment().isPresent()) {
849 context
.getAttachmentHelper().downloadAttachment(message
.getFileAttachment().get());
851 if (message
.getTextAttachment().isPresent()) {
852 final var textAttachment
= message
.getTextAttachment().get();
853 if (textAttachment
.getPreview().isPresent()) {
854 final var preview
= textAttachment
.getPreview().get();
855 if (preview
.getImage().isPresent()) {
856 context
.getAttachmentHelper().downloadAttachment(preview
.getImage().get());
862 if (message
.getProfileKey().isPresent()) {
863 handleIncomingProfileKey(message
.getProfileKey().get(), source
);
869 private void handleGroupV2Context(final SignalServiceGroupV2 groupContext
) {
870 final var groupMasterKey
= groupContext
.getMasterKey();
872 context
.getGroupHelper()
873 .getOrMigrateGroup(groupMasterKey
,
874 groupContext
.getRevision(),
875 groupContext
.hasSignedGroupChange() ? groupContext
.getSignedGroupChange() : null);
878 private void handleIncomingProfileKey(final byte[] profileKeyBytes
, final RecipientId source
) {
879 if (profileKeyBytes
.length
!= 32) {
880 logger
.debug("Received invalid profile key of length {}", profileKeyBytes
.length
);
883 final ProfileKey profileKey
;
885 profileKey
= new ProfileKey(profileKeyBytes
);
886 } catch (InvalidInputException e
) {
887 throw new AssertionError(e
);
889 if (account
.getSelfRecipientId().equals(source
)) {
890 this.account
.setProfileKey(profileKey
);
892 this.account
.getProfileStore().storeProfileKey(source
, profileKey
);
895 private SignalServiceAddress
getSenderAddress(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
896 if (!envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()) {
897 return envelope
.getSourceAddress();
898 } else if (content
!= null) {
899 return content
.getSender();
905 private DeviceAddress
getSender(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
906 if (!envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()) {
907 return new DeviceAddress(context
.getRecipientHelper().resolveRecipient(envelope
.getSourceAddress()),
908 envelope
.getSourceAddress().getServiceId(),
909 envelope
.getSourceDevice());
911 return new DeviceAddress(context
.getRecipientHelper().resolveRecipient(content
.getSender()),
912 content
.getSender().getServiceId(),
913 content
.getSenderDevice());
917 private DeviceAddress
getDestination(SignalServiceEnvelope envelope
) {
918 if (!envelope
.hasDestinationUuid()) {
919 return new DeviceAddress(account
.getSelfRecipientId(), account
.getAci(), account
.getDeviceId());
921 final var addressOptional
= SignalServiceAddress
.fromRaw(envelope
.getDestinationUuid(), null);
922 if (addressOptional
.isEmpty()) {
923 return new DeviceAddress(account
.getSelfRecipientId(), account
.getAci(), account
.getDeviceId());
925 final var address
= addressOptional
.get();
926 return new DeviceAddress(context
.getRecipientHelper().resolveRecipient(address
), address
.getServiceId(), 0);
929 private record DeviceAddress(RecipientId recipientId
, ServiceId serviceId
, int deviceId
) {}