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