]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
Add PNI to recipients
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / IncomingMessageHandler.java
1 package org.asamk.signal.manager.helper;
2
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;
68
69 import java.util.ArrayList;
70 import java.util.List;
71 import java.util.Optional;
72 import java.util.stream.Collectors;
73
74 public final class IncomingMessageHandler {
75
76 private final static Logger logger = LoggerFactory.getLogger(IncomingMessageHandler.class);
77
78 private final SignalAccount account;
79 private final SignalDependencies dependencies;
80 private final Context context;
81
82 public IncomingMessageHandler(final Context context) {
83 this.account = context.getAccount();
84 this.dependencies = context.getDependencies();
85 this.context = context;
86 }
87
88 public Pair<List<HandleAction>, Exception> handleRetryEnvelope(
89 final SignalServiceEnvelope envelope,
90 final ReceiveConfig receiveConfig,
91 final Manager.ReceiveMessageHandler handler
92 ) {
93 final List<HandleAction> actions = new ArrayList<>();
94 if (envelope.isPreKeySignalMessage()) {
95 actions.add(RefreshPreKeysAction.create());
96 }
97
98 SignalServiceContent content = null;
99 if (!envelope.isReceipt()) {
100 account.getIdentityKeyStore().setRetryingDecryption(true);
101 try {
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);
111 } finally {
112 account.getIdentityKeyStore().setRetryingDecryption(false);
113 }
114 }
115 actions.addAll(checkAndHandleMessage(envelope, content, receiveConfig, handler, null));
116 return new Pair<>(actions, null);
117 }
118
119 public Pair<List<HandleAction>, Exception> handleEnvelope(
120 final SignalServiceEnvelope envelope,
121 final ReceiveConfig receiveConfig,
122 final Manager.ReceiveMessageHandler handler
123 ) {
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());
129 }
130 SignalServiceContent content = null;
131 Exception exception = null;
132 if (!envelope.isReceipt()) {
133 try {
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.");
147 } else {
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)
155 .serviceId()
156 .orElse(null);
157 }
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));
168 } else {
169 logger.debug("Received invalid message, queuing renew session action.");
170 actions.add(new RenewSessionAction(sender, serviceId));
171 }
172 } else {
173 logger.debug("Received invalid message from invalid sender: {}", e.getSender());
174 }
175 }
176 exception = e;
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);
182 exception = e;
183 }
184 }
185
186 actions.addAll(checkAndHandleMessage(envelope, content, receiveConfig, handler, exception));
187 return new Pair<>(actions, exception);
188 }
189
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
196 ) {
197 if (content != null) {
198 // Store uuid if we don't have it already
199 // address/uuid is validated by unidentified sender certificate
200
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);
207 }
208 }
209 if (!handledPniSignature) {
210 account.getRecipientTrustedResolver().resolveRecipientTrusted(content.getSender());
211 }
212 }
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);
218 }
219
220 var notAllowedToSendToGroup = isNotAllowedToSendToGroup(envelope, content);
221 final var groupContext = getGroupContext(content);
222 if (groupContext != null && groupContext.getGroupV2().isPresent()) {
223 handleGroupV2Context(groupContext.getGroupV2().get());
224 }
225 // Check again in case the user just joined the group
226 notAllowedToSendToGroup = notAllowedToSendToGroup && isNotAllowedToSendToGroup(envelope, content);
227
228 if (isMessageBlocked(envelope, content)) {
229 logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
230 return List.of();
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());
236 return List.of();
237 } else {
238 List<HandleAction> actions;
239 if (content != null) {
240 actions = handleMessage(envelope, content, receiveConfig);
241 } else {
242 actions = List.of();
243 }
244 handler.handleMessage(MessageEnvelope.from(envelope,
245 content,
246 account.getRecipientResolver(),
247 account.getRecipientAddressResolver(),
248 context.getAttachmentHelper()::getAttachmentFile,
249 exception), exception);
250 return actions;
251 }
252 }
253
254 public List<HandleAction> handleMessage(
255 SignalServiceEnvelope envelope, SignalServiceContent content, ReceiveConfig receiveConfig
256 ) {
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);
263
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);
269 }
270 }
271
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(),
277 protocolAddress);
278 dependencies.getMessageSender().processSenderKeyDistributionMessage(protocolAddress, message);
279 }
280
281 if (content.getDecryptionErrorMessage().isPresent()) {
282 var message = content.getDecryptionErrorMessage().get();
283 logger.debug("Received a decryption error message from {}.{} (resend request for {})",
284 sender,
285 senderDeviceId,
286 message.getTimestamp());
287 if (message.getDeviceId() == account.getDeviceId()) {
288 handleDecryptionErrorMessage(actions, sender, senderServiceId, senderDeviceId, message);
289 } else {
290 logger.debug("Request is for another one of our devices");
291 }
292 }
293
294 if (content.getDataMessage().isPresent()) {
295 var message = content.getDataMessage().get();
296
297 if (content.isNeedsReceipt()) {
298 actions.add(new SendReceiptAction(sender,
299 SignalServiceReceiptMessage.Type.DELIVERY,
300 message.getTimestamp()));
301 } else {
302 // Message wasn't sent as unidentified sender message
303 final var contact = context.getAccount().getContactStore().getContact(sender);
304 if (account.isPrimaryDevice()
305 && contact != null
306 && !contact.isBlocked()
307 && contact.isProfileSharingEnabled()) {
308 actions.add(UpdateAccountAttributesAction.create());
309 actions.add(new SendProfileKeyAction(sender));
310 }
311 }
312 if (receiveConfig.sendReadReceipts()) {
313 actions.add(new SendReceiptAction(sender,
314 SignalServiceReceiptMessage.Type.READ,
315 message.getTimestamp()));
316 }
317
318 actions.addAll(handleSignalServiceDataMessage(message,
319 false,
320 senderDeviceAddress,
321 destination,
322 receiveConfig.ignoreAttachments()));
323 }
324
325 if (content.getStoryMessage().isPresent()) {
326 final var message = content.getStoryMessage().get();
327 actions.addAll(handleSignalServiceStoryMessage(message, sender, receiveConfig.ignoreAttachments()));
328 }
329
330 if (content.getSyncMessage().isPresent()) {
331 var syncMessage = content.getSyncMessage().get();
332 actions.addAll(handleSyncMessage(envelope,
333 syncMessage,
334 senderDeviceAddress,
335 receiveConfig.ignoreAttachments()));
336 }
337
338 return actions;
339 }
340
341 private boolean handlePniSignatureMessage(
342 final SignalServicePniSignatureMessage message, final SignalServiceAddress senderAddress
343 ) {
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);
348
349 if (aciIdentity == null || pniIdentity == null || aci.equals(pni)) {
350 return false;
351 }
352
353 final var verified = pniIdentity.getIdentityKey()
354 .verifyAlternateIdentity(aciIdentity.getIdentityKey(), message.getSignature());
355
356 if (!verified) {
357 logger.debug("Invalid PNI signature of ACI {} with PNI {}", aci, pni);
358 return false;
359 }
360
361 logger.debug("Verified association of ACI {} with PNI {}", aci, pni);
362 account.getRecipientTrustedResolver()
363 .resolveRecipientTrusted(Optional.of(aci), Optional.of(pni), senderAddress.getNumber());
364 return true;
365 }
366
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
373 ) {
374 final var logEntries = account.getMessageSendLogStore()
375 .findMessages(senderServiceId,
376 senderDeviceId,
377 message.getTimestamp(),
378 message.getRatchetKey().isEmpty());
379
380 for (final var logEntry : logEntries) {
381 actions.add(new ResendMessageAction(sender, message.getTimestamp(), logEntry));
382 }
383
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));
390 } else {
391 logger.trace("Archiving the session with sender, a resend message has already been queued");
392 context.getAccount().getAciSessionStore().archiveSessions(senderServiceId);
393 }
394 }
395 return;
396 }
397
398 var found = false;
399 for (final var logEntry : logEntries) {
400 if (logEntry.groupId().isEmpty()) {
401 continue;
402 }
403 final var group = account.getGroupStore().getGroup(logEntry.groupId().get());
404 if (group == null) {
405 continue;
406 }
407 found = true;
408 logger.trace("Deleting shared sender key with {} ({}): {}",
409 sender,
410 senderDeviceId,
411 group.getDistributionId());
412 account.getSenderKeyStore().deleteSharedWith(senderServiceId, senderDeviceId, group.getDistributionId());
413 }
414 if (!found) {
415 logger.debug("Reset all shared sender keys with this recipient, no related message found in send log");
416 account.getSenderKeyStore().deleteSharedWith(senderServiceId);
417 }
418 }
419
420 private List<HandleAction> handleSyncMessage(
421 final SignalServiceEnvelope envelope,
422 final SignalServiceSyncMessage syncMessage,
423 final DeviceAddress sender,
424 final boolean ignoreAttachments
425 ) {
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(),
433 true,
434 sender,
435 destination == null
436 ? null
437 : new DeviceAddress(context.getRecipientHelper().resolveRecipient(destination),
438 destination.getServiceId(),
439 0),
440 ignoreAttachments));
441 }
442 if (message.getStoryMessage().isPresent()) {
443 actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(),
444 sender.recipientId(),
445 ignoreAttachments));
446 }
447 }
448 if (syncMessage.getRequest().isPresent() && account.isPrimaryDevice()) {
449 var rm = syncMessage.getRequest().get();
450 if (rm.isContactsRequest()) {
451 actions.add(SendSyncContactsAction.create());
452 }
453 if (rm.isGroupsRequest()) {
454 actions.add(SendSyncGroupsAction.create());
455 }
456 if (rm.isBlockedListRequest()) {
457 actions.add(SendSyncBlockedListAction.create());
458 }
459 if (rm.isKeysRequest()) {
460 actions.add(SendSyncKeysAction.create());
461 }
462 if (rm.isConfigurationRequest()) {
463 actions.add(SendSyncConfigurationAction.create());
464 }
465 if (rm.isPniIdentityRequest()) {
466 actions.add(SendPniIdentityKeyAction.create());
467 }
468 }
469 if (syncMessage.getGroups().isPresent()) {
470 try {
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());
476 }
477 }
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);
483 }
484 for (var groupId : blockedListMessage.getGroupIds()
485 .stream()
486 .map(GroupId::unknownVersion)
487 .collect(Collectors.toSet())) {
488 try {
489 context.getGroupHelper().setGroupBlocked(groupId, true);
490 } catch (GroupNotFoundException e) {
491 logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
492 groupId.toBase64());
493 }
494 }
495 }
496 if (syncMessage.getContacts().isPresent()) {
497 try {
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());
504 }
505 }
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()));
512 }
513 if (syncMessage.getStickerPackOperations().isPresent()) {
514 final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
515 for (var m : stickerPackOperationMessages) {
516 if (m.getPackId().isEmpty()) {
517 continue;
518 }
519 final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
520 final var installed = m.getType().isEmpty()
521 || m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
522
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);
528 }
529 if (installed) {
530 context.getJobExecutor()
531 .enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
532 }
533 }
534
535 if (sticker != null && sticker.isInstalled() != installed) {
536 account.getStickerStore().updateStickerPackInstalled(sticker.packId(), installed);
537 }
538 }
539 }
540 if (syncMessage.getFetchType().isPresent()) {
541 switch (syncMessage.getFetchType().get()) {
542 case LOCAL_PROFILE:
543 actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
544 case STORAGE_MANIFEST:
545 actions.add(RetrieveStorageDataAction.create());
546 }
547 }
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());
554 }
555 }
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());
561 }
562 if (configurationMessage.getLinkPreviews().isPresent()) {
563 configurationStore.setLinkPreviews(configurationMessage.getLinkPreviews().get());
564 }
565 if (configurationMessage.getTypingIndicators().isPresent()) {
566 configurationStore.setTypingIndicators(configurationMessage.getTypingIndicators().get());
567 }
568 if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) {
569 configurationStore.setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
570 .get());
571 }
572 }
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());
578 }
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());
587 try {
588 final var updatedPni = PNI.parseOrThrow(envelope.getUpdatedPni());
589 context.getAccountHelper()
590 .setPni(updatedPni,
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);
596 }
597 }
598 }
599 return actions;
600 }
601
602 private SignalServiceGroupContext getGroupContext(SignalServiceContent content) {
603 if (content == null) {
604 return null;
605 }
606
607 if (content.getDataMessage().isPresent()) {
608 var message = content.getDataMessage().get();
609 if (message.getGroupContext().isPresent()) {
610 return message.getGroupContext().get();
611 }
612 }
613
614 if (content.getStoryMessage().isPresent()) {
615 var message = content.getStoryMessage().get();
616 if (message.getGroupContext().isPresent()) {
617 try {
618 return SignalServiceGroupContext.create(null, message.getGroupContext().get());
619 } catch (InvalidMessageException e) {
620 throw new AssertionError(e);
621 }
622 }
623 }
624
625 return null;
626 }
627
628 private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
629 SignalServiceAddress source = getSenderAddress(envelope, content);
630 if (source == null) {
631 return false;
632 }
633 final var recipientId = context.getRecipientHelper().resolveRecipient(source);
634 if (context.getContactHelper().isContactBlocked(recipientId)) {
635 return true;
636 }
637
638 final var groupContext = getGroupContext(content);
639 if (groupContext != null) {
640 var groupId = GroupUtils.getGroupId(groupContext);
641 return context.getGroupHelper().isGroupBlocked(groupId);
642 }
643
644 return false;
645 }
646
647 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
648 SignalServiceAddress source = getSenderAddress(envelope, content);
649 if (source == null) {
650 return false;
651 }
652
653 final var groupContext = getGroupContext(content);
654 if (groupContext == null) {
655 return false;
656 }
657
658 if (groupContext.getGroupV1().isPresent()) {
659 var groupInfo = groupContext.getGroupV1().get();
660 if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) {
661 return false;
662 }
663 }
664
665 var groupId = GroupUtils.getGroupId(groupContext);
666 var group = context.getGroupHelper().getGroup(groupId);
667 if (group == null) {
668 return false;
669 }
670
671 final var message = content.getDataMessage().orElse(null);
672
673 final var recipientId = context.getRecipientHelper().resolveRecipient(source);
674 if (!group.isMember(recipientId) && !(
675 group.isPendingMember(recipientId) && message != null && message.isGroupV2Update()
676 )) {
677 return true;
678 }
679
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();
688 }
689 return false;
690 }
691
692 private List<HandleAction> handleSignalServiceDataMessage(
693 SignalServiceDataMessage message,
694 boolean isSync,
695 DeviceAddress source,
696 DeviceAddress destination,
697 boolean ignoreAttachments
698 ) {
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()) {
709 case UPDATE: {
710 if (groupV1 == null) {
711 groupV1 = new GroupInfoV1(groupId);
712 }
713
714 if (groupInfo.getAvatar().isPresent()) {
715 var avatar = groupInfo.getAvatar().get();
716 context.getGroupHelper().downloadGroupAvatar(groupV1.getGroupId(), avatar);
717 }
718
719 if (groupInfo.getName().isPresent()) {
720 groupV1.name = groupInfo.getName().get();
721 }
722
723 if (groupInfo.getMembers().isPresent()) {
724 groupV1.addMembers(groupInfo.getMembers()
725 .get()
726 .stream()
727 .map(context.getRecipientHelper()::resolveRecipient)
728 .collect(Collectors.toSet()));
729 }
730
731 account.getGroupStore().updateGroup(groupV1);
732 break;
733 }
734 case DELIVER:
735 if (groupV1 == null && !isSync) {
736 actions.add(new SendGroupInfoRequestAction(source.recipientId(), groupId));
737 }
738 break;
739 case QUIT: {
740 if (groupV1 != null) {
741 groupV1.removeMember(source.recipientId());
742 account.getGroupStore().updateGroup(groupV1);
743 }
744 break;
745 }
746 case REQUEST_INFO:
747 if (groupV1 != null && !isSync) {
748 actions.add(new SendGroupInfoAction(source.recipientId(), groupV1.getGroupId()));
749 }
750 break;
751 }
752 } else {
753 // Received a group v1 message for a v2 group
754 }
755 }
756 if (groupContext.getGroupV2().isPresent()) {
757 handleGroupV2Context(groupContext.getGroupV2().get());
758 }
759 }
760
761 final var conversationPartnerAddress = isSync ? destination : source;
762 if (conversationPartnerAddress != null && message.isEndSession()) {
763 account.getAciSessionStore().deleteAllSessions(conversationPartnerAddress.serviceId());
764 }
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()));
771 if (group != null) {
772 if (group.messageExpirationTime != message.getExpiresInSeconds()) {
773 group.messageExpirationTime = message.getExpiresInSeconds();
774 account.getGroupStore().updateGroup(group);
775 }
776 }
777 } else if (groupContext.getGroupV2().isPresent()) {
778 // disappearing message timer already stored in the DecryptedGroup
779 }
780 } else if (conversationPartnerAddress != null) {
781 context.getContactHelper()
782 .setExpirationTimer(conversationPartnerAddress.recipientId(), message.getExpiresInSeconds());
783 }
784 }
785 if (!ignoreAttachments) {
786 if (message.getAttachments().isPresent()) {
787 for (var attachment : message.getAttachments().get()) {
788 context.getAttachmentHelper().downloadAttachment(attachment);
789 }
790 }
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());
795 }
796 }
797 }
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());
803 }
804 }
805 }
806 if (message.getQuote().isPresent()) {
807 final var quote = message.getQuote().get();
808
809 for (var quotedAttachment : quote.getAttachments()) {
810 final var thumbnail = quotedAttachment.getThumbnail();
811 if (thumbnail != null) {
812 context.getAttachmentHelper().downloadAttachment(thumbnail);
813 }
814 }
815 }
816 }
817 if (message.getProfileKey().isPresent()) {
818 handleIncomingProfileKey(message.getProfileKey().get(), source.recipientId());
819 }
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);
827 }
828 context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey()));
829 }
830 return actions;
831 }
832
833 private List<HandleAction> handleSignalServiceStoryMessage(
834 SignalServiceStoryMessage message, RecipientId source, boolean ignoreAttachments
835 ) {
836 var actions = new ArrayList<HandleAction>();
837 if (message.getGroupContext().isPresent()) {
838 handleGroupV2Context(message.getGroupContext().get());
839 }
840
841 if (!ignoreAttachments) {
842 if (message.getFileAttachment().isPresent()) {
843 context.getAttachmentHelper().downloadAttachment(message.getFileAttachment().get());
844 }
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());
851 }
852 }
853 }
854 }
855
856 if (message.getProfileKey().isPresent()) {
857 handleIncomingProfileKey(message.getProfileKey().get(), source);
858 }
859
860 return actions;
861 }
862
863 private void handleGroupV2Context(final SignalServiceGroupV2 groupContext) {
864 final var groupMasterKey = groupContext.getMasterKey();
865
866 context.getGroupHelper()
867 .getOrMigrateGroup(groupMasterKey,
868 groupContext.getRevision(),
869 groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
870 }
871
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);
875 return;
876 }
877 final ProfileKey profileKey;
878 try {
879 profileKey = new ProfileKey(profileKeyBytes);
880 } catch (InvalidInputException e) {
881 throw new AssertionError(e);
882 }
883 if (account.getSelfRecipientId().equals(source)) {
884 this.account.setProfileKey(profileKey);
885 }
886 this.account.getProfileStore().storeProfileKey(source, profileKey);
887 }
888
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();
894 } else {
895 return null;
896 }
897 }
898
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());
904 } else {
905 return new DeviceAddress(context.getRecipientHelper().resolveRecipient(content.getSender()),
906 content.getSender().getServiceId(),
907 content.getSenderDevice());
908 }
909 }
910
911 private DeviceAddress getDestination(SignalServiceEnvelope envelope) {
912 if (!envelope.hasDestinationUuid()) {
913 return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
914 }
915 final var addressOptional = SignalServiceAddress.fromRaw(envelope.getDestinationUuid(), null);
916 if (addressOptional.isEmpty()) {
917 return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
918 }
919 final var address = addressOptional.get();
920 return new DeviceAddress(context.getRecipientHelper().resolveRecipient(address), address.getServiceId(), 0);
921 }
922
923 private record DeviceAddress(RecipientId recipientId, ServiceId serviceId, int deviceId) {}
924 }