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