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