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