]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
294b17f6120d0d2b4ddc8bf3fe66cc4185c7166a
[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.actions.HandleAction;
5 import org.asamk.signal.manager.actions.RefreshPreKeysAction;
6 import org.asamk.signal.manager.actions.RenewSessionAction;
7 import org.asamk.signal.manager.actions.ResendMessageAction;
8 import org.asamk.signal.manager.actions.RetrieveProfileAction;
9 import org.asamk.signal.manager.actions.SendGroupInfoAction;
10 import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
11 import org.asamk.signal.manager.actions.SendProfileKeyAction;
12 import org.asamk.signal.manager.actions.SendReceiptAction;
13 import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
14 import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
15 import org.asamk.signal.manager.actions.SendSyncConfigurationAction;
16 import org.asamk.signal.manager.actions.SendSyncContactsAction;
17 import org.asamk.signal.manager.actions.SendSyncGroupsAction;
18 import org.asamk.signal.manager.actions.SendSyncKeysAction;
19 import org.asamk.signal.manager.actions.SyncStorageDataAction;
20 import org.asamk.signal.manager.actions.UpdateAccountAttributesAction;
21 import org.asamk.signal.manager.api.GroupId;
22 import org.asamk.signal.manager.api.GroupNotFoundException;
23 import org.asamk.signal.manager.api.MessageEnvelope;
24 import org.asamk.signal.manager.api.Pair;
25 import org.asamk.signal.manager.api.ReceiveConfig;
26 import org.asamk.signal.manager.api.StickerPackId;
27 import org.asamk.signal.manager.api.TrustLevel;
28 import org.asamk.signal.manager.api.UntrustedIdentityException;
29 import org.asamk.signal.manager.groups.GroupUtils;
30 import org.asamk.signal.manager.internal.SignalDependencies;
31 import org.asamk.signal.manager.jobs.RetrieveStickerPackJob;
32 import org.asamk.signal.manager.storage.SignalAccount;
33 import org.asamk.signal.manager.storage.groups.GroupInfoV1;
34 import org.asamk.signal.manager.storage.recipients.RecipientId;
35 import org.asamk.signal.manager.storage.stickers.StickerPack;
36 import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
37 import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
38 import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
39 import org.signal.libsignal.metadata.ProtocolNoSessionException;
40 import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
41 import org.signal.libsignal.metadata.SelfSendException;
42 import org.signal.libsignal.protocol.InvalidMessageException;
43 import org.signal.libsignal.protocol.groups.GroupSessionBuilder;
44 import org.signal.libsignal.protocol.message.DecryptionErrorMessage;
45 import org.signal.libsignal.zkgroup.InvalidInputException;
46 import org.signal.libsignal.zkgroup.profiles.ProfileKey;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.whispersystems.signalservice.api.InvalidMessageStructureException;
50 import org.whispersystems.signalservice.api.crypto.SignalGroupSessionBuilder;
51 import org.whispersystems.signalservice.api.crypto.SignalServiceCipherResult;
52 import org.whispersystems.signalservice.api.messages.EnvelopeContentValidator;
53 import org.whispersystems.signalservice.api.messages.SignalServiceContent;
54 import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
55 import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
56 import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
57 import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
58 import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
59 import org.whispersystems.signalservice.api.messages.SignalServicePniSignatureMessage;
60 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
61 import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
62 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
63 import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
64 import org.whispersystems.signalservice.api.push.ServiceId;
65 import org.whispersystems.signalservice.api.push.ServiceId.ACI;
66 import org.whispersystems.signalservice.api.push.ServiceIdType;
67 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
68 import org.whispersystems.signalservice.internal.push.Envelope;
69 import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
70
71 import java.util.ArrayList;
72 import java.util.List;
73 import java.util.Optional;
74 import java.util.stream.Collectors;
75
76 public final class IncomingMessageHandler {
77
78 private static final Logger logger = LoggerFactory.getLogger(IncomingMessageHandler.class);
79
80 private final SignalAccount account;
81 private final SignalDependencies dependencies;
82 private final Context context;
83
84 public IncomingMessageHandler(final Context context) {
85 this.account = context.getAccount();
86 this.dependencies = context.getDependencies();
87 this.context = context;
88 }
89
90 public Pair<List<HandleAction>, Exception> handleRetryEnvelope(
91 final SignalServiceEnvelope envelope,
92 final ReceiveConfig receiveConfig,
93 final Manager.ReceiveMessageHandler handler
94 ) {
95 final List<HandleAction> actions = new ArrayList<>();
96 if (envelope.isPreKeySignalMessage()) {
97 actions.add(RefreshPreKeysAction.create());
98 }
99
100 SignalServiceContent content = null;
101 if (!envelope.isReceipt()) {
102 account.getIdentityKeyStore().setRetryingDecryption(true);
103 final var destination = getDestination(envelope).serviceId();
104 try {
105 final var cipherResult = dependencies.getCipher(destination == null
106 || destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI)
107 .decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp());
108 content = validate(envelope.getProto(), cipherResult, envelope.getServerDeliveredTimestamp());
109 if (content == null) {
110 return new Pair<>(List.of(), null);
111 }
112 } catch (ProtocolUntrustedIdentityException e) {
113 final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender());
114 final var exception = new UntrustedIdentityException(account.getRecipientAddressResolver()
115 .resolveRecipientAddress(recipientId)
116 .toApiRecipientAddress(), e.getSenderDevice());
117 return new Pair<>(List.of(), exception);
118 } catch (Exception e) {
119 return new Pair<>(List.of(), e);
120 } finally {
121 account.getIdentityKeyStore().setRetryingDecryption(false);
122 }
123 }
124 actions.addAll(checkAndHandleMessage(envelope, content, receiveConfig, handler, null));
125 return new Pair<>(actions, null);
126 }
127
128 public Pair<List<HandleAction>, Exception> handleEnvelope(
129 final SignalServiceEnvelope envelope,
130 final ReceiveConfig receiveConfig,
131 final Manager.ReceiveMessageHandler handler
132 ) {
133 final var actions = new ArrayList<HandleAction>();
134 SignalServiceContent content = null;
135 Exception exception = null;
136 envelope.getSourceServiceId().map(ServiceId::parseOrNull)
137 // Store uuid if we don't have it already
138 // uuid in envelope is sent by server
139 .ifPresent(serviceId -> account.getRecipientResolver().resolveRecipient(serviceId));
140 if (!envelope.isReceipt()) {
141 final var destination = getDestination(envelope).serviceId();
142 try {
143 final var cipherResult = dependencies.getCipher(destination == null
144 || destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI)
145 .decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp());
146 content = validate(envelope.getProto(), cipherResult, envelope.getServerDeliveredTimestamp());
147 if (content == null) {
148 return new Pair<>(List.of(), null);
149 }
150 } catch (ProtocolUntrustedIdentityException e) {
151 final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender());
152 actions.add(new RetrieveProfileAction(recipientId));
153 exception = new UntrustedIdentityException(account.getRecipientAddressResolver()
154 .resolveRecipientAddress(recipientId)
155 .toApiRecipientAddress(), e.getSenderDevice());
156 } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException |
157 ProtocolInvalidMessageException e) {
158 logger.debug("Failed to decrypt incoming message", e);
159 final var sender = account.getRecipientResolver().resolveRecipient(e.getSender());
160 if (context.getContactHelper().isContactBlocked(sender)) {
161 logger.debug("Received invalid message from blocked contact, ignoring.");
162 } else {
163 var serviceId = ServiceId.parseOrNull(e.getSender());
164 if (serviceId != null) {
165 final var isSelf = sender.equals(account.getSelfRecipientId())
166 && e.getSenderDevice() == account.getDeviceId();
167 if (!isSelf) {
168 logger.debug("Received invalid message, requesting message resend.");
169 actions.add(new SendRetryMessageRequestAction(sender, serviceId, e, envelope, destination));
170 } else {
171 logger.debug("Received invalid message, queuing renew session action.");
172 actions.add(new RenewSessionAction(sender, serviceId, destination));
173 }
174 } else {
175 logger.debug("Received invalid message from invalid sender: {}", e.getSender());
176 }
177 }
178 exception = e;
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);
184 exception = e;
185 }
186 }
187
188 actions.addAll(checkAndHandleMessage(envelope, content, receiveConfig, handler, exception));
189 return new Pair<>(actions, exception);
190 }
191
192 private SignalServiceContent validate(
193 Envelope envelope, SignalServiceCipherResult cipherResult, long serverDeliveredTimestamp
194 ) throws ProtocolInvalidKeyException, ProtocolInvalidMessageException, UnsupportedDataMessageException, InvalidMessageStructureException {
195 final var content = cipherResult.getContent();
196 final var envelopeMetadata = cipherResult.getMetadata();
197 final var validationResult = EnvelopeContentValidator.INSTANCE.validate(envelope, content, account.getAci());
198
199 if (validationResult instanceof EnvelopeContentValidator.Result.Invalid v) {
200 logger.warn("Invalid content! {}", v.getReason(), v.getThrowable());
201 return null;
202 }
203
204 if (validationResult instanceof EnvelopeContentValidator.Result.UnsupportedDataMessage v) {
205 logger.warn("Unsupported DataMessage! Our version: {}, their version: {}",
206 v.getOurVersion(),
207 v.getTheirVersion());
208 return null;
209 }
210
211 return SignalServiceContent.Companion.createFrom(account.getNumber(),
212 envelope,
213 envelopeMetadata,
214 content,
215 serverDeliveredTimestamp);
216 }
217
218 private List<HandleAction> checkAndHandleMessage(
219 final SignalServiceEnvelope envelope,
220 final SignalServiceContent content,
221 final ReceiveConfig receiveConfig,
222 final Manager.ReceiveMessageHandler handler,
223 final Exception exception
224 ) {
225 if (content != null) {
226 // Store uuid if we don't have it already
227 // address/uuid is validated by unidentified sender certificate
228
229 boolean handledPniSignature = false;
230 if (content.getPniSignatureMessage().isPresent()) {
231 final var message = content.getPniSignatureMessage().get();
232 final var senderAddress = getSenderAddress(envelope, content);
233 if (senderAddress != null) {
234 handledPniSignature = handlePniSignatureMessage(message, senderAddress);
235 }
236 }
237 if (!handledPniSignature) {
238 account.getRecipientTrustedResolver().resolveRecipientTrusted(content.getSender());
239 }
240 }
241 if (envelope.isReceipt()) {
242 final var senderDeviceAddress = getSender(envelope, content);
243 final var sender = senderDeviceAddress.serviceId();
244 final var senderDeviceId = senderDeviceAddress.deviceId();
245 account.getMessageSendLogStore().deleteEntryForRecipient(envelope.getTimestamp(), sender, senderDeviceId);
246 }
247
248 var notAllowedToSendToGroup = isNotAllowedToSendToGroup(envelope, content);
249 final var groupContext = getGroupContext(content);
250 if (groupContext != null && groupContext.getGroupV2().isPresent()) {
251 handleGroupV2Context(groupContext.getGroupV2().get());
252 }
253 // Check again in case the user just joined the group
254 notAllowedToSendToGroup = notAllowedToSendToGroup && isNotAllowedToSendToGroup(envelope, content);
255
256 if (isMessageBlocked(envelope, content)) {
257 logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
258 return List.of();
259 } else if (notAllowedToSendToGroup) {
260 final var senderAddress = getSenderAddress(envelope, content);
261 logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
262 senderAddress == null ? null : senderAddress.getIdentifier(),
263 envelope.getTimestamp());
264 return List.of();
265 } else {
266 List<HandleAction> actions;
267 if (content != null) {
268 actions = handleMessage(envelope, content, receiveConfig);
269 } else {
270 actions = List.of();
271 }
272 handler.handleMessage(MessageEnvelope.from(envelope,
273 content,
274 account.getRecipientResolver(),
275 account.getRecipientAddressResolver(),
276 context.getAttachmentHelper()::getAttachmentFile,
277 exception), exception);
278 return actions;
279 }
280 }
281
282 public List<HandleAction> handleMessage(
283 SignalServiceEnvelope envelope, SignalServiceContent content, ReceiveConfig receiveConfig
284 ) {
285 var actions = new ArrayList<HandleAction>();
286 final var senderDeviceAddress = getSender(envelope, content);
287 final var sender = senderDeviceAddress.recipientId();
288 final var senderServiceId = senderDeviceAddress.serviceId();
289 final var senderDeviceId = senderDeviceAddress.deviceId();
290 final var destination = getDestination(envelope);
291
292 if (account.getPni().equals(destination.serviceId)) {
293 account.getRecipientStore().markNeedsPniSignature(destination.recipientId, true);
294 } else if (account.getAci().equals(destination.serviceId)) {
295 account.getRecipientStore().markNeedsPniSignature(destination.recipientId, false);
296 }
297
298 if (content.getReceiptMessage().isPresent()) {
299 final var message = content.getReceiptMessage().get();
300 if (message.isDeliveryReceipt()) {
301 account.getMessageSendLogStore()
302 .deleteEntriesForRecipient(message.getTimestamps(), senderServiceId, senderDeviceId);
303 }
304 }
305
306 if (content.getSenderKeyDistributionMessage().isPresent()) {
307 final var message = content.getSenderKeyDistributionMessage().get();
308 final var protocolAddress = senderServiceId.toProtocolAddress(senderDeviceId);
309 logger.debug("Received a sender key distribution message for distributionId {} from {}",
310 message.getDistributionId(),
311 protocolAddress);
312 new SignalGroupSessionBuilder(dependencies.getSessionLock(),
313 new GroupSessionBuilder(account.getSenderKeyStore())).process(protocolAddress, message);
314 }
315
316 if (content.getDecryptionErrorMessage().isPresent()) {
317 var message = content.getDecryptionErrorMessage().get();
318 logger.debug("Received a decryption error message from {}.{} (resend request for {})",
319 sender,
320 senderDeviceId,
321 message.getTimestamp());
322 if (message.getDeviceId() == account.getDeviceId()) {
323 handleDecryptionErrorMessage(actions,
324 sender,
325 senderServiceId,
326 senderDeviceId,
327 message,
328 destination.serviceId());
329 } else {
330 logger.debug("Request is for another one of our devices");
331 }
332 }
333
334 if (content.getDataMessage().isPresent() || content.getEditMessage().isPresent()) {
335 var message = content.getDataMessage().isPresent()
336 ? content.getDataMessage().get()
337 : content.getEditMessage().get().getDataMessage();
338
339 if (content.isNeedsReceipt()) {
340 actions.add(new SendReceiptAction(sender,
341 SignalServiceReceiptMessage.Type.DELIVERY,
342 message.getTimestamp()));
343 } else {
344 // Message wasn't sent as unidentified sender message
345 final var contact = context.getAccount().getContactStore().getContact(sender);
346 if (account.isPrimaryDevice()
347 && contact != null
348 && !contact.isBlocked()
349 && contact.isProfileSharingEnabled()) {
350 actions.add(UpdateAccountAttributesAction.create());
351 actions.add(new SendProfileKeyAction(sender));
352 }
353 }
354 if (receiveConfig.sendReadReceipts()) {
355 actions.add(new SendReceiptAction(sender,
356 SignalServiceReceiptMessage.Type.READ,
357 message.getTimestamp()));
358 }
359
360 actions.addAll(handleSignalServiceDataMessage(message,
361 false,
362 senderDeviceAddress,
363 destination,
364 receiveConfig.ignoreAttachments()));
365 }
366
367 if (content.getStoryMessage().isPresent()) {
368 final var message = content.getStoryMessage().get();
369 actions.addAll(handleSignalServiceStoryMessage(message, sender, receiveConfig.ignoreAttachments()));
370 }
371
372 if (content.getSyncMessage().isPresent()) {
373 var syncMessage = content.getSyncMessage().get();
374 actions.addAll(handleSyncMessage(envelope,
375 syncMessage,
376 senderDeviceAddress,
377 receiveConfig.ignoreAttachments()));
378 }
379
380 return actions;
381 }
382
383 private boolean handlePniSignatureMessage(
384 final SignalServicePniSignatureMessage message, final SignalServiceAddress senderAddress
385 ) {
386 final var aci = senderAddress.getServiceId();
387 final var aciIdentity = account.getIdentityKeyStore().getIdentityInfo(aci);
388 final var pni = message.getPni();
389 final var pniIdentity = account.getIdentityKeyStore().getIdentityInfo(pni);
390
391 if (aciIdentity == null || pniIdentity == null || aci.equals(pni)) {
392 return false;
393 }
394
395 final var verified = pniIdentity.getIdentityKey()
396 .verifyAlternateIdentity(aciIdentity.getIdentityKey(), message.getSignature());
397
398 if (!verified) {
399 logger.debug("Invalid PNI signature of ACI {} with PNI {}", aci, pni);
400 return false;
401 }
402
403 logger.debug("Verified association of ACI {} with PNI {}", aci, pni);
404 account.getRecipientTrustedResolver()
405 .resolveRecipientTrusted(Optional.of(ACI.from(aci.getRawUuid())),
406 Optional.of(pni),
407 senderAddress.getNumber());
408 return true;
409 }
410
411 private void handleDecryptionErrorMessage(
412 final List<HandleAction> actions,
413 final RecipientId sender,
414 final ServiceId senderServiceId,
415 final int senderDeviceId,
416 final DecryptionErrorMessage message,
417 final ServiceId destination
418 ) {
419 final var logEntries = account.getMessageSendLogStore()
420 .findMessages(senderServiceId,
421 senderDeviceId,
422 message.getTimestamp(),
423 message.getRatchetKey().isEmpty());
424
425 for (final var logEntry : logEntries) {
426 actions.add(new ResendMessageAction(sender, message.getTimestamp(), logEntry));
427 }
428
429 if (message.getRatchetKey().isPresent()) {
430 final var sessionStore = account.getAccountData(destination).getSessionStore();
431 if (sessionStore.isCurrentRatchetKey(senderServiceId, senderDeviceId, message.getRatchetKey().get())) {
432 if (logEntries.isEmpty()) {
433 logger.debug("Renewing the session with sender");
434 actions.add(new RenewSessionAction(sender, senderServiceId, destination));
435 } else {
436 logger.trace("Archiving the session with sender, a resend message has already been queued");
437 sessionStore.archiveSessions(senderServiceId);
438 }
439 }
440 return;
441 }
442
443 var found = false;
444 for (final var logEntry : logEntries) {
445 if (logEntry.groupId().isEmpty()) {
446 continue;
447 }
448 final var group = account.getGroupStore().getGroup(logEntry.groupId().get());
449 if (group == null) {
450 continue;
451 }
452 found = true;
453 logger.trace("Deleting shared sender key with {} ({}): {}",
454 sender,
455 senderDeviceId,
456 group.getDistributionId());
457 account.getSenderKeyStore().deleteSharedWith(senderServiceId, senderDeviceId, group.getDistributionId());
458 }
459 if (!found) {
460 logger.debug("Reset all shared sender keys with this recipient, no related message found in send log");
461 account.getSenderKeyStore().deleteSharedWith(senderServiceId);
462 }
463 }
464
465 private List<HandleAction> handleSyncMessage(
466 final SignalServiceEnvelope envelope,
467 final SignalServiceSyncMessage syncMessage,
468 final DeviceAddress sender,
469 final boolean ignoreAttachments
470 ) {
471 var actions = new ArrayList<HandleAction>();
472 account.setMultiDevice(true);
473 if (syncMessage.getSent().isPresent()) {
474 var message = syncMessage.getSent().get();
475 final var destination = message.getDestination().orElse(null);
476 if (message.getDataMessage().isPresent()) {
477 actions.addAll(handleSignalServiceDataMessage(message.getDataMessage().get(),
478 true,
479 sender,
480 destination == null
481 ? null
482 : new DeviceAddress(account.getRecipientResolver().resolveRecipient(destination),
483 destination.getServiceId(),
484 0),
485 ignoreAttachments));
486 }
487 if (message.getStoryMessage().isPresent()) {
488 actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(),
489 sender.recipientId(),
490 ignoreAttachments));
491 }
492 }
493 if (syncMessage.getRequest().isPresent() && account.isPrimaryDevice()) {
494 var rm = syncMessage.getRequest().get();
495 if (rm.isContactsRequest()) {
496 actions.add(SendSyncContactsAction.create());
497 }
498 if (rm.isGroupsRequest()) {
499 actions.add(SendSyncGroupsAction.create());
500 }
501 if (rm.isBlockedListRequest()) {
502 actions.add(SendSyncBlockedListAction.create());
503 }
504 if (rm.isKeysRequest()) {
505 actions.add(SendSyncKeysAction.create());
506 }
507 if (rm.isConfigurationRequest()) {
508 actions.add(SendSyncConfigurationAction.create());
509 }
510 actions.add(SyncStorageDataAction.create());
511 }
512 if (syncMessage.getGroups().isPresent()) {
513 try {
514 final var groupsMessage = syncMessage.getGroups().get();
515 context.getAttachmentHelper()
516 .retrieveAttachment(groupsMessage, context.getSyncHelper()::handleSyncDeviceGroups);
517 } catch (Exception e) {
518 logger.warn("Failed to handle received sync groups, ignoring: {}", e.getMessage());
519 }
520 }
521 if (syncMessage.getBlockedList().isPresent()) {
522 final var blockedListMessage = syncMessage.getBlockedList().get();
523 for (var address : blockedListMessage.getAddresses()) {
524 context.getContactHelper()
525 .setContactBlocked(account.getRecipientResolver().resolveRecipient(address), true);
526 }
527 for (var groupId : blockedListMessage.getGroupIds()
528 .stream()
529 .map(GroupId::unknownVersion)
530 .collect(Collectors.toSet())) {
531 try {
532 context.getGroupHelper().setGroupBlocked(groupId, true);
533 } catch (GroupNotFoundException e) {
534 logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
535 groupId.toBase64());
536 }
537 }
538 }
539 if (syncMessage.getContacts().isPresent()) {
540 try {
541 final var contactsMessage = syncMessage.getContacts().get();
542 context.getAttachmentHelper()
543 .retrieveAttachment(contactsMessage.getContactsStream(),
544 context.getSyncHelper()::handleSyncDeviceContacts);
545 } catch (Exception e) {
546 logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage());
547 }
548 }
549 if (syncMessage.getVerified().isPresent()) {
550 final var verifiedMessage = syncMessage.getVerified().get();
551 account.getIdentityKeyStore()
552 .setIdentityTrustLevel(verifiedMessage.getDestination().getServiceId(),
553 verifiedMessage.getIdentityKey(),
554 TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
555 }
556 if (syncMessage.getStickerPackOperations().isPresent()) {
557 final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
558 for (var m : stickerPackOperationMessages) {
559 if (m.getPackId().isEmpty()) {
560 continue;
561 }
562 final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
563 final var stickerPackKey = m.getPackKey().orElse(null);
564 final var installed = m.getType().isEmpty()
565 || m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
566
567 final var sticker = context.getStickerHelper()
568 .addOrUpdateStickerPack(stickerPackId, stickerPackKey, installed);
569
570 if (sticker != null && installed) {
571 context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, sticker.packKey()));
572 }
573 }
574 }
575 if (syncMessage.getFetchType().isPresent()) {
576 switch (syncMessage.getFetchType().get()) {
577 case LOCAL_PROFILE -> actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
578 case STORAGE_MANIFEST -> actions.add(SyncStorageDataAction.create());
579 }
580 }
581 if (syncMessage.getKeys().isPresent()) {
582 final var keysMessage = syncMessage.getKeys().get();
583 if (keysMessage.getStorageService().isPresent()) {
584 final var storageKey = keysMessage.getStorageService().get();
585 account.setStorageKey(storageKey);
586 actions.add(SyncStorageDataAction.create());
587 }
588 if (keysMessage.getMaster().isPresent()) {
589 final var masterKey = keysMessage.getMaster().get();
590 account.setMasterKey(masterKey);
591 actions.add(SyncStorageDataAction.create());
592 }
593 }
594 if (syncMessage.getConfiguration().isPresent()) {
595 final var configurationMessage = syncMessage.getConfiguration().get();
596 final var configurationStore = account.getConfigurationStore();
597 if (configurationMessage.getReadReceipts().isPresent()) {
598 configurationStore.setReadReceipts(configurationMessage.getReadReceipts().get());
599 }
600 if (configurationMessage.getLinkPreviews().isPresent()) {
601 configurationStore.setLinkPreviews(configurationMessage.getLinkPreviews().get());
602 }
603 if (configurationMessage.getTypingIndicators().isPresent()) {
604 configurationStore.setTypingIndicators(configurationMessage.getTypingIndicators().get());
605 }
606 if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) {
607 configurationStore.setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
608 .get());
609 }
610 }
611 if (syncMessage.getPniChangeNumber().isPresent()) {
612 final var pniChangeNumber = syncMessage.getPniChangeNumber().get();
613 logger.debug("Received PNI change number sync message, applying.");
614 final var updatedPniString = envelope.getUpdatedPni();
615 if (updatedPniString != null && !updatedPniString.isEmpty()) {
616 final var updatedPni = ServiceId.PNI.parseOrThrow(updatedPniString);
617 context.getAccountHelper().handlePniChangeNumberMessage(pniChangeNumber, updatedPni);
618 }
619 }
620 return actions;
621 }
622
623 private SignalServiceGroupContext getGroupContext(SignalServiceContent content) {
624 if (content == null) {
625 return null;
626 }
627
628 if (content.getDataMessage().isPresent()) {
629 var message = content.getDataMessage().get();
630 if (message.getGroupContext().isPresent()) {
631 return message.getGroupContext().get();
632 }
633 }
634
635 if (content.getStoryMessage().isPresent()) {
636 var message = content.getStoryMessage().get();
637 if (message.getGroupContext().isPresent()) {
638 try {
639 return SignalServiceGroupContext.create(null, message.getGroupContext().get());
640 } catch (InvalidMessageException e) {
641 throw new AssertionError(e);
642 }
643 }
644 }
645
646 return null;
647 }
648
649 private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
650 SignalServiceAddress source = getSenderAddress(envelope, content);
651 if (source == null) {
652 return false;
653 }
654 final var recipientId = account.getRecipientResolver().resolveRecipient(source);
655 if (context.getContactHelper().isContactBlocked(recipientId)) {
656 return true;
657 }
658
659 final var groupContext = getGroupContext(content);
660 if (groupContext != null) {
661 var groupId = GroupUtils.getGroupId(groupContext);
662 return context.getGroupHelper().isGroupBlocked(groupId);
663 }
664
665 return false;
666 }
667
668 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
669 SignalServiceAddress source = getSenderAddress(envelope, content);
670 if (source == null) {
671 return false;
672 }
673
674 final var groupContext = getGroupContext(content);
675 if (groupContext == null) {
676 return false;
677 }
678
679 if (groupContext.getGroupV1().isPresent()) {
680 var groupInfo = groupContext.getGroupV1().get();
681 if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) {
682 return false;
683 }
684 }
685
686 var groupId = GroupUtils.getGroupId(groupContext);
687 var group = context.getGroupHelper().getGroup(groupId);
688 if (group == null) {
689 return false;
690 }
691
692 final var message = content.getDataMessage().orElse(null);
693
694 final var recipientId = account.getRecipientResolver().resolveRecipient(source);
695 if (!group.isMember(recipientId) && !(
696 group.isPendingMember(recipientId) && message != null && message.isGroupV2Update()
697 )) {
698 return true;
699 }
700
701 if (group.isAnnouncementGroup() && !group.isAdmin(recipientId)) {
702 return message == null
703 || message.getBody().isPresent()
704 || message.getAttachments().isPresent()
705 || message.getQuote().isPresent()
706 || message.getPreviews().isPresent()
707 || message.getMentions().isPresent()
708 || message.getSticker().isPresent();
709 }
710 return false;
711 }
712
713 private List<HandleAction> handleSignalServiceDataMessage(
714 SignalServiceDataMessage message,
715 boolean isSync,
716 DeviceAddress source,
717 DeviceAddress destination,
718 boolean ignoreAttachments
719 ) {
720 var actions = new ArrayList<HandleAction>();
721 if (message.getGroupContext().isPresent()) {
722 final var groupContext = message.getGroupContext().get();
723 if (groupContext.getGroupV1().isPresent()) {
724 var groupInfo = groupContext.getGroupV1().get();
725 var groupId = GroupId.v1(groupInfo.getGroupId());
726 var group = context.getGroupHelper().getGroup(groupId);
727 if (group == null || group instanceof GroupInfoV1) {
728 var groupV1 = (GroupInfoV1) group;
729 switch (groupInfo.getType()) {
730 case UPDATE -> {
731 if (groupV1 == null) {
732 groupV1 = new GroupInfoV1(groupId);
733 }
734
735 if (groupInfo.getAvatar().isPresent()) {
736 var avatar = groupInfo.getAvatar().get();
737 context.getGroupHelper().downloadGroupAvatar(groupV1.getGroupId(), avatar);
738 }
739
740 if (groupInfo.getName().isPresent()) {
741 groupV1.name = groupInfo.getName().get();
742 }
743
744 if (groupInfo.getMembers().isPresent()) {
745 final var recipientResolver = account.getRecipientResolver();
746 groupV1.addMembers(groupInfo.getMembers()
747 .get()
748 .stream()
749 .map(recipientResolver::resolveRecipient)
750 .collect(Collectors.toSet()));
751 }
752
753 account.getGroupStore().updateGroup(groupV1);
754 }
755 case DELIVER -> {
756 if (groupV1 == null && !isSync) {
757 actions.add(new SendGroupInfoRequestAction(source.recipientId(), groupId));
758 }
759 }
760 case QUIT -> {
761 if (groupV1 != null) {
762 groupV1.removeMember(source.recipientId());
763 account.getGroupStore().updateGroup(groupV1);
764 }
765 }
766 case REQUEST_INFO -> {
767 if (groupV1 != null && !isSync) {
768 actions.add(new SendGroupInfoAction(source.recipientId(), groupV1.getGroupId()));
769 }
770 }
771 }
772 } else {
773 // Received a group v1 message for a v2 group
774 }
775 }
776 if (groupContext.getGroupV2().isPresent()) {
777 handleGroupV2Context(groupContext.getGroupV2().get());
778 }
779 }
780
781 final var selfAddress = isSync ? source : destination;
782 final var conversationPartnerAddress = isSync ? destination : source;
783 if (conversationPartnerAddress != null && message.isEndSession()) {
784 account.getAccountData(selfAddress.serviceId())
785 .getSessionStore()
786 .deleteAllSessions(conversationPartnerAddress.serviceId());
787 }
788 if (message.isExpirationUpdate() || message.getBody().isPresent()) {
789 if (message.getGroupContext().isPresent()) {
790 final var groupContext = message.getGroupContext().get();
791 if (groupContext.getGroupV1().isPresent()) {
792 var groupInfo = groupContext.getGroupV1().get();
793 var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
794 if (group != null) {
795 if (group.messageExpirationTime != message.getExpiresInSeconds()) {
796 group.messageExpirationTime = message.getExpiresInSeconds();
797 account.getGroupStore().updateGroup(group);
798 }
799 }
800 } else if (groupContext.getGroupV2().isPresent()) {
801 // disappearing message timer already stored in the DecryptedGroup
802 }
803 } else if (conversationPartnerAddress != null) {
804 context.getContactHelper()
805 .setExpirationTimer(conversationPartnerAddress.recipientId(),
806 message.getExpiresInSeconds(),
807 message.getExpireTimerVersion());
808 }
809 }
810 if (!ignoreAttachments) {
811 if (message.getAttachments().isPresent()) {
812 for (var attachment : message.getAttachments().get()) {
813 context.getAttachmentHelper().downloadAttachment(attachment);
814 }
815 }
816 if (message.getSharedContacts().isPresent()) {
817 for (var contact : message.getSharedContacts().get()) {
818 if (contact.getAvatar().isPresent()) {
819 context.getAttachmentHelper().downloadAttachment(contact.getAvatar().get().getAttachment());
820 }
821 }
822 }
823 if (message.getPreviews().isPresent()) {
824 final var previews = message.getPreviews().get();
825 for (var preview : previews) {
826 if (preview.getImage().isPresent()) {
827 context.getAttachmentHelper().downloadAttachment(preview.getImage().get());
828 }
829 }
830 }
831 if (message.getQuote().isPresent()) {
832 final var quote = message.getQuote().get();
833
834 if (quote.getAttachments() != null) {
835 for (var quotedAttachment : quote.getAttachments()) {
836 final var thumbnail = quotedAttachment.getThumbnail();
837 if (thumbnail != null) {
838 context.getAttachmentHelper().downloadAttachment(thumbnail);
839 }
840 }
841 }
842 }
843 }
844 if (message.getGiftBadge().isPresent()) {
845 handleIncomingGiftBadge(message.getGiftBadge().get());
846 }
847 if (message.getProfileKey().isPresent()) {
848 handleIncomingProfileKey(message.getProfileKey().get(), source.recipientId());
849 }
850 if (message.getSticker().isPresent()) {
851 final var messageSticker = message.getSticker().get();
852 final var stickerPackId = StickerPackId.deserialize(messageSticker.getPackId());
853 var sticker = account.getStickerStore().getStickerPack(stickerPackId);
854 if (sticker == null) {
855 sticker = new StickerPack(stickerPackId, messageSticker.getPackKey());
856 account.getStickerStore().addStickerPack(sticker);
857 }
858 context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey()));
859 }
860 return actions;
861 }
862
863 private void handleIncomingGiftBadge(final SignalServiceDataMessage.GiftBadge giftBadge) {
864 // TODO
865 }
866
867 private List<HandleAction> handleSignalServiceStoryMessage(
868 SignalServiceStoryMessage message, RecipientId source, boolean ignoreAttachments
869 ) {
870 var actions = new ArrayList<HandleAction>();
871 if (message.getGroupContext().isPresent()) {
872 handleGroupV2Context(message.getGroupContext().get());
873 }
874
875 if (!ignoreAttachments) {
876 if (message.getFileAttachment().isPresent()) {
877 context.getAttachmentHelper().downloadAttachment(message.getFileAttachment().get());
878 }
879 if (message.getTextAttachment().isPresent()) {
880 final var textAttachment = message.getTextAttachment().get();
881 if (textAttachment.getPreview().isPresent()) {
882 final var preview = textAttachment.getPreview().get();
883 if (preview.getImage().isPresent()) {
884 context.getAttachmentHelper().downloadAttachment(preview.getImage().get());
885 }
886 }
887 }
888 }
889
890 if (message.getProfileKey().isPresent()) {
891 handleIncomingProfileKey(message.getProfileKey().get(), source);
892 }
893
894 return actions;
895 }
896
897 private void handleGroupV2Context(final SignalServiceGroupV2 groupContext) {
898 final var groupMasterKey = groupContext.getMasterKey();
899
900 context.getGroupHelper()
901 .getOrMigrateGroup(groupMasterKey,
902 groupContext.getRevision(),
903 groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
904 }
905
906 private void handleIncomingProfileKey(final byte[] profileKeyBytes, final RecipientId source) {
907 if (profileKeyBytes.length != 32) {
908 logger.debug("Received invalid profile key of length {}", profileKeyBytes.length);
909 return;
910 }
911 final ProfileKey profileKey;
912 try {
913 profileKey = new ProfileKey(profileKeyBytes);
914 } catch (InvalidInputException e) {
915 throw new AssertionError(e);
916 }
917 if (account.getSelfRecipientId().equals(source)) {
918 this.account.setProfileKey(profileKey);
919 }
920 this.account.getProfileStore().storeProfileKey(source, profileKey);
921 }
922
923 private SignalServiceAddress getSenderAddress(SignalServiceEnvelope envelope, SignalServiceContent content) {
924 final var serviceId = envelope.getSourceServiceId().map(ServiceId::parseOrNull).orElse(null);
925 if (!envelope.isUnidentifiedSender() && serviceId != null) {
926 return new SignalServiceAddress(serviceId);
927 } else if (content != null) {
928 return content.getSender();
929 } else {
930 return null;
931 }
932 }
933
934 private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
935 final var serviceId = envelope.getSourceServiceId().map(ServiceId::parseOrNull).orElse(null);
936 if (!envelope.isUnidentifiedSender() && serviceId != null) {
937 return new DeviceAddress(account.getRecipientResolver().resolveRecipient(serviceId),
938 serviceId,
939 envelope.getSourceDevice());
940 } else {
941 return new DeviceAddress(account.getRecipientResolver().resolveRecipient(content.getSender()),
942 content.getSender().getServiceId(),
943 content.getSenderDevice());
944 }
945 }
946
947 private DeviceAddress getDestination(SignalServiceEnvelope envelope) {
948 final var destination = envelope.getDestinationServiceId();
949 if (destination == null) {
950 return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
951 }
952 return new DeviceAddress(account.getRecipientResolver().resolveRecipient(destination),
953 destination,
954 account.getDeviceId());
955 }
956
957 private record DeviceAddress(RecipientId recipientId, ServiceId serviceId, int deviceId) {}
958 }