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
.message
.DecryptionErrorMessage
;
48 import org
.signal
.libsignal
.protocol
.state
.SignedPreKeyRecord
;
49 import org
.signal
.libsignal
.zkgroup
.InvalidInputException
;
50 import org
.signal
.libsignal
.zkgroup
.profiles
.ProfileKey
;
51 import org
.slf4j
.Logger
;
52 import org
.slf4j
.LoggerFactory
;
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
.ACI
;
65 import org
.whispersystems
.signalservice
.api
.push
.PNI
;
66 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
;
67 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
69 import java
.util
.ArrayList
;
70 import java
.util
.List
;
71 import java
.util
.Optional
;
72 import java
.util
.stream
.Collectors
;
74 public final class IncomingMessageHandler
{
76 private final static Logger logger
= LoggerFactory
.getLogger(IncomingMessageHandler
.class);
78 private final SignalAccount account
;
79 private final SignalDependencies dependencies
;
80 private final Context context
;
82 public IncomingMessageHandler(final Context context
) {
83 this.account
= context
.getAccount();
84 this.dependencies
= context
.getDependencies();
85 this.context
= context
;
88 public Pair
<List
<HandleAction
>, Exception
> handleRetryEnvelope(
89 final SignalServiceEnvelope envelope
,
90 final ReceiveConfig receiveConfig
,
91 final Manager
.ReceiveMessageHandler handler
93 final List
<HandleAction
> actions
= new ArrayList
<>();
94 if (envelope
.isPreKeySignalMessage()) {
95 actions
.add(RefreshPreKeysAction
.create());
98 SignalServiceContent content
= null;
99 if (!envelope
.isReceipt()) {
100 account
.getIdentityKeyStore().setRetryingDecryption(true);
102 content
= dependencies
.getCipher().decrypt(envelope
);
103 } catch (ProtocolUntrustedIdentityException e
) {
104 final var recipientId
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
105 final var exception
= new UntrustedIdentityException(account
.getRecipientAddressResolver()
106 .resolveRecipientAddress(recipientId
)
107 .toApiRecipientAddress(), e
.getSenderDevice());
108 return new Pair
<>(List
.of(), exception
);
109 } catch (Exception e
) {
110 return new Pair
<>(List
.of(), e
);
112 account
.getIdentityKeyStore().setRetryingDecryption(false);
115 actions
.addAll(checkAndHandleMessage(envelope
, content
, receiveConfig
, handler
, null));
116 return new Pair
<>(actions
, null);
119 public Pair
<List
<HandleAction
>, Exception
> handleEnvelope(
120 final SignalServiceEnvelope envelope
,
121 final ReceiveConfig receiveConfig
,
122 final Manager
.ReceiveMessageHandler handler
124 final var actions
= new ArrayList
<HandleAction
>();
125 if (envelope
.hasSourceUuid()) {
126 // Store uuid if we don't have it already
127 // address/uuid in envelope is sent by server
128 account
.getRecipientTrustedResolver().resolveRecipientTrusted(envelope
.getSourceAddress());
130 SignalServiceContent content
= null;
131 Exception exception
= null;
132 if (!envelope
.isReceipt()) {
134 content
= dependencies
.getCipher().decrypt(envelope
);
135 } catch (ProtocolUntrustedIdentityException e
) {
136 final var recipientId
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
137 actions
.add(new RetrieveProfileAction(recipientId
));
138 exception
= new UntrustedIdentityException(account
.getRecipientAddressResolver()
139 .resolveRecipientAddress(recipientId
)
140 .toApiRecipientAddress(), e
.getSenderDevice());
141 } catch (ProtocolInvalidKeyIdException
| ProtocolInvalidKeyException
| ProtocolNoSessionException
|
142 ProtocolInvalidMessageException e
) {
143 logger
.debug("Failed to decrypt incoming message", e
);
144 final var sender
= account
.getRecipientResolver().resolveRecipient(e
.getSender());
145 if (context
.getContactHelper().isContactBlocked(sender
)) {
146 logger
.debug("Received invalid message from blocked contact, ignoring.");
148 final var senderProfile
= context
.getProfileHelper().getRecipientProfile(sender
);
149 final var selfProfile
= context
.getProfileHelper().getSelfProfile();
150 var serviceId
= ServiceId
.parseOrNull(e
.getSender());
151 if (serviceId
== null) {
152 // Workaround for libsignal-client issue #492
153 serviceId
= account
.getRecipientAddressResolver()
154 .resolveRecipientAddress(sender
)
158 if (serviceId
!= null) {
159 final var isSelf
= sender
.equals(account
.getSelfRecipientId())
160 && e
.getSenderDevice() == account
.getDeviceId();
161 final var isSenderSenderKeyCapable
= senderProfile
!= null && senderProfile
.getCapabilities()
162 .contains(Profile
.Capability
.senderKey
);
163 final var isSelfSenderKeyCapable
= selfProfile
!= null && selfProfile
.getCapabilities()
164 .contains(Profile
.Capability
.senderKey
);
165 if (!isSelf
&& isSenderSenderKeyCapable
&& isSelfSenderKeyCapable
) {
166 logger
.debug("Received invalid message, requesting message resend.");
167 actions
.add(new SendRetryMessageRequestAction(sender
, serviceId
, e
, envelope
));
169 logger
.debug("Received invalid message, queuing renew session action.");
170 actions
.add(new RenewSessionAction(sender
, serviceId
));
173 logger
.debug("Received invalid message from invalid sender: {}", e
.getSender());
177 } catch (SelfSendException e
) {
178 logger
.debug("Dropping unidentified message from self.");
179 return new Pair
<>(List
.of(), null);
180 } catch (Exception e
) {
181 logger
.debug("Failed to handle incoming message", e
);
186 actions
.addAll(checkAndHandleMessage(envelope
, content
, receiveConfig
, handler
, exception
));
187 return new Pair
<>(actions
, exception
);
190 private List
<HandleAction
> checkAndHandleMessage(
191 final SignalServiceEnvelope envelope
,
192 final SignalServiceContent content
,
193 final ReceiveConfig receiveConfig
,
194 final Manager
.ReceiveMessageHandler handler
,
195 final Exception exception
197 if (content
!= null) {
198 // Store uuid if we don't have it already
199 // address/uuid is validated by unidentified sender certificate
201 boolean handledPniSignature
= false;
202 if (content
.getPniSignatureMessage().isPresent()) {
203 final var message
= content
.getPniSignatureMessage().get();
204 final var senderAddress
= getSenderAddress(envelope
, content
);
205 if (senderAddress
!= null) {
206 handledPniSignature
= handlePniSignatureMessage(message
, senderAddress
);
209 if (!handledPniSignature
) {
210 account
.getRecipientTrustedResolver().resolveRecipientTrusted(content
.getSender());
213 if (envelope
.isReceipt()) {
214 final var senderDeviceAddress
= getSender(envelope
, content
);
215 final var sender
= senderDeviceAddress
.serviceId();
216 final var senderDeviceId
= senderDeviceAddress
.deviceId();
217 account
.getMessageSendLogStore().deleteEntryForRecipient(envelope
.getTimestamp(), sender
, senderDeviceId
);
220 var notAllowedToSendToGroup
= isNotAllowedToSendToGroup(envelope
, content
);
221 final var groupContext
= getGroupContext(content
);
222 if (groupContext
!= null && groupContext
.getGroupV2().isPresent()) {
223 handleGroupV2Context(groupContext
.getGroupV2().get());
225 // Check again in case the user just joined the group
226 notAllowedToSendToGroup
= notAllowedToSendToGroup
&& isNotAllowedToSendToGroup(envelope
, content
);
228 if (isMessageBlocked(envelope
, content
)) {
229 logger
.info("Ignoring a message from blocked user/group: {}", envelope
.getTimestamp());
231 } else if (notAllowedToSendToGroup
) {
232 final var senderAddress
= getSenderAddress(envelope
, content
);
233 logger
.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
234 senderAddress
== null ?
null : senderAddress
.getIdentifier(),
235 envelope
.getTimestamp());
238 List
<HandleAction
> actions
;
239 if (content
!= null) {
240 actions
= handleMessage(envelope
, content
, receiveConfig
);
244 handler
.handleMessage(MessageEnvelope
.from(envelope
,
246 account
.getRecipientResolver(),
247 account
.getRecipientAddressResolver(),
248 context
.getAttachmentHelper()::getAttachmentFile
,
249 exception
), exception
);
254 public List
<HandleAction
> handleMessage(
255 SignalServiceEnvelope envelope
, SignalServiceContent content
, ReceiveConfig receiveConfig
257 var actions
= new ArrayList
<HandleAction
>();
258 final var senderDeviceAddress
= getSender(envelope
, content
);
259 final var sender
= senderDeviceAddress
.recipientId();
260 final var senderServiceId
= senderDeviceAddress
.serviceId();
261 final var senderDeviceId
= senderDeviceAddress
.deviceId();
262 final var destination
= getDestination(envelope
);
264 if (content
.getReceiptMessage().isPresent()) {
265 final var message
= content
.getReceiptMessage().get();
266 if (message
.isDeliveryReceipt()) {
267 account
.getMessageSendLogStore()
268 .deleteEntriesForRecipient(message
.getTimestamps(), senderServiceId
, senderDeviceId
);
272 if (content
.getSenderKeyDistributionMessage().isPresent()) {
273 final var message
= content
.getSenderKeyDistributionMessage().get();
274 final var protocolAddress
= senderServiceId
.toProtocolAddress(senderDeviceId
);
275 logger
.debug("Received a sender key distribution message for distributionId {} from {}",
276 message
.getDistributionId(),
278 dependencies
.getMessageSender().processSenderKeyDistributionMessage(protocolAddress
, message
);
281 if (content
.getDecryptionErrorMessage().isPresent()) {
282 var message
= content
.getDecryptionErrorMessage().get();
283 logger
.debug("Received a decryption error message from {}.{} (resend request for {})",
286 message
.getTimestamp());
287 if (message
.getDeviceId() == account
.getDeviceId()) {
288 handleDecryptionErrorMessage(actions
, sender
, senderServiceId
, senderDeviceId
, message
);
290 logger
.debug("Request is for another one of our devices");
294 if (content
.getDataMessage().isPresent()) {
295 var message
= content
.getDataMessage().get();
297 if (content
.isNeedsReceipt()) {
298 actions
.add(new SendReceiptAction(sender
,
299 SignalServiceReceiptMessage
.Type
.DELIVERY
,
300 message
.getTimestamp()));
302 // Message wasn't sent as unidentified sender message
303 final var contact
= context
.getAccount().getContactStore().getContact(sender
);
304 if (account
.isPrimaryDevice()
306 && !contact
.isBlocked()
307 && contact
.isProfileSharingEnabled()) {
308 actions
.add(UpdateAccountAttributesAction
.create());
309 actions
.add(new SendProfileKeyAction(sender
));
312 if (receiveConfig
.sendReadReceipts()) {
313 actions
.add(new SendReceiptAction(sender
,
314 SignalServiceReceiptMessage
.Type
.READ
,
315 message
.getTimestamp()));
318 actions
.addAll(handleSignalServiceDataMessage(message
,
322 receiveConfig
.ignoreAttachments()));
325 if (content
.getStoryMessage().isPresent()) {
326 final var message
= content
.getStoryMessage().get();
327 actions
.addAll(handleSignalServiceStoryMessage(message
, sender
, receiveConfig
.ignoreAttachments()));
330 if (content
.getSyncMessage().isPresent()) {
331 var syncMessage
= content
.getSyncMessage().get();
332 actions
.addAll(handleSyncMessage(envelope
,
335 receiveConfig
.ignoreAttachments()));
341 private boolean handlePniSignatureMessage(
342 final SignalServicePniSignatureMessage message
, final SignalServiceAddress senderAddress
344 final var aci
= ACI
.from(senderAddress
.getServiceId());
345 final var aciIdentity
= account
.getIdentityKeyStore().getIdentityInfo(aci
);
346 final var pni
= message
.getPni();
347 final var pniIdentity
= account
.getIdentityKeyStore().getIdentityInfo(pni
);
349 if (aciIdentity
== null || pniIdentity
== null || aci
.equals(pni
)) {
353 final var verified
= pniIdentity
.getIdentityKey()
354 .verifyAlternateIdentity(aciIdentity
.getIdentityKey(), message
.getSignature());
357 logger
.debug("Invalid PNI signature of ACI {} with PNI {}", aci
, pni
);
361 logger
.debug("Verified association of ACI {} with PNI {}", aci
, pni
);
362 account
.getRecipientTrustedResolver()
363 .resolveRecipientTrusted(Optional
.of(aci
), Optional
.of(pni
), senderAddress
.getNumber());
367 private void handleDecryptionErrorMessage(
368 final List
<HandleAction
> actions
,
369 final RecipientId sender
,
370 final ServiceId senderServiceId
,
371 final int senderDeviceId
,
372 final DecryptionErrorMessage message
374 final var logEntries
= account
.getMessageSendLogStore()
375 .findMessages(senderServiceId
,
377 message
.getTimestamp(),
378 message
.getRatchetKey().isEmpty());
380 for (final var logEntry
: logEntries
) {
381 actions
.add(new ResendMessageAction(sender
, message
.getTimestamp(), logEntry
));
384 if (message
.getRatchetKey().isPresent()) {
385 if (account
.getAciSessionStore()
386 .isCurrentRatchetKey(senderServiceId
, senderDeviceId
, message
.getRatchetKey().get())) {
387 if (logEntries
.isEmpty()) {
388 logger
.debug("Renewing the session with sender");
389 actions
.add(new RenewSessionAction(sender
, senderServiceId
));
391 logger
.trace("Archiving the session with sender, a resend message has already been queued");
392 context
.getAccount().getAciSessionStore().archiveSessions(senderServiceId
);
399 for (final var logEntry
: logEntries
) {
400 if (logEntry
.groupId().isEmpty()) {
403 final var group
= account
.getGroupStore().getGroup(logEntry
.groupId().get());
408 logger
.trace("Deleting shared sender key with {} ({}): {}",
411 group
.getDistributionId());
412 account
.getSenderKeyStore().deleteSharedWith(senderServiceId
, senderDeviceId
, group
.getDistributionId());
415 logger
.debug("Reset all shared sender keys with this recipient, no related message found in send log");
416 account
.getSenderKeyStore().deleteSharedWith(senderServiceId
);
420 private List
<HandleAction
> handleSyncMessage(
421 final SignalServiceEnvelope envelope
,
422 final SignalServiceSyncMessage syncMessage
,
423 final DeviceAddress sender
,
424 final boolean ignoreAttachments
426 var actions
= new ArrayList
<HandleAction
>();
427 account
.setMultiDevice(true);
428 if (syncMessage
.getSent().isPresent()) {
429 var message
= syncMessage
.getSent().get();
430 final var destination
= message
.getDestination().orElse(null);
431 if (message
.getDataMessage().isPresent()) {
432 actions
.addAll(handleSignalServiceDataMessage(message
.getDataMessage().get(),
437 : new DeviceAddress(context
.getRecipientHelper().resolveRecipient(destination
),
438 destination
.getServiceId(),
442 if (message
.getStoryMessage().isPresent()) {
443 actions
.addAll(handleSignalServiceStoryMessage(message
.getStoryMessage().get(),
444 sender
.recipientId(),
448 if (syncMessage
.getRequest().isPresent() && account
.isPrimaryDevice()) {
449 var rm
= syncMessage
.getRequest().get();
450 if (rm
.isContactsRequest()) {
451 actions
.add(SendSyncContactsAction
.create());
453 if (rm
.isGroupsRequest()) {
454 actions
.add(SendSyncGroupsAction
.create());
456 if (rm
.isBlockedListRequest()) {
457 actions
.add(SendSyncBlockedListAction
.create());
459 if (rm
.isKeysRequest()) {
460 actions
.add(SendSyncKeysAction
.create());
462 if (rm
.isConfigurationRequest()) {
463 actions
.add(SendSyncConfigurationAction
.create());
465 if (rm
.isPniIdentityRequest()) {
466 actions
.add(SendPniIdentityKeyAction
.create());
469 if (syncMessage
.getGroups().isPresent()) {
471 final var groupsMessage
= syncMessage
.getGroups().get();
472 context
.getAttachmentHelper()
473 .retrieveAttachment(groupsMessage
, context
.getSyncHelper()::handleSyncDeviceGroups
);
474 } catch (Exception e
) {
475 logger
.warn("Failed to handle received sync groups, ignoring: {}", e
.getMessage());
478 if (syncMessage
.getBlockedList().isPresent()) {
479 final var blockedListMessage
= syncMessage
.getBlockedList().get();
480 for (var address
: blockedListMessage
.getAddresses()) {
481 context
.getContactHelper()
482 .setContactBlocked(context
.getRecipientHelper().resolveRecipient(address
), true);
484 for (var groupId
: blockedListMessage
.getGroupIds()
486 .map(GroupId
::unknownVersion
)
487 .collect(Collectors
.toSet())) {
489 context
.getGroupHelper().setGroupBlocked(groupId
, true);
490 } catch (GroupNotFoundException e
) {
491 logger
.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
496 if (syncMessage
.getContacts().isPresent()) {
498 final var contactsMessage
= syncMessage
.getContacts().get();
499 context
.getAttachmentHelper()
500 .retrieveAttachment(contactsMessage
.getContactsStream(),
501 context
.getSyncHelper()::handleSyncDeviceContacts
);
502 } catch (Exception e
) {
503 logger
.warn("Failed to handle received sync contacts, ignoring: {}", e
.getMessage());
506 if (syncMessage
.getVerified().isPresent()) {
507 final var verifiedMessage
= syncMessage
.getVerified().get();
508 account
.getIdentityKeyStore()
509 .setIdentityTrustLevel(verifiedMessage
.getDestination().getServiceId(),
510 verifiedMessage
.getIdentityKey(),
511 TrustLevel
.fromVerifiedState(verifiedMessage
.getVerified()));
513 if (syncMessage
.getStickerPackOperations().isPresent()) {
514 final var stickerPackOperationMessages
= syncMessage
.getStickerPackOperations().get();
515 for (var m
: stickerPackOperationMessages
) {
516 if (m
.getPackId().isEmpty()) {
519 final var stickerPackId
= StickerPackId
.deserialize(m
.getPackId().get());
520 final var installed
= m
.getType().isEmpty()
521 || m
.getType().get() == StickerPackOperationMessage
.Type
.INSTALL
;
523 var sticker
= account
.getStickerStore().getStickerPack(stickerPackId
);
524 if (m
.getPackKey().isPresent()) {
525 if (sticker
== null) {
526 sticker
= new StickerPack(-1, stickerPackId
, m
.getPackKey().get(), installed
);
527 account
.getStickerStore().addStickerPack(sticker
);
530 context
.getJobExecutor()
531 .enqueueJob(new RetrieveStickerPackJob(stickerPackId
, m
.getPackKey().get()));
535 if (sticker
!= null && sticker
.isInstalled() != installed
) {
536 account
.getStickerStore().updateStickerPackInstalled(sticker
.packId(), installed
);
540 if (syncMessage
.getFetchType().isPresent()) {
541 switch (syncMessage
.getFetchType().get()) {
543 actions
.add(new RetrieveProfileAction(account
.getSelfRecipientId()));
544 case STORAGE_MANIFEST
:
545 actions
.add(RetrieveStorageDataAction
.create());
548 if (syncMessage
.getKeys().isPresent()) {
549 final var keysMessage
= syncMessage
.getKeys().get();
550 if (keysMessage
.getStorageService().isPresent()) {
551 final var storageKey
= keysMessage
.getStorageService().get();
552 account
.setStorageKey(storageKey
);
553 actions
.add(RetrieveStorageDataAction
.create());
556 if (syncMessage
.getConfiguration().isPresent()) {
557 final var configurationMessage
= syncMessage
.getConfiguration().get();
558 final var configurationStore
= account
.getConfigurationStore();
559 if (configurationMessage
.getReadReceipts().isPresent()) {
560 configurationStore
.setReadReceipts(configurationMessage
.getReadReceipts().get());
562 if (configurationMessage
.getLinkPreviews().isPresent()) {
563 configurationStore
.setLinkPreviews(configurationMessage
.getLinkPreviews().get());
565 if (configurationMessage
.getTypingIndicators().isPresent()) {
566 configurationStore
.setTypingIndicators(configurationMessage
.getTypingIndicators().get());
568 if (configurationMessage
.getUnidentifiedDeliveryIndicators().isPresent()) {
569 configurationStore
.setUnidentifiedDeliveryIndicators(configurationMessage
.getUnidentifiedDeliveryIndicators()
573 if (syncMessage
.getPniIdentity().isPresent()) {
574 final var pniIdentity
= syncMessage
.getPniIdentity().get();
575 account
.setPniIdentityKeyPair(KeyUtils
.getIdentityKeyPair(pniIdentity
.getPublicKey().toByteArray(),
576 pniIdentity
.getPrivateKey().toByteArray()));
577 actions
.add(RefreshPreKeysAction
.create());
579 if (syncMessage
.getPniChangeNumber().isPresent()) {
580 final var pniChangeNumber
= syncMessage
.getPniChangeNumber().get();
581 logger
.debug("Received PNI change number sync message, applying.");
582 if (pniChangeNumber
.hasIdentityKeyPair()
583 && pniChangeNumber
.hasRegistrationId()
584 && pniChangeNumber
.hasSignedPreKey()
585 && !envelope
.getUpdatedPni().isEmpty()) {
586 logger
.debug("New PNI: {}", envelope
.getUpdatedPni());
588 final var updatedPni
= PNI
.parseOrThrow(envelope
.getUpdatedPni());
589 context
.getAccountHelper()
591 new IdentityKeyPair(pniChangeNumber
.getIdentityKeyPair().toByteArray()),
592 new SignedPreKeyRecord(pniChangeNumber
.getSignedPreKey().toByteArray()),
593 pniChangeNumber
.getRegistrationId());
594 } catch (Exception e
) {
595 logger
.warn("Failed to handle change number message", e
);
602 private SignalServiceGroupContext
getGroupContext(SignalServiceContent content
) {
603 if (content
== null) {
607 if (content
.getDataMessage().isPresent()) {
608 var message
= content
.getDataMessage().get();
609 if (message
.getGroupContext().isPresent()) {
610 return message
.getGroupContext().get();
614 if (content
.getStoryMessage().isPresent()) {
615 var message
= content
.getStoryMessage().get();
616 if (message
.getGroupContext().isPresent()) {
618 return SignalServiceGroupContext
.create(null, message
.getGroupContext().get());
619 } catch (InvalidMessageException e
) {
620 throw new AssertionError(e
);
628 private boolean isMessageBlocked(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
629 SignalServiceAddress source
= getSenderAddress(envelope
, content
);
630 if (source
== null) {
633 final var recipientId
= context
.getRecipientHelper().resolveRecipient(source
);
634 if (context
.getContactHelper().isContactBlocked(recipientId
)) {
638 final var groupContext
= getGroupContext(content
);
639 if (groupContext
!= null) {
640 var groupId
= GroupUtils
.getGroupId(groupContext
);
641 return context
.getGroupHelper().isGroupBlocked(groupId
);
647 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
648 SignalServiceAddress source
= getSenderAddress(envelope
, content
);
649 if (source
== null) {
653 final var groupContext
= getGroupContext(content
);
654 if (groupContext
== null) {
658 if (groupContext
.getGroupV1().isPresent()) {
659 var groupInfo
= groupContext
.getGroupV1().get();
660 if (groupInfo
.getType() == SignalServiceGroup
.Type
.QUIT
) {
665 var groupId
= GroupUtils
.getGroupId(groupContext
);
666 var group
= context
.getGroupHelper().getGroup(groupId
);
671 final var message
= content
.getDataMessage().orElse(null);
673 final var recipientId
= context
.getRecipientHelper().resolveRecipient(source
);
674 if (!group
.isMember(recipientId
) && !(
675 group
.isPendingMember(recipientId
) && message
!= null && message
.isGroupV2Update()
680 if (group
.isAnnouncementGroup() && !group
.isAdmin(recipientId
)) {
681 return message
== null
682 || message
.getBody().isPresent()
683 || message
.getAttachments().isPresent()
684 || message
.getQuote().isPresent()
685 || message
.getPreviews().isPresent()
686 || message
.getMentions().isPresent()
687 || message
.getSticker().isPresent();
692 private List
<HandleAction
> handleSignalServiceDataMessage(
693 SignalServiceDataMessage message
,
695 DeviceAddress source
,
696 DeviceAddress destination
,
697 boolean ignoreAttachments
699 var actions
= new ArrayList
<HandleAction
>();
700 if (message
.getGroupContext().isPresent()) {
701 final var groupContext
= message
.getGroupContext().get();
702 if (groupContext
.getGroupV1().isPresent()) {
703 var groupInfo
= groupContext
.getGroupV1().get();
704 var groupId
= GroupId
.v1(groupInfo
.getGroupId());
705 var group
= context
.getGroupHelper().getGroup(groupId
);
706 if (group
== null || group
instanceof GroupInfoV1
) {
707 var groupV1
= (GroupInfoV1
) group
;
708 switch (groupInfo
.getType()) {
710 if (groupV1
== null) {
711 groupV1
= new GroupInfoV1(groupId
);
714 if (groupInfo
.getAvatar().isPresent()) {
715 var avatar
= groupInfo
.getAvatar().get();
716 context
.getGroupHelper().downloadGroupAvatar(groupV1
.getGroupId(), avatar
);
719 if (groupInfo
.getName().isPresent()) {
720 groupV1
.name
= groupInfo
.getName().get();
723 if (groupInfo
.getMembers().isPresent()) {
724 groupV1
.addMembers(groupInfo
.getMembers()
727 .map(context
.getRecipientHelper()::resolveRecipient
)
728 .collect(Collectors
.toSet()));
731 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
);
747 if (groupV1
!= null && !isSync
) {
748 actions
.add(new SendGroupInfoAction(source
.recipientId(), groupV1
.getGroupId()));
753 // Received a group v1 message for a v2 group
756 if (groupContext
.getGroupV2().isPresent()) {
757 handleGroupV2Context(groupContext
.getGroupV2().get());
761 final var conversationPartnerAddress
= isSync ? destination
: source
;
762 if (conversationPartnerAddress
!= null && message
.isEndSession()) {
763 account
.getAciSessionStore().deleteAllSessions(conversationPartnerAddress
.serviceId());
765 if (message
.isExpirationUpdate() || message
.getBody().isPresent()) {
766 if (message
.getGroupContext().isPresent()) {
767 final var groupContext
= message
.getGroupContext().get();
768 if (groupContext
.getGroupV1().isPresent()) {
769 var groupInfo
= groupContext
.getGroupV1().get();
770 var group
= account
.getGroupStore().getOrCreateGroupV1(GroupId
.v1(groupInfo
.getGroupId()));
772 if (group
.messageExpirationTime
!= message
.getExpiresInSeconds()) {
773 group
.messageExpirationTime
= message
.getExpiresInSeconds();
774 account
.getGroupStore().updateGroup(group
);
777 } else if (groupContext
.getGroupV2().isPresent()) {
778 // disappearing message timer already stored in the DecryptedGroup
780 } else if (conversationPartnerAddress
!= null) {
781 context
.getContactHelper()
782 .setExpirationTimer(conversationPartnerAddress
.recipientId(), message
.getExpiresInSeconds());
785 if (!ignoreAttachments
) {
786 if (message
.getAttachments().isPresent()) {
787 for (var attachment
: message
.getAttachments().get()) {
788 context
.getAttachmentHelper().downloadAttachment(attachment
);
791 if (message
.getSharedContacts().isPresent()) {
792 for (var contact
: message
.getSharedContacts().get()) {
793 if (contact
.getAvatar().isPresent()) {
794 context
.getAttachmentHelper().downloadAttachment(contact
.getAvatar().get().getAttachment());
798 if (message
.getPreviews().isPresent()) {
799 final var previews
= message
.getPreviews().get();
800 for (var preview
: previews
) {
801 if (preview
.getImage().isPresent()) {
802 context
.getAttachmentHelper().downloadAttachment(preview
.getImage().get());
806 if (message
.getQuote().isPresent()) {
807 final var quote
= message
.getQuote().get();
809 for (var quotedAttachment
: quote
.getAttachments()) {
810 final var thumbnail
= quotedAttachment
.getThumbnail();
811 if (thumbnail
!= null) {
812 context
.getAttachmentHelper().downloadAttachment(thumbnail
);
817 if (message
.getProfileKey().isPresent()) {
818 handleIncomingProfileKey(message
.getProfileKey().get(), source
.recipientId());
820 if (message
.getSticker().isPresent()) {
821 final var messageSticker
= message
.getSticker().get();
822 final var stickerPackId
= StickerPackId
.deserialize(messageSticker
.getPackId());
823 var sticker
= account
.getStickerStore().getStickerPack(stickerPackId
);
824 if (sticker
== null) {
825 sticker
= new StickerPack(stickerPackId
, messageSticker
.getPackKey());
826 account
.getStickerStore().addStickerPack(sticker
);
828 context
.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId
, messageSticker
.getPackKey()));
833 private List
<HandleAction
> handleSignalServiceStoryMessage(
834 SignalServiceStoryMessage message
, RecipientId source
, boolean ignoreAttachments
836 var actions
= new ArrayList
<HandleAction
>();
837 if (message
.getGroupContext().isPresent()) {
838 handleGroupV2Context(message
.getGroupContext().get());
841 if (!ignoreAttachments
) {
842 if (message
.getFileAttachment().isPresent()) {
843 context
.getAttachmentHelper().downloadAttachment(message
.getFileAttachment().get());
845 if (message
.getTextAttachment().isPresent()) {
846 final var textAttachment
= message
.getTextAttachment().get();
847 if (textAttachment
.getPreview().isPresent()) {
848 final var preview
= textAttachment
.getPreview().get();
849 if (preview
.getImage().isPresent()) {
850 context
.getAttachmentHelper().downloadAttachment(preview
.getImage().get());
856 if (message
.getProfileKey().isPresent()) {
857 handleIncomingProfileKey(message
.getProfileKey().get(), source
);
863 private void handleGroupV2Context(final SignalServiceGroupV2 groupContext
) {
864 final var groupMasterKey
= groupContext
.getMasterKey();
866 context
.getGroupHelper()
867 .getOrMigrateGroup(groupMasterKey
,
868 groupContext
.getRevision(),
869 groupContext
.hasSignedGroupChange() ? groupContext
.getSignedGroupChange() : null);
872 private void handleIncomingProfileKey(final byte[] profileKeyBytes
, final RecipientId source
) {
873 if (profileKeyBytes
.length
!= 32) {
874 logger
.debug("Received invalid profile key of length {}", profileKeyBytes
.length
);
877 final ProfileKey profileKey
;
879 profileKey
= new ProfileKey(profileKeyBytes
);
880 } catch (InvalidInputException e
) {
881 throw new AssertionError(e
);
883 if (account
.getSelfRecipientId().equals(source
)) {
884 this.account
.setProfileKey(profileKey
);
886 this.account
.getProfileStore().storeProfileKey(source
, profileKey
);
889 private SignalServiceAddress
getSenderAddress(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
890 if (!envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()) {
891 return envelope
.getSourceAddress();
892 } else if (content
!= null) {
893 return content
.getSender();
899 private DeviceAddress
getSender(SignalServiceEnvelope envelope
, SignalServiceContent content
) {
900 if (!envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()) {
901 return new DeviceAddress(context
.getRecipientHelper().resolveRecipient(envelope
.getSourceAddress()),
902 envelope
.getSourceAddress().getServiceId(),
903 envelope
.getSourceDevice());
905 return new DeviceAddress(context
.getRecipientHelper().resolveRecipient(content
.getSender()),
906 content
.getSender().getServiceId(),
907 content
.getSenderDevice());
911 private DeviceAddress
getDestination(SignalServiceEnvelope envelope
) {
912 if (!envelope
.hasDestinationUuid()) {
913 return new DeviceAddress(account
.getSelfRecipientId(), account
.getAci(), account
.getDeviceId());
915 final var addressOptional
= SignalServiceAddress
.fromRaw(envelope
.getDestinationUuid(), null);
916 if (addressOptional
.isEmpty()) {
917 return new DeviceAddress(account
.getSelfRecipientId(), account
.getAci(), account
.getDeviceId());
919 final var address
= addressOptional
.get();
920 return new DeviceAddress(context
.getRecipientHelper().resolveRecipient(address
), address
.getServiceId(), 0);
923 private record DeviceAddress(RecipientId recipientId
, ServiceId serviceId
, int deviceId
) {}