]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
Refactor ReceiveCommand in dbus mode and remove ExtendedDbusCommand
[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.JobExecutor;
4 import org.asamk.signal.manager.Manager;
5 import org.asamk.signal.manager.SignalDependencies;
6 import org.asamk.signal.manager.TrustLevel;
7 import org.asamk.signal.manager.UntrustedIdentityException;
8 import org.asamk.signal.manager.actions.HandleAction;
9 import org.asamk.signal.manager.actions.RefreshPreKeysAction;
10 import org.asamk.signal.manager.actions.RenewSessionAction;
11 import org.asamk.signal.manager.actions.RetrieveProfileAction;
12 import org.asamk.signal.manager.actions.RetrieveStorageDataAction;
13 import org.asamk.signal.manager.actions.SendGroupInfoAction;
14 import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
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.api.MessageEnvelope;
23 import org.asamk.signal.manager.api.Pair;
24 import org.asamk.signal.manager.groups.GroupId;
25 import org.asamk.signal.manager.groups.GroupNotFoundException;
26 import org.asamk.signal.manager.groups.GroupUtils;
27 import org.asamk.signal.manager.jobs.RetrieveStickerPackJob;
28 import org.asamk.signal.manager.storage.SignalAccount;
29 import org.asamk.signal.manager.storage.groups.GroupInfoV1;
30 import org.asamk.signal.manager.storage.recipients.Profile;
31 import org.asamk.signal.manager.storage.recipients.RecipientId;
32 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
33 import org.asamk.signal.manager.storage.stickers.Sticker;
34 import org.asamk.signal.manager.storage.stickers.StickerPackId;
35 import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
36 import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
37 import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
38 import org.signal.libsignal.metadata.ProtocolNoSessionException;
39 import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
40 import org.signal.libsignal.metadata.SelfSendException;
41 import org.signal.zkgroup.InvalidInputException;
42 import org.signal.zkgroup.profiles.ProfileKey;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.whispersystems.libsignal.SignalProtocolAddress;
46 import org.whispersystems.signalservice.api.messages.SignalServiceContent;
47 import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
48 import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
49 import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
50 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
51 import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
52 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
53
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.stream.Collectors;
57
58 public final class IncomingMessageHandler {
59
60 private final static Logger logger = LoggerFactory.getLogger(IncomingMessageHandler.class);
61
62 private final SignalAccount account;
63 private final SignalDependencies dependencies;
64 private final RecipientResolver recipientResolver;
65 private final SignalServiceAddressResolver addressResolver;
66 private final GroupHelper groupHelper;
67 private final ContactHelper contactHelper;
68 private final AttachmentHelper attachmentHelper;
69 private final SyncHelper syncHelper;
70 private final ProfileProvider profileProvider;
71 private final JobExecutor jobExecutor;
72
73 public IncomingMessageHandler(
74 final SignalAccount account,
75 final SignalDependencies dependencies,
76 final RecipientResolver recipientResolver,
77 final SignalServiceAddressResolver addressResolver,
78 final GroupHelper groupHelper,
79 final ContactHelper contactHelper,
80 final AttachmentHelper attachmentHelper,
81 final SyncHelper syncHelper,
82 final ProfileProvider profileProvider,
83 final JobExecutor jobExecutor
84 ) {
85 this.account = account;
86 this.dependencies = dependencies;
87 this.recipientResolver = recipientResolver;
88 this.addressResolver = addressResolver;
89 this.groupHelper = groupHelper;
90 this.contactHelper = contactHelper;
91 this.attachmentHelper = attachmentHelper;
92 this.syncHelper = syncHelper;
93 this.profileProvider = profileProvider;
94 this.jobExecutor = jobExecutor;
95 }
96
97 public Pair<List<HandleAction>, Exception> handleRetryEnvelope(
98 final SignalServiceEnvelope envelope,
99 final boolean ignoreAttachments,
100 final Manager.ReceiveMessageHandler handler
101 ) {
102 final List<HandleAction> actions = new ArrayList<>();
103 if (envelope.isPreKeySignalMessage()) {
104 actions.add(RefreshPreKeysAction.create());
105 }
106
107 SignalServiceContent content = null;
108 if (!envelope.isReceipt()) {
109 try {
110 content = dependencies.getCipher().decrypt(envelope);
111 } catch (ProtocolUntrustedIdentityException e) {
112 final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender());
113 final var exception = new UntrustedIdentityException(account.getRecipientStore()
114 .resolveRecipientAddress(recipientId), e.getSenderDevice());
115 return new Pair<>(List.of(), exception);
116 } catch (Exception e) {
117 return new Pair<>(List.of(), e);
118 }
119 }
120 actions.addAll(checkAndHandleMessage(envelope, content, ignoreAttachments, handler, null));
121 return new Pair<>(actions, null);
122 }
123
124 public Pair<List<HandleAction>, Exception> handleEnvelope(
125 final SignalServiceEnvelope envelope,
126 final boolean ignoreAttachments,
127 final Manager.ReceiveMessageHandler handler
128 ) {
129 final var actions = new ArrayList<HandleAction>();
130 if (envelope.hasSourceUuid()) {
131 // Store uuid if we don't have it already
132 // address/uuid in envelope is sent by server
133 account.getRecipientStore().resolveRecipientTrusted(envelope.getSourceAddress());
134 }
135 SignalServiceContent content = null;
136 Exception exception = null;
137 if (!envelope.isReceipt()) {
138 try {
139 content = dependencies.getCipher().decrypt(envelope);
140 } catch (ProtocolUntrustedIdentityException e) {
141 final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender());
142 actions.add(new RetrieveProfileAction(recipientId));
143 exception = new UntrustedIdentityException(account.getRecipientStore()
144 .resolveRecipientAddress(recipientId), e.getSenderDevice());
145 } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) {
146 final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
147 final var senderProfile = profileProvider.getProfile(sender);
148 final var selfProfile = profileProvider.getProfile(account.getSelfRecipientId());
149 if (e.getSenderDevice() != account.getDeviceId()
150 && senderProfile != null
151 && senderProfile.getCapabilities().contains(Profile.Capability.senderKey)
152 && selfProfile != null
153 && selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) {
154 logger.debug("Received invalid message, requesting message resend.");
155 actions.add(new SendRetryMessageRequestAction(sender, e, envelope));
156 } else {
157 logger.debug("Received invalid message, queuing renew session action.");
158 actions.add(new RenewSessionAction(sender));
159 }
160 exception = e;
161 } catch (SelfSendException e) {
162 logger.debug("Dropping unidentified message from self.");
163 return new Pair<>(List.of(), null);
164 } catch (Exception e) {
165 exception = e;
166 }
167 }
168
169 actions.addAll(checkAndHandleMessage(envelope, content, ignoreAttachments, handler, exception));
170 return new Pair<>(actions, exception);
171 }
172
173 private List<HandleAction> checkAndHandleMessage(
174 final SignalServiceEnvelope envelope,
175 final SignalServiceContent content,
176 final boolean ignoreAttachments,
177 final Manager.ReceiveMessageHandler handler,
178 final Exception exception
179 ) {
180 if (!envelope.hasSourceUuid() && content != null) {
181 // Store uuid if we don't have it already
182 // address/uuid is validated by unidentified sender certificate
183 account.getRecipientStore().resolveRecipientTrusted(content.getSender());
184 }
185 if (isMessageBlocked(envelope, content)) {
186 logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
187 return List.of();
188 } else if (isNotAllowedToSendToGroup(envelope, content)) {
189 logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
190 (envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(),
191 envelope.getTimestamp());
192 return List.of();
193 } else {
194 List<HandleAction> actions;
195 if (content != null) {
196 actions = handleMessage(envelope, content, ignoreAttachments);
197 } else {
198 actions = List.of();
199 }
200 handler.handleMessage(MessageEnvelope.from(envelope,
201 content,
202 recipientResolver,
203 account.getRecipientStore()::resolveRecipientAddress,
204 attachmentHelper::getAttachmentFile), exception);
205 return actions;
206 }
207 }
208
209 public List<HandleAction> handleMessage(
210 SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments
211 ) {
212 var actions = new ArrayList<HandleAction>();
213 final RecipientId sender;
214 final int senderDeviceId;
215 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
216 sender = recipientResolver.resolveRecipient(envelope.getSourceAddress());
217 senderDeviceId = envelope.getSourceDevice();
218 } else {
219 sender = recipientResolver.resolveRecipient(content.getSender());
220 senderDeviceId = content.getSenderDevice();
221 }
222
223 if (content.getSenderKeyDistributionMessage().isPresent()) {
224 final var message = content.getSenderKeyDistributionMessage().get();
225 final var protocolAddress = new SignalProtocolAddress(addressResolver.resolveSignalServiceAddress(sender)
226 .getIdentifier(), senderDeviceId);
227 logger.debug("Received a sender key distribution message for distributionId {} from {}",
228 message.getDistributionId(),
229 protocolAddress);
230 dependencies.getMessageSender().processSenderKeyDistributionMessage(protocolAddress, message);
231 }
232
233 if (content.getDecryptionErrorMessage().isPresent()) {
234 var message = content.getDecryptionErrorMessage().get();
235 logger.debug("Received a decryption error message (resend request for {})", message.getTimestamp());
236 }
237
238 if (content.getDataMessage().isPresent()) {
239 var message = content.getDataMessage().get();
240
241 if (content.isNeedsReceipt()) {
242 actions.add(new SendReceiptAction(sender, message.getTimestamp()));
243 }
244
245 actions.addAll(handleSignalServiceDataMessage(message,
246 false,
247 sender,
248 account.getSelfRecipientId(),
249 ignoreAttachments));
250 }
251
252 if (content.getSyncMessage().isPresent()) {
253 var syncMessage = content.getSyncMessage().get();
254 actions.addAll(handleSyncMessage(syncMessage, sender, ignoreAttachments));
255 }
256
257 return actions;
258 }
259
260 private List<HandleAction> handleSyncMessage(
261 final SignalServiceSyncMessage syncMessage, final RecipientId sender, final boolean ignoreAttachments
262 ) {
263 var actions = new ArrayList<HandleAction>();
264 account.setMultiDevice(true);
265 if (syncMessage.getSent().isPresent()) {
266 var message = syncMessage.getSent().get();
267 final var destination = message.getDestination().orNull();
268 actions.addAll(handleSignalServiceDataMessage(message.getMessage(),
269 true,
270 sender,
271 destination == null ? null : recipientResolver.resolveRecipient(destination),
272 ignoreAttachments));
273 }
274 if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) {
275 var rm = syncMessage.getRequest().get();
276 if (rm.isContactsRequest()) {
277 actions.add(SendSyncContactsAction.create());
278 }
279 if (rm.isGroupsRequest()) {
280 actions.add(SendSyncGroupsAction.create());
281 }
282 if (rm.isBlockedListRequest()) {
283 actions.add(SendSyncBlockedListAction.create());
284 }
285 if (rm.isKeysRequest()) {
286 actions.add(SendSyncKeysAction.create());
287 }
288 if (rm.isConfigurationRequest()) {
289 actions.add(SendSyncConfigurationAction.create());
290 }
291 }
292 if (syncMessage.getGroups().isPresent()) {
293 logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
294 }
295 if (syncMessage.getBlockedList().isPresent()) {
296 final var blockedListMessage = syncMessage.getBlockedList().get();
297 for (var address : blockedListMessage.getAddresses()) {
298 contactHelper.setContactBlocked(recipientResolver.resolveRecipient(address), true);
299 }
300 for (var groupId : blockedListMessage.getGroupIds()
301 .stream()
302 .map(GroupId::unknownVersion)
303 .collect(Collectors.toSet())) {
304 try {
305 groupHelper.setGroupBlocked(groupId, true);
306 } catch (GroupNotFoundException e) {
307 logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
308 groupId.toBase64());
309 }
310 }
311 }
312 if (syncMessage.getContacts().isPresent()) {
313 try {
314 final var contactsMessage = syncMessage.getContacts().get();
315 attachmentHelper.retrieveAttachment(contactsMessage.getContactsStream(),
316 syncHelper::handleSyncDeviceContacts);
317 } catch (Exception e) {
318 logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage());
319 }
320 }
321 if (syncMessage.getVerified().isPresent()) {
322 final var verifiedMessage = syncMessage.getVerified().get();
323 account.getIdentityKeyStore()
324 .setIdentityTrustLevel(account.getRecipientStore()
325 .resolveRecipientTrusted(verifiedMessage.getDestination()),
326 verifiedMessage.getIdentityKey(),
327 TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
328 }
329 if (syncMessage.getStickerPackOperations().isPresent()) {
330 final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
331 for (var m : stickerPackOperationMessages) {
332 if (!m.getPackId().isPresent()) {
333 continue;
334 }
335 final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
336 final var installed = !m.getType().isPresent()
337 || m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
338
339 var sticker = account.getStickerStore().getSticker(stickerPackId);
340 if (m.getPackKey().isPresent()) {
341 if (sticker == null) {
342 sticker = new Sticker(stickerPackId, m.getPackKey().get());
343 }
344 if (installed) {
345 jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
346 }
347 }
348
349 if (sticker != null) {
350 sticker.setInstalled(installed);
351 account.getStickerStore().updateSticker(sticker);
352 }
353 }
354 }
355 if (syncMessage.getFetchType().isPresent()) {
356 switch (syncMessage.getFetchType().get()) {
357 case LOCAL_PROFILE:
358 actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
359 case STORAGE_MANIFEST:
360 actions.add(RetrieveStorageDataAction.create());
361 }
362 }
363 if (syncMessage.getKeys().isPresent()) {
364 final var keysMessage = syncMessage.getKeys().get();
365 if (keysMessage.getStorageService().isPresent()) {
366 final var storageKey = keysMessage.getStorageService().get();
367 account.setStorageKey(storageKey);
368 actions.add(RetrieveStorageDataAction.create());
369 }
370 }
371 if (syncMessage.getConfiguration().isPresent()) {
372 final var configurationMessage = syncMessage.getConfiguration().get();
373 final var configurationStore = account.getConfigurationStore();
374 if (configurationMessage.getReadReceipts().isPresent()) {
375 configurationStore.setReadReceipts(configurationMessage.getReadReceipts().get());
376 }
377 if (configurationMessage.getLinkPreviews().isPresent()) {
378 configurationStore.setLinkPreviews(configurationMessage.getLinkPreviews().get());
379 }
380 if (configurationMessage.getTypingIndicators().isPresent()) {
381 configurationStore.setTypingIndicators(configurationMessage.getTypingIndicators().get());
382 }
383 if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) {
384 configurationStore.setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
385 .get());
386 }
387 }
388 return actions;
389 }
390
391 private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
392 SignalServiceAddress source;
393 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
394 source = envelope.getSourceAddress();
395 } else if (content != null) {
396 source = content.getSender();
397 } else {
398 return false;
399 }
400 final var recipientId = recipientResolver.resolveRecipient(source);
401 if (contactHelper.isContactBlocked(recipientId)) {
402 return true;
403 }
404
405 if (content != null && content.getDataMessage().isPresent()) {
406 var message = content.getDataMessage().get();
407 if (message.getGroupContext().isPresent()) {
408 var groupId = GroupUtils.getGroupId(message.getGroupContext().get());
409 return groupHelper.isGroupBlocked(groupId);
410 }
411 }
412
413 return false;
414 }
415
416 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
417 SignalServiceAddress source;
418 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
419 source = envelope.getSourceAddress();
420 } else if (content != null) {
421 source = content.getSender();
422 } else {
423 return false;
424 }
425
426 if (content == null || !content.getDataMessage().isPresent()) {
427 return false;
428 }
429
430 var message = content.getDataMessage().get();
431 if (!message.getGroupContext().isPresent()) {
432 return false;
433 }
434
435 if (message.getGroupContext().get().getGroupV1().isPresent()) {
436 var groupInfo = message.getGroupContext().get().getGroupV1().get();
437 if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) {
438 return false;
439 }
440 }
441
442 var groupId = GroupUtils.getGroupId(message.getGroupContext().get());
443 var group = groupHelper.getGroup(groupId);
444 if (group == null) {
445 return false;
446 }
447
448 final var recipientId = recipientResolver.resolveRecipient(source);
449 if (!group.isMember(recipientId) && !(group.isPendingMember(recipientId) && message.isGroupV2Update())) {
450 return true;
451 }
452
453 if (group.isAnnouncementGroup() && !group.isAdmin(recipientId)) {
454 return message.getBody().isPresent()
455 || message.getAttachments().isPresent()
456 || message.getQuote()
457 .isPresent()
458 || message.getPreviews().isPresent()
459 || message.getMentions().isPresent()
460 || message.getSticker().isPresent();
461 }
462 return false;
463 }
464
465 private List<HandleAction> handleSignalServiceDataMessage(
466 SignalServiceDataMessage message,
467 boolean isSync,
468 RecipientId source,
469 RecipientId destination,
470 boolean ignoreAttachments
471 ) {
472 var actions = new ArrayList<HandleAction>();
473 if (message.getGroupContext().isPresent()) {
474 if (message.getGroupContext().get().getGroupV1().isPresent()) {
475 var groupInfo = message.getGroupContext().get().getGroupV1().get();
476 var groupId = GroupId.v1(groupInfo.getGroupId());
477 var group = groupHelper.getGroup(groupId);
478 if (group == null || group instanceof GroupInfoV1) {
479 var groupV1 = (GroupInfoV1) group;
480 switch (groupInfo.getType()) {
481 case UPDATE: {
482 if (groupV1 == null) {
483 groupV1 = new GroupInfoV1(groupId);
484 }
485
486 if (groupInfo.getAvatar().isPresent()) {
487 var avatar = groupInfo.getAvatar().get();
488 groupHelper.downloadGroupAvatar(groupV1.getGroupId(), avatar);
489 }
490
491 if (groupInfo.getName().isPresent()) {
492 groupV1.name = groupInfo.getName().get();
493 }
494
495 if (groupInfo.getMembers().isPresent()) {
496 groupV1.addMembers(groupInfo.getMembers()
497 .get()
498 .stream()
499 .map(recipientResolver::resolveRecipient)
500 .collect(Collectors.toSet()));
501 }
502
503 account.getGroupStore().updateGroup(groupV1);
504 break;
505 }
506 case DELIVER:
507 if (groupV1 == null && !isSync) {
508 actions.add(new SendGroupInfoRequestAction(source, groupId));
509 }
510 break;
511 case QUIT: {
512 if (groupV1 != null) {
513 groupV1.removeMember(source);
514 account.getGroupStore().updateGroup(groupV1);
515 }
516 break;
517 }
518 case REQUEST_INFO:
519 if (groupV1 != null && !isSync) {
520 actions.add(new SendGroupInfoAction(source, groupV1.getGroupId()));
521 }
522 break;
523 }
524 } else {
525 // Received a group v1 message for a v2 group
526 }
527 }
528 if (message.getGroupContext().get().getGroupV2().isPresent()) {
529 final var groupContext = message.getGroupContext().get().getGroupV2().get();
530 final var groupMasterKey = groupContext.getMasterKey();
531
532 groupHelper.getOrMigrateGroup(groupMasterKey,
533 groupContext.getRevision(),
534 groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
535 }
536 }
537
538 final var conversationPartnerAddress = isSync ? destination : source;
539 if (conversationPartnerAddress != null && message.isEndSession()) {
540 account.getSessionStore().deleteAllSessions(conversationPartnerAddress);
541 }
542 if (message.isExpirationUpdate() || message.getBody().isPresent()) {
543 if (message.getGroupContext().isPresent()) {
544 if (message.getGroupContext().get().getGroupV1().isPresent()) {
545 var groupInfo = message.getGroupContext().get().getGroupV1().get();
546 var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
547 if (group != null) {
548 if (group.messageExpirationTime != message.getExpiresInSeconds()) {
549 group.messageExpirationTime = message.getExpiresInSeconds();
550 account.getGroupStore().updateGroup(group);
551 }
552 }
553 } else if (message.getGroupContext().get().getGroupV2().isPresent()) {
554 // disappearing message timer already stored in the DecryptedGroup
555 }
556 } else if (conversationPartnerAddress != null) {
557 contactHelper.setExpirationTimer(conversationPartnerAddress, message.getExpiresInSeconds());
558 }
559 }
560 if (!ignoreAttachments) {
561 if (message.getAttachments().isPresent()) {
562 for (var attachment : message.getAttachments().get()) {
563 attachmentHelper.downloadAttachment(attachment);
564 }
565 }
566 if (message.getSharedContacts().isPresent()) {
567 for (var contact : message.getSharedContacts().get()) {
568 if (contact.getAvatar().isPresent()) {
569 attachmentHelper.downloadAttachment(contact.getAvatar().get().getAttachment());
570 }
571 }
572 }
573 if (message.getPreviews().isPresent()) {
574 final var previews = message.getPreviews().get();
575 for (var preview : previews) {
576 if (preview.getImage().isPresent()) {
577 attachmentHelper.downloadAttachment(preview.getImage().get());
578 }
579 }
580 }
581 if (message.getQuote().isPresent()) {
582 final var quote = message.getQuote().get();
583
584 for (var quotedAttachment : quote.getAttachments()) {
585 final var thumbnail = quotedAttachment.getThumbnail();
586 if (thumbnail != null) {
587 attachmentHelper.downloadAttachment(thumbnail);
588 }
589 }
590 }
591 }
592 if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
593 final ProfileKey profileKey;
594 try {
595 profileKey = new ProfileKey(message.getProfileKey().get());
596 } catch (InvalidInputException e) {
597 throw new AssertionError(e);
598 }
599 if (account.getSelfRecipientId().equals(source)) {
600 this.account.setProfileKey(profileKey);
601 }
602 this.account.getProfileStore().storeProfileKey(source, profileKey);
603 }
604 if (message.getSticker().isPresent()) {
605 final var messageSticker = message.getSticker().get();
606 final var stickerPackId = StickerPackId.deserialize(messageSticker.getPackId());
607 var sticker = account.getStickerStore().getSticker(stickerPackId);
608 if (sticker == null) {
609 sticker = new Sticker(stickerPackId, messageSticker.getPackKey());
610 account.getStickerStore().updateSticker(sticker);
611 }
612 jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey()));
613 }
614 return actions;
615 }
616 }