]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
Implement simple handling of incoming decryption error message
[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 if (message.getRatchetKey().isPresent()) {
237 if (message.getDeviceId() == account.getDeviceId() && account.getSessionStore()
238 .isCurrentRatchetKey(sender, senderDeviceId, message.getRatchetKey().get())) {
239 logger.debug("Renewing the session with sender");
240 actions.add(new RenewSessionAction(sender));
241 }
242 } else {
243 logger.debug("Reset shared sender keys with this recipient");
244 account.getSenderKeyStore().deleteSharedWith(sender);
245 }
246 }
247
248 if (content.getDataMessage().isPresent()) {
249 var message = content.getDataMessage().get();
250
251 if (content.isNeedsReceipt()) {
252 actions.add(new SendReceiptAction(sender, message.getTimestamp()));
253 }
254
255 actions.addAll(handleSignalServiceDataMessage(message,
256 false,
257 sender,
258 account.getSelfRecipientId(),
259 ignoreAttachments));
260 }
261
262 if (content.getSyncMessage().isPresent()) {
263 var syncMessage = content.getSyncMessage().get();
264 actions.addAll(handleSyncMessage(syncMessage, sender, ignoreAttachments));
265 }
266
267 return actions;
268 }
269
270 private List<HandleAction> handleSyncMessage(
271 final SignalServiceSyncMessage syncMessage, final RecipientId sender, final boolean ignoreAttachments
272 ) {
273 var actions = new ArrayList<HandleAction>();
274 account.setMultiDevice(true);
275 if (syncMessage.getSent().isPresent()) {
276 var message = syncMessage.getSent().get();
277 final var destination = message.getDestination().orNull();
278 actions.addAll(handleSignalServiceDataMessage(message.getMessage(),
279 true,
280 sender,
281 destination == null ? null : recipientResolver.resolveRecipient(destination),
282 ignoreAttachments));
283 }
284 if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) {
285 var rm = syncMessage.getRequest().get();
286 if (rm.isContactsRequest()) {
287 actions.add(SendSyncContactsAction.create());
288 }
289 if (rm.isGroupsRequest()) {
290 actions.add(SendSyncGroupsAction.create());
291 }
292 if (rm.isBlockedListRequest()) {
293 actions.add(SendSyncBlockedListAction.create());
294 }
295 if (rm.isKeysRequest()) {
296 actions.add(SendSyncKeysAction.create());
297 }
298 if (rm.isConfigurationRequest()) {
299 actions.add(SendSyncConfigurationAction.create());
300 }
301 }
302 if (syncMessage.getGroups().isPresent()) {
303 logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
304 }
305 if (syncMessage.getBlockedList().isPresent()) {
306 final var blockedListMessage = syncMessage.getBlockedList().get();
307 for (var address : blockedListMessage.getAddresses()) {
308 contactHelper.setContactBlocked(recipientResolver.resolveRecipient(address), true);
309 }
310 for (var groupId : blockedListMessage.getGroupIds()
311 .stream()
312 .map(GroupId::unknownVersion)
313 .collect(Collectors.toSet())) {
314 try {
315 groupHelper.setGroupBlocked(groupId, true);
316 } catch (GroupNotFoundException e) {
317 logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
318 groupId.toBase64());
319 }
320 }
321 }
322 if (syncMessage.getContacts().isPresent()) {
323 try {
324 final var contactsMessage = syncMessage.getContacts().get();
325 attachmentHelper.retrieveAttachment(contactsMessage.getContactsStream(),
326 syncHelper::handleSyncDeviceContacts);
327 } catch (Exception e) {
328 logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage());
329 }
330 }
331 if (syncMessage.getVerified().isPresent()) {
332 final var verifiedMessage = syncMessage.getVerified().get();
333 account.getIdentityKeyStore()
334 .setIdentityTrustLevel(account.getRecipientStore()
335 .resolveRecipientTrusted(verifiedMessage.getDestination()),
336 verifiedMessage.getIdentityKey(),
337 TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
338 }
339 if (syncMessage.getStickerPackOperations().isPresent()) {
340 final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
341 for (var m : stickerPackOperationMessages) {
342 if (!m.getPackId().isPresent()) {
343 continue;
344 }
345 final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
346 final var installed = !m.getType().isPresent()
347 || m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
348
349 var sticker = account.getStickerStore().getSticker(stickerPackId);
350 if (m.getPackKey().isPresent()) {
351 if (sticker == null) {
352 sticker = new Sticker(stickerPackId, m.getPackKey().get());
353 }
354 if (installed) {
355 jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
356 }
357 }
358
359 if (sticker != null) {
360 sticker.setInstalled(installed);
361 account.getStickerStore().updateSticker(sticker);
362 }
363 }
364 }
365 if (syncMessage.getFetchType().isPresent()) {
366 switch (syncMessage.getFetchType().get()) {
367 case LOCAL_PROFILE:
368 actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
369 case STORAGE_MANIFEST:
370 actions.add(RetrieveStorageDataAction.create());
371 }
372 }
373 if (syncMessage.getKeys().isPresent()) {
374 final var keysMessage = syncMessage.getKeys().get();
375 if (keysMessage.getStorageService().isPresent()) {
376 final var storageKey = keysMessage.getStorageService().get();
377 account.setStorageKey(storageKey);
378 actions.add(RetrieveStorageDataAction.create());
379 }
380 }
381 if (syncMessage.getConfiguration().isPresent()) {
382 final var configurationMessage = syncMessage.getConfiguration().get();
383 final var configurationStore = account.getConfigurationStore();
384 if (configurationMessage.getReadReceipts().isPresent()) {
385 configurationStore.setReadReceipts(configurationMessage.getReadReceipts().get());
386 }
387 if (configurationMessage.getLinkPreviews().isPresent()) {
388 configurationStore.setLinkPreviews(configurationMessage.getLinkPreviews().get());
389 }
390 if (configurationMessage.getTypingIndicators().isPresent()) {
391 configurationStore.setTypingIndicators(configurationMessage.getTypingIndicators().get());
392 }
393 if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) {
394 configurationStore.setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
395 .get());
396 }
397 }
398 return actions;
399 }
400
401 private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
402 SignalServiceAddress source;
403 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
404 source = envelope.getSourceAddress();
405 } else if (content != null) {
406 source = content.getSender();
407 } else {
408 return false;
409 }
410 final var recipientId = recipientResolver.resolveRecipient(source);
411 if (contactHelper.isContactBlocked(recipientId)) {
412 return true;
413 }
414
415 if (content != null && content.getDataMessage().isPresent()) {
416 var message = content.getDataMessage().get();
417 if (message.getGroupContext().isPresent()) {
418 var groupId = GroupUtils.getGroupId(message.getGroupContext().get());
419 return groupHelper.isGroupBlocked(groupId);
420 }
421 }
422
423 return false;
424 }
425
426 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
427 SignalServiceAddress source;
428 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
429 source = envelope.getSourceAddress();
430 } else if (content != null) {
431 source = content.getSender();
432 } else {
433 return false;
434 }
435
436 if (content == null || !content.getDataMessage().isPresent()) {
437 return false;
438 }
439
440 var message = content.getDataMessage().get();
441 if (!message.getGroupContext().isPresent()) {
442 return false;
443 }
444
445 if (message.getGroupContext().get().getGroupV1().isPresent()) {
446 var groupInfo = message.getGroupContext().get().getGroupV1().get();
447 if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) {
448 return false;
449 }
450 }
451
452 var groupId = GroupUtils.getGroupId(message.getGroupContext().get());
453 var group = groupHelper.getGroup(groupId);
454 if (group == null) {
455 return false;
456 }
457
458 final var recipientId = recipientResolver.resolveRecipient(source);
459 if (!group.isMember(recipientId) && !(group.isPendingMember(recipientId) && message.isGroupV2Update())) {
460 return true;
461 }
462
463 if (group.isAnnouncementGroup() && !group.isAdmin(recipientId)) {
464 return message.getBody().isPresent()
465 || message.getAttachments().isPresent()
466 || message.getQuote()
467 .isPresent()
468 || message.getPreviews().isPresent()
469 || message.getMentions().isPresent()
470 || message.getSticker().isPresent();
471 }
472 return false;
473 }
474
475 private List<HandleAction> handleSignalServiceDataMessage(
476 SignalServiceDataMessage message,
477 boolean isSync,
478 RecipientId source,
479 RecipientId destination,
480 boolean ignoreAttachments
481 ) {
482 var actions = new ArrayList<HandleAction>();
483 if (message.getGroupContext().isPresent()) {
484 if (message.getGroupContext().get().getGroupV1().isPresent()) {
485 var groupInfo = message.getGroupContext().get().getGroupV1().get();
486 var groupId = GroupId.v1(groupInfo.getGroupId());
487 var group = groupHelper.getGroup(groupId);
488 if (group == null || group instanceof GroupInfoV1) {
489 var groupV1 = (GroupInfoV1) group;
490 switch (groupInfo.getType()) {
491 case UPDATE: {
492 if (groupV1 == null) {
493 groupV1 = new GroupInfoV1(groupId);
494 }
495
496 if (groupInfo.getAvatar().isPresent()) {
497 var avatar = groupInfo.getAvatar().get();
498 groupHelper.downloadGroupAvatar(groupV1.getGroupId(), avatar);
499 }
500
501 if (groupInfo.getName().isPresent()) {
502 groupV1.name = groupInfo.getName().get();
503 }
504
505 if (groupInfo.getMembers().isPresent()) {
506 groupV1.addMembers(groupInfo.getMembers()
507 .get()
508 .stream()
509 .map(recipientResolver::resolveRecipient)
510 .collect(Collectors.toSet()));
511 }
512
513 account.getGroupStore().updateGroup(groupV1);
514 break;
515 }
516 case DELIVER:
517 if (groupV1 == null && !isSync) {
518 actions.add(new SendGroupInfoRequestAction(source, groupId));
519 }
520 break;
521 case QUIT: {
522 if (groupV1 != null) {
523 groupV1.removeMember(source);
524 account.getGroupStore().updateGroup(groupV1);
525 }
526 break;
527 }
528 case REQUEST_INFO:
529 if (groupV1 != null && !isSync) {
530 actions.add(new SendGroupInfoAction(source, groupV1.getGroupId()));
531 }
532 break;
533 }
534 } else {
535 // Received a group v1 message for a v2 group
536 }
537 }
538 if (message.getGroupContext().get().getGroupV2().isPresent()) {
539 final var groupContext = message.getGroupContext().get().getGroupV2().get();
540 final var groupMasterKey = groupContext.getMasterKey();
541
542 groupHelper.getOrMigrateGroup(groupMasterKey,
543 groupContext.getRevision(),
544 groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
545 }
546 }
547
548 final var conversationPartnerAddress = isSync ? destination : source;
549 if (conversationPartnerAddress != null && message.isEndSession()) {
550 account.getSessionStore().deleteAllSessions(conversationPartnerAddress);
551 }
552 if (message.isExpirationUpdate() || message.getBody().isPresent()) {
553 if (message.getGroupContext().isPresent()) {
554 if (message.getGroupContext().get().getGroupV1().isPresent()) {
555 var groupInfo = message.getGroupContext().get().getGroupV1().get();
556 var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
557 if (group != null) {
558 if (group.messageExpirationTime != message.getExpiresInSeconds()) {
559 group.messageExpirationTime = message.getExpiresInSeconds();
560 account.getGroupStore().updateGroup(group);
561 }
562 }
563 } else if (message.getGroupContext().get().getGroupV2().isPresent()) {
564 // disappearing message timer already stored in the DecryptedGroup
565 }
566 } else if (conversationPartnerAddress != null) {
567 contactHelper.setExpirationTimer(conversationPartnerAddress, message.getExpiresInSeconds());
568 }
569 }
570 if (!ignoreAttachments) {
571 if (message.getAttachments().isPresent()) {
572 for (var attachment : message.getAttachments().get()) {
573 attachmentHelper.downloadAttachment(attachment);
574 }
575 }
576 if (message.getSharedContacts().isPresent()) {
577 for (var contact : message.getSharedContacts().get()) {
578 if (contact.getAvatar().isPresent()) {
579 attachmentHelper.downloadAttachment(contact.getAvatar().get().getAttachment());
580 }
581 }
582 }
583 if (message.getPreviews().isPresent()) {
584 final var previews = message.getPreviews().get();
585 for (var preview : previews) {
586 if (preview.getImage().isPresent()) {
587 attachmentHelper.downloadAttachment(preview.getImage().get());
588 }
589 }
590 }
591 if (message.getQuote().isPresent()) {
592 final var quote = message.getQuote().get();
593
594 for (var quotedAttachment : quote.getAttachments()) {
595 final var thumbnail = quotedAttachment.getThumbnail();
596 if (thumbnail != null) {
597 attachmentHelper.downloadAttachment(thumbnail);
598 }
599 }
600 }
601 }
602 if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
603 final ProfileKey profileKey;
604 try {
605 profileKey = new ProfileKey(message.getProfileKey().get());
606 } catch (InvalidInputException e) {
607 throw new AssertionError(e);
608 }
609 if (account.getSelfRecipientId().equals(source)) {
610 this.account.setProfileKey(profileKey);
611 }
612 this.account.getProfileStore().storeProfileKey(source, profileKey);
613 }
614 if (message.getSticker().isPresent()) {
615 final var messageSticker = message.getSticker().get();
616 final var stickerPackId = StickerPackId.deserialize(messageSticker.getPackId());
617 var sticker = account.getStickerStore().getSticker(stickerPackId);
618 if (sticker == null) {
619 sticker = new Sticker(stickerPackId, messageSticker.getPackKey());
620 account.getStickerStore().updateSticker(sticker);
621 }
622 jobExecutor.enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey()));
623 }
624 return actions;
625 }
626 }