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