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