]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
Add workaround for #1045
[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.ReceiveConfig;
26 import org.asamk.signal.manager.api.StickerPackId;
27 import org.asamk.signal.manager.api.TrustLevel;
28 import org.asamk.signal.manager.api.UntrustedIdentityException;
29 import org.asamk.signal.manager.groups.GroupId;
30 import org.asamk.signal.manager.groups.GroupNotFoundException;
31 import org.asamk.signal.manager.groups.GroupUtils;
32 import org.asamk.signal.manager.jobs.RetrieveStickerPackJob;
33 import org.asamk.signal.manager.storage.SignalAccount;
34 import org.asamk.signal.manager.storage.groups.GroupInfoV1;
35 import org.asamk.signal.manager.storage.recipients.Profile;
36 import org.asamk.signal.manager.storage.recipients.RecipientId;
37 import org.asamk.signal.manager.storage.stickers.StickerPack;
38 import org.asamk.signal.manager.util.KeyUtils;
39 import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
40 import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
41 import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
42 import org.signal.libsignal.metadata.ProtocolNoSessionException;
43 import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
44 import org.signal.libsignal.metadata.SelfSendException;
45 import org.signal.libsignal.protocol.IdentityKeyPair;
46 import org.signal.libsignal.protocol.InvalidMessageException;
47 import org.signal.libsignal.protocol.message.DecryptionErrorMessage;
48 import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
49 import org.signal.libsignal.zkgroup.InvalidInputException;
50 import org.signal.libsignal.zkgroup.profiles.ProfileKey;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.whispersystems.signalservice.api.messages.SignalServiceContent;
54 import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
55 import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
56 import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
57 import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
58 import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
59 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
60 import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
61 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
62 import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
63 import org.whispersystems.signalservice.api.push.PNI;
64 import org.whispersystems.signalservice.api.push.ServiceId;
65 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
66
67 import java.util.ArrayList;
68 import java.util.List;
69 import java.util.stream.Collectors;
70
71 public final class IncomingMessageHandler {
72
73 private final static Logger logger = LoggerFactory.getLogger(IncomingMessageHandler.class);
74
75 private final SignalAccount account;
76 private final SignalDependencies dependencies;
77 private final Context context;
78
79 public IncomingMessageHandler(final Context context) {
80 this.account = context.getAccount();
81 this.dependencies = context.getDependencies();
82 this.context = context;
83 }
84
85 public Pair<List<HandleAction>, Exception> handleRetryEnvelope(
86 final SignalServiceEnvelope envelope,
87 final ReceiveConfig receiveConfig,
88 final Manager.ReceiveMessageHandler handler
89 ) {
90 final List<HandleAction> actions = new ArrayList<>();
91 if (envelope.isPreKeySignalMessage()) {
92 actions.add(RefreshPreKeysAction.create());
93 }
94
95 SignalServiceContent content = null;
96 if (!envelope.isReceipt()) {
97 account.getIdentityKeyStore().setRetryingDecryption(true);
98 try {
99 content = dependencies.getCipher().decrypt(envelope);
100 } catch (ProtocolUntrustedIdentityException e) {
101 final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender());
102 final var exception = new UntrustedIdentityException(account.getRecipientAddressResolver()
103 .resolveRecipientAddress(recipientId)
104 .toApiRecipientAddress(), e.getSenderDevice());
105 return new Pair<>(List.of(), exception);
106 } catch (Exception e) {
107 return new Pair<>(List.of(), e);
108 } finally {
109 account.getIdentityKeyStore().setRetryingDecryption(false);
110 }
111 }
112 actions.addAll(checkAndHandleMessage(envelope, content, receiveConfig, handler, null));
113 return new Pair<>(actions, null);
114 }
115
116 public Pair<List<HandleAction>, Exception> handleEnvelope(
117 final SignalServiceEnvelope envelope,
118 final ReceiveConfig receiveConfig,
119 final Manager.ReceiveMessageHandler handler
120 ) {
121 final var actions = new ArrayList<HandleAction>();
122 if (envelope.hasSourceUuid()) {
123 // Store uuid if we don't have it already
124 // address/uuid in envelope is sent by server
125 account.getRecipientTrustedResolver().resolveRecipientTrusted(envelope.getSourceAddress());
126 }
127 SignalServiceContent content = null;
128 Exception exception = null;
129 if (!envelope.isReceipt()) {
130 try {
131 content = dependencies.getCipher().decrypt(envelope);
132 } catch (ProtocolUntrustedIdentityException e) {
133 final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender());
134 actions.add(new RetrieveProfileAction(recipientId));
135 exception = new UntrustedIdentityException(account.getRecipientAddressResolver()
136 .resolveRecipientAddress(recipientId)
137 .toApiRecipientAddress(), e.getSenderDevice());
138 } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException |
139 ProtocolInvalidMessageException e) {
140 logger.debug("Failed to decrypt incoming message", e);
141 final var sender = account.getRecipientResolver().resolveRecipient(e.getSender());
142 if (context.getContactHelper().isContactBlocked(sender)) {
143 logger.debug("Received invalid message from blocked contact, ignoring.");
144 } else {
145 final var senderProfile = context.getProfileHelper().getRecipientProfile(sender);
146 final var selfProfile = context.getProfileHelper().getSelfProfile();
147 var serviceId = ServiceId.parseOrNull(e.getSender());
148 if (serviceId == null) {
149 // Workaround for libsignal-client issue #492
150 serviceId = account.getRecipientAddressResolver()
151 .resolveRecipientAddress(sender)
152 .serviceId()
153 .orElse(null);
154 }
155 if (serviceId != null) {
156 final var isSelf = sender.equals(account.getSelfRecipientId())
157 && e.getSenderDevice() == account.getDeviceId();
158 final var isSenderSenderKeyCapable = senderProfile != null && senderProfile.getCapabilities()
159 .contains(Profile.Capability.senderKey);
160 final var isSelfSenderKeyCapable = selfProfile != null && selfProfile.getCapabilities()
161 .contains(Profile.Capability.senderKey);
162 if (!isSelf && isSenderSenderKeyCapable && isSelfSenderKeyCapable) {
163 logger.debug("Received invalid message, requesting message resend.");
164 actions.add(new SendRetryMessageRequestAction(sender, serviceId, e, envelope));
165 } else {
166 logger.debug("Received invalid message, queuing renew session action.");
167 actions.add(new RenewSessionAction(sender, serviceId));
168 }
169 } else {
170 logger.debug("Received invalid message from invalid sender: {}", e.getSender());
171 }
172 }
173 exception = e;
174 } catch (SelfSendException e) {
175 logger.debug("Dropping unidentified message from self.");
176 return new Pair<>(List.of(), null);
177 } catch (Exception e) {
178 logger.debug("Failed to handle incoming message", e);
179 exception = e;
180 }
181 }
182
183 actions.addAll(checkAndHandleMessage(envelope, content, receiveConfig, handler, exception));
184 return new Pair<>(actions, exception);
185 }
186
187 private List<HandleAction> checkAndHandleMessage(
188 final SignalServiceEnvelope envelope,
189 final SignalServiceContent content,
190 final ReceiveConfig receiveConfig,
191 final Manager.ReceiveMessageHandler handler,
192 final Exception exception
193 ) {
194 if (content != null) {
195 // Store uuid if we don't have it already
196 // address/uuid is validated by unidentified sender certificate
197 account.getRecipientTrustedResolver().resolveRecipientTrusted(content.getSender());
198 }
199 if (envelope.isReceipt()) {
200 final var senderDeviceAddress = getSender(envelope, content);
201 final var sender = senderDeviceAddress.serviceId();
202 final var senderDeviceId = senderDeviceAddress.deviceId();
203 account.getMessageSendLogStore().deleteEntryForRecipient(envelope.getTimestamp(), sender, senderDeviceId);
204 }
205
206 var notAllowedToSendToGroup = isNotAllowedToSendToGroup(envelope, content);
207 final var groupContext = getGroupContext(content);
208 if (groupContext != null && groupContext.getGroupV2().isPresent()) {
209 handleGroupV2Context(groupContext.getGroupV2().get());
210 }
211 // Check again in case the user just joined the group
212 notAllowedToSendToGroup = notAllowedToSendToGroup && isNotAllowedToSendToGroup(envelope, content);
213
214 if (isMessageBlocked(envelope, content)) {
215 logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
216 return List.of();
217 } else if (notAllowedToSendToGroup) {
218 logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
219 (envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(),
220 envelope.getTimestamp());
221 return List.of();
222 } else {
223 List<HandleAction> actions;
224 if (content != null) {
225 actions = handleMessage(envelope, content, receiveConfig);
226 } else {
227 actions = List.of();
228 }
229 handler.handleMessage(MessageEnvelope.from(envelope,
230 content,
231 account.getRecipientResolver(),
232 account.getRecipientAddressResolver(),
233 context.getAttachmentHelper()::getAttachmentFile,
234 exception), exception);
235 return actions;
236 }
237 }
238
239 public List<HandleAction> handleMessage(
240 SignalServiceEnvelope envelope, SignalServiceContent content, ReceiveConfig receiveConfig
241 ) {
242 var actions = new ArrayList<HandleAction>();
243 final var senderDeviceAddress = getSender(envelope, content);
244 final var sender = senderDeviceAddress.recipientId();
245 final var senderServiceId = senderDeviceAddress.serviceId();
246 final var senderDeviceId = senderDeviceAddress.deviceId();
247 final var destination = getDestination(envelope);
248
249 if (content.getReceiptMessage().isPresent()) {
250 final var message = content.getReceiptMessage().get();
251 if (message.isDeliveryReceipt()) {
252 account.getMessageSendLogStore()
253 .deleteEntriesForRecipient(message.getTimestamps(), senderServiceId, senderDeviceId);
254 }
255 }
256
257 if (content.getSenderKeyDistributionMessage().isPresent()) {
258 final var message = content.getSenderKeyDistributionMessage().get();
259 final var protocolAddress = senderServiceId.toProtocolAddress(senderDeviceId);
260 logger.debug("Received a sender key distribution message for distributionId {} from {}",
261 message.getDistributionId(),
262 protocolAddress);
263 dependencies.getMessageSender().processSenderKeyDistributionMessage(protocolAddress, message);
264 }
265
266 if (content.getDecryptionErrorMessage().isPresent()) {
267 var message = content.getDecryptionErrorMessage().get();
268 logger.debug("Received a decryption error message from {}.{} (resend request for {})",
269 sender,
270 senderDeviceId,
271 message.getTimestamp());
272 if (message.getDeviceId() == account.getDeviceId()) {
273 handleDecryptionErrorMessage(actions, sender, senderServiceId, senderDeviceId, message);
274 } else {
275 logger.debug("Request is for another one of our devices");
276 }
277 }
278
279 if (content.getDataMessage().isPresent()) {
280 var message = content.getDataMessage().get();
281
282 if (content.isNeedsReceipt()) {
283 actions.add(new SendReceiptAction(sender,
284 SignalServiceReceiptMessage.Type.DELIVERY,
285 message.getTimestamp()));
286 } else {
287 // Message wasn't sent as unidentified sender message
288 final var contact = context.getAccount().getContactStore().getContact(sender);
289 if (account.isPrimaryDevice()
290 && contact != null
291 && !contact.isBlocked()
292 && contact.isProfileSharingEnabled()) {
293 actions.add(UpdateAccountAttributesAction.create());
294 actions.add(new SendProfileKeyAction(sender));
295 }
296 }
297 if (receiveConfig.sendReadReceipts()) {
298 actions.add(new SendReceiptAction(sender,
299 SignalServiceReceiptMessage.Type.READ,
300 message.getTimestamp()));
301 }
302
303 actions.addAll(handleSignalServiceDataMessage(message,
304 false,
305 senderDeviceAddress,
306 destination,
307 receiveConfig.ignoreAttachments()));
308 }
309
310 if (content.getStoryMessage().isPresent()) {
311 final var message = content.getStoryMessage().get();
312 actions.addAll(handleSignalServiceStoryMessage(message, sender, receiveConfig.ignoreAttachments()));
313 }
314
315 if (content.getSyncMessage().isPresent()) {
316 var syncMessage = content.getSyncMessage().get();
317 actions.addAll(handleSyncMessage(envelope,
318 syncMessage,
319 senderDeviceAddress,
320 receiveConfig.ignoreAttachments()));
321 }
322
323 return actions;
324 }
325
326 private void handleDecryptionErrorMessage(
327 final List<HandleAction> actions,
328 final RecipientId sender,
329 final ServiceId senderServiceId,
330 final int senderDeviceId,
331 final DecryptionErrorMessage message
332 ) {
333 final var logEntries = account.getMessageSendLogStore()
334 .findMessages(senderServiceId,
335 senderDeviceId,
336 message.getTimestamp(),
337 message.getRatchetKey().isEmpty());
338
339 for (final var logEntry : logEntries) {
340 actions.add(new ResendMessageAction(sender, message.getTimestamp(), logEntry));
341 }
342
343 if (message.getRatchetKey().isPresent()) {
344 if (account.getAciSessionStore()
345 .isCurrentRatchetKey(senderServiceId, senderDeviceId, message.getRatchetKey().get())) {
346 if (logEntries.isEmpty()) {
347 logger.debug("Renewing the session with sender");
348 actions.add(new RenewSessionAction(sender, senderServiceId));
349 } else {
350 logger.trace("Archiving the session with sender, a resend message has already been queued");
351 context.getAccount().getAciSessionStore().archiveSessions(senderServiceId);
352 }
353 }
354 return;
355 }
356
357 var found = false;
358 for (final var logEntry : logEntries) {
359 if (logEntry.groupId().isEmpty()) {
360 continue;
361 }
362 final var group = account.getGroupStore().getGroup(logEntry.groupId().get());
363 if (group == null) {
364 continue;
365 }
366 found = true;
367 logger.trace("Deleting shared sender key with {} ({}): {}",
368 sender,
369 senderDeviceId,
370 group.getDistributionId());
371 account.getSenderKeyStore().deleteSharedWith(senderServiceId, senderDeviceId, group.getDistributionId());
372 }
373 if (!found) {
374 logger.debug("Reset all shared sender keys with this recipient, no related message found in send log");
375 account.getSenderKeyStore().deleteSharedWith(senderServiceId);
376 }
377 }
378
379 private List<HandleAction> handleSyncMessage(
380 final SignalServiceEnvelope envelope,
381 final SignalServiceSyncMessage syncMessage,
382 final DeviceAddress sender,
383 final boolean ignoreAttachments
384 ) {
385 var actions = new ArrayList<HandleAction>();
386 account.setMultiDevice(true);
387 if (syncMessage.getSent().isPresent()) {
388 var message = syncMessage.getSent().get();
389 final var destination = message.getDestination().orElse(null);
390 if (message.getDataMessage().isPresent()) {
391 actions.addAll(handleSignalServiceDataMessage(message.getDataMessage().get(),
392 true,
393 sender,
394 destination == null
395 ? null
396 : new DeviceAddress(context.getRecipientHelper().resolveRecipient(destination),
397 destination.getServiceId(),
398 0),
399 ignoreAttachments));
400 }
401 if (message.getStoryMessage().isPresent()) {
402 actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(),
403 sender.recipientId(),
404 ignoreAttachments));
405 }
406 }
407 if (syncMessage.getRequest().isPresent() && account.isPrimaryDevice()) {
408 var rm = syncMessage.getRequest().get();
409 if (rm.isContactsRequest()) {
410 actions.add(SendSyncContactsAction.create());
411 }
412 if (rm.isGroupsRequest()) {
413 actions.add(SendSyncGroupsAction.create());
414 }
415 if (rm.isBlockedListRequest()) {
416 actions.add(SendSyncBlockedListAction.create());
417 }
418 if (rm.isKeysRequest()) {
419 actions.add(SendSyncKeysAction.create());
420 }
421 if (rm.isConfigurationRequest()) {
422 actions.add(SendSyncConfigurationAction.create());
423 }
424 if (rm.isPniIdentityRequest()) {
425 actions.add(SendPniIdentityKeyAction.create());
426 }
427 }
428 if (syncMessage.getGroups().isPresent()) {
429 try {
430 final var groupsMessage = syncMessage.getGroups().get();
431 context.getAttachmentHelper()
432 .retrieveAttachment(groupsMessage, context.getSyncHelper()::handleSyncDeviceGroups);
433 } catch (Exception e) {
434 logger.warn("Failed to handle received sync groups, ignoring: {}", e.getMessage());
435 }
436 }
437 if (syncMessage.getBlockedList().isPresent()) {
438 final var blockedListMessage = syncMessage.getBlockedList().get();
439 for (var address : blockedListMessage.getAddresses()) {
440 context.getContactHelper()
441 .setContactBlocked(context.getRecipientHelper().resolveRecipient(address), true);
442 }
443 for (var groupId : blockedListMessage.getGroupIds()
444 .stream()
445 .map(GroupId::unknownVersion)
446 .collect(Collectors.toSet())) {
447 try {
448 context.getGroupHelper().setGroupBlocked(groupId, true);
449 } catch (GroupNotFoundException e) {
450 logger.warn("BlockedListMessage contained groupID that was not found in GroupStore: {}",
451 groupId.toBase64());
452 }
453 }
454 }
455 if (syncMessage.getContacts().isPresent()) {
456 try {
457 final var contactsMessage = syncMessage.getContacts().get();
458 context.getAttachmentHelper()
459 .retrieveAttachment(contactsMessage.getContactsStream(),
460 context.getSyncHelper()::handleSyncDeviceContacts);
461 } catch (Exception e) {
462 logger.warn("Failed to handle received sync contacts, ignoring: {}", e.getMessage());
463 }
464 }
465 if (syncMessage.getVerified().isPresent()) {
466 final var verifiedMessage = syncMessage.getVerified().get();
467 account.getIdentityKeyStore()
468 .setIdentityTrustLevel(verifiedMessage.getDestination().getServiceId(),
469 verifiedMessage.getIdentityKey(),
470 TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
471 }
472 if (syncMessage.getStickerPackOperations().isPresent()) {
473 final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
474 for (var m : stickerPackOperationMessages) {
475 if (m.getPackId().isEmpty()) {
476 continue;
477 }
478 final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
479 final var installed = m.getType().isEmpty()
480 || m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
481
482 var sticker = account.getStickerStore().getStickerPack(stickerPackId);
483 if (m.getPackKey().isPresent()) {
484 if (sticker == null) {
485 sticker = new StickerPack(-1, stickerPackId, m.getPackKey().get(), installed);
486 account.getStickerStore().addStickerPack(sticker);
487 }
488 if (installed) {
489 context.getJobExecutor()
490 .enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
491 }
492 }
493
494 if (sticker != null && sticker.isInstalled() != installed) {
495 account.getStickerStore().updateStickerPackInstalled(sticker.packId(), installed);
496 }
497 }
498 }
499 if (syncMessage.getFetchType().isPresent()) {
500 switch (syncMessage.getFetchType().get()) {
501 case LOCAL_PROFILE:
502 actions.add(new RetrieveProfileAction(account.getSelfRecipientId()));
503 case STORAGE_MANIFEST:
504 actions.add(RetrieveStorageDataAction.create());
505 }
506 }
507 if (syncMessage.getKeys().isPresent()) {
508 final var keysMessage = syncMessage.getKeys().get();
509 if (keysMessage.getStorageService().isPresent()) {
510 final var storageKey = keysMessage.getStorageService().get();
511 account.setStorageKey(storageKey);
512 actions.add(RetrieveStorageDataAction.create());
513 }
514 }
515 if (syncMessage.getConfiguration().isPresent()) {
516 final var configurationMessage = syncMessage.getConfiguration().get();
517 final var configurationStore = account.getConfigurationStore();
518 if (configurationMessage.getReadReceipts().isPresent()) {
519 configurationStore.setReadReceipts(configurationMessage.getReadReceipts().get());
520 }
521 if (configurationMessage.getLinkPreviews().isPresent()) {
522 configurationStore.setLinkPreviews(configurationMessage.getLinkPreviews().get());
523 }
524 if (configurationMessage.getTypingIndicators().isPresent()) {
525 configurationStore.setTypingIndicators(configurationMessage.getTypingIndicators().get());
526 }
527 if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) {
528 configurationStore.setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
529 .get());
530 }
531 }
532 if (syncMessage.getPniIdentity().isPresent()) {
533 final var pniIdentity = syncMessage.getPniIdentity().get();
534 account.setPniIdentityKeyPair(KeyUtils.getIdentityKeyPair(pniIdentity.getPublicKey().toByteArray(),
535 pniIdentity.getPrivateKey().toByteArray()));
536 actions.add(RefreshPreKeysAction.create());
537 }
538 if (syncMessage.getPniChangeNumber().isPresent()) {
539 final var pniChangeNumber = syncMessage.getPniChangeNumber().get();
540 logger.debug("Received PNI change number sync message, applying.");
541 if (pniChangeNumber.hasIdentityKeyPair()
542 && pniChangeNumber.hasRegistrationId()
543 && pniChangeNumber.hasSignedPreKey()
544 && !envelope.getUpdatedPni().isEmpty()) {
545 logger.debug("New PNI: {}", envelope.getUpdatedPni());
546 try {
547 final var updatedPni = PNI.parseOrThrow(envelope.getUpdatedPni());
548 context.getAccountHelper()
549 .setPni(updatedPni,
550 new IdentityKeyPair(pniChangeNumber.getIdentityKeyPair().toByteArray()),
551 new SignedPreKeyRecord(pniChangeNumber.getSignedPreKey().toByteArray()),
552 pniChangeNumber.getRegistrationId());
553 } catch (Exception e) {
554 logger.warn("Failed to handle change number message", e);
555 }
556 }
557 }
558 return actions;
559 }
560
561 private SignalServiceGroupContext getGroupContext(SignalServiceContent content) {
562 if (content == null) {
563 return null;
564 }
565
566 if (content.getDataMessage().isPresent()) {
567 var message = content.getDataMessage().get();
568 if (message.getGroupContext().isPresent()) {
569 return message.getGroupContext().get();
570 }
571 }
572
573 if (content.getStoryMessage().isPresent()) {
574 var message = content.getStoryMessage().get();
575 if (message.getGroupContext().isPresent()) {
576 try {
577 return SignalServiceGroupContext.create(null, message.getGroupContext().get());
578 } catch (InvalidMessageException e) {
579 throw new AssertionError(e);
580 }
581 }
582 }
583
584 return null;
585 }
586
587 private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
588 SignalServiceAddress source;
589 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
590 source = envelope.getSourceAddress();
591 } else if (content != null) {
592 source = content.getSender();
593 } else {
594 return false;
595 }
596 final var recipientId = context.getRecipientHelper().resolveRecipient(source);
597 if (context.getContactHelper().isContactBlocked(recipientId)) {
598 return true;
599 }
600
601 final var groupContext = getGroupContext(content);
602 if (groupContext != null) {
603 var groupId = GroupUtils.getGroupId(groupContext);
604 return context.getGroupHelper().isGroupBlocked(groupId);
605 }
606
607 return false;
608 }
609
610 private boolean isNotAllowedToSendToGroup(SignalServiceEnvelope envelope, SignalServiceContent content) {
611 SignalServiceAddress source;
612 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
613 source = envelope.getSourceAddress();
614 } else if (content != null) {
615 source = content.getSender();
616 } else {
617 return false;
618 }
619
620 final var groupContext = getGroupContext(content);
621 if (groupContext == null) {
622 return false;
623 }
624
625 if (groupContext.getGroupV1().isPresent()) {
626 var groupInfo = groupContext.getGroupV1().get();
627 if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) {
628 return false;
629 }
630 }
631
632 var groupId = GroupUtils.getGroupId(groupContext);
633 var group = context.getGroupHelper().getGroup(groupId);
634 if (group == null) {
635 return false;
636 }
637
638 final var message = content.getDataMessage().orElse(null);
639
640 final var recipientId = context.getRecipientHelper().resolveRecipient(source);
641 if (!group.isMember(recipientId) && !(
642 group.isPendingMember(recipientId) && message != null && message.isGroupV2Update()
643 )) {
644 return true;
645 }
646
647 if (group.isAnnouncementGroup() && !group.isAdmin(recipientId)) {
648 return message == null
649 || message.getBody().isPresent()
650 || message.getAttachments().isPresent()
651 || message.getQuote().isPresent()
652 || message.getPreviews().isPresent()
653 || message.getMentions().isPresent()
654 || message.getSticker().isPresent();
655 }
656 return false;
657 }
658
659 private List<HandleAction> handleSignalServiceDataMessage(
660 SignalServiceDataMessage message,
661 boolean isSync,
662 DeviceAddress source,
663 DeviceAddress destination,
664 boolean ignoreAttachments
665 ) {
666 var actions = new ArrayList<HandleAction>();
667 if (message.getGroupContext().isPresent()) {
668 final var groupContext = message.getGroupContext().get();
669 if (groupContext.getGroupV1().isPresent()) {
670 var groupInfo = groupContext.getGroupV1().get();
671 var groupId = GroupId.v1(groupInfo.getGroupId());
672 var group = context.getGroupHelper().getGroup(groupId);
673 if (group == null || group instanceof GroupInfoV1) {
674 var groupV1 = (GroupInfoV1) group;
675 switch (groupInfo.getType()) {
676 case UPDATE: {
677 if (groupV1 == null) {
678 groupV1 = new GroupInfoV1(groupId);
679 }
680
681 if (groupInfo.getAvatar().isPresent()) {
682 var avatar = groupInfo.getAvatar().get();
683 context.getGroupHelper().downloadGroupAvatar(groupV1.getGroupId(), avatar);
684 }
685
686 if (groupInfo.getName().isPresent()) {
687 groupV1.name = groupInfo.getName().get();
688 }
689
690 if (groupInfo.getMembers().isPresent()) {
691 groupV1.addMembers(groupInfo.getMembers()
692 .get()
693 .stream()
694 .map(context.getRecipientHelper()::resolveRecipient)
695 .collect(Collectors.toSet()));
696 }
697
698 account.getGroupStore().updateGroup(groupV1);
699 break;
700 }
701 case DELIVER:
702 if (groupV1 == null && !isSync) {
703 actions.add(new SendGroupInfoRequestAction(source.recipientId(), groupId));
704 }
705 break;
706 case QUIT: {
707 if (groupV1 != null) {
708 groupV1.removeMember(source.recipientId());
709 account.getGroupStore().updateGroup(groupV1);
710 }
711 break;
712 }
713 case REQUEST_INFO:
714 if (groupV1 != null && !isSync) {
715 actions.add(new SendGroupInfoAction(source.recipientId(), groupV1.getGroupId()));
716 }
717 break;
718 }
719 } else {
720 // Received a group v1 message for a v2 group
721 }
722 }
723 if (groupContext.getGroupV2().isPresent()) {
724 handleGroupV2Context(groupContext.getGroupV2().get());
725 }
726 }
727
728 final var conversationPartnerAddress = isSync ? destination : source;
729 if (conversationPartnerAddress != null && message.isEndSession()) {
730 account.getAciSessionStore().deleteAllSessions(conversationPartnerAddress.serviceId());
731 }
732 if (message.isExpirationUpdate() || message.getBody().isPresent()) {
733 if (message.getGroupContext().isPresent()) {
734 final var groupContext = message.getGroupContext().get();
735 if (groupContext.getGroupV1().isPresent()) {
736 var groupInfo = groupContext.getGroupV1().get();
737 var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
738 if (group != null) {
739 if (group.messageExpirationTime != message.getExpiresInSeconds()) {
740 group.messageExpirationTime = message.getExpiresInSeconds();
741 account.getGroupStore().updateGroup(group);
742 }
743 }
744 } else if (groupContext.getGroupV2().isPresent()) {
745 // disappearing message timer already stored in the DecryptedGroup
746 }
747 } else if (conversationPartnerAddress != null) {
748 context.getContactHelper()
749 .setExpirationTimer(conversationPartnerAddress.recipientId(), message.getExpiresInSeconds());
750 }
751 }
752 if (!ignoreAttachments) {
753 if (message.getAttachments().isPresent()) {
754 for (var attachment : message.getAttachments().get()) {
755 context.getAttachmentHelper().downloadAttachment(attachment);
756 }
757 }
758 if (message.getSharedContacts().isPresent()) {
759 for (var contact : message.getSharedContacts().get()) {
760 if (contact.getAvatar().isPresent()) {
761 context.getAttachmentHelper().downloadAttachment(contact.getAvatar().get().getAttachment());
762 }
763 }
764 }
765 if (message.getPreviews().isPresent()) {
766 final var previews = message.getPreviews().get();
767 for (var preview : previews) {
768 if (preview.getImage().isPresent()) {
769 context.getAttachmentHelper().downloadAttachment(preview.getImage().get());
770 }
771 }
772 }
773 if (message.getQuote().isPresent()) {
774 final var quote = message.getQuote().get();
775
776 for (var quotedAttachment : quote.getAttachments()) {
777 final var thumbnail = quotedAttachment.getThumbnail();
778 if (thumbnail != null) {
779 context.getAttachmentHelper().downloadAttachment(thumbnail);
780 }
781 }
782 }
783 }
784 if (message.getProfileKey().isPresent()) {
785 handleIncomingProfileKey(message.getProfileKey().get(), source.recipientId());
786 }
787 if (message.getSticker().isPresent()) {
788 final var messageSticker = message.getSticker().get();
789 final var stickerPackId = StickerPackId.deserialize(messageSticker.getPackId());
790 var sticker = account.getStickerStore().getStickerPack(stickerPackId);
791 if (sticker == null) {
792 sticker = new StickerPack(stickerPackId, messageSticker.getPackKey());
793 account.getStickerStore().addStickerPack(sticker);
794 }
795 context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey()));
796 }
797 return actions;
798 }
799
800 private List<HandleAction> handleSignalServiceStoryMessage(
801 SignalServiceStoryMessage message, RecipientId source, boolean ignoreAttachments
802 ) {
803 var actions = new ArrayList<HandleAction>();
804 if (message.getGroupContext().isPresent()) {
805 handleGroupV2Context(message.getGroupContext().get());
806 }
807
808 if (!ignoreAttachments) {
809 if (message.getFileAttachment().isPresent()) {
810 context.getAttachmentHelper().downloadAttachment(message.getFileAttachment().get());
811 }
812 if (message.getTextAttachment().isPresent()) {
813 final var textAttachment = message.getTextAttachment().get();
814 if (textAttachment.getPreview().isPresent()) {
815 final var preview = textAttachment.getPreview().get();
816 if (preview.getImage().isPresent()) {
817 context.getAttachmentHelper().downloadAttachment(preview.getImage().get());
818 }
819 }
820 }
821 }
822
823 if (message.getProfileKey().isPresent()) {
824 handleIncomingProfileKey(message.getProfileKey().get(), source);
825 }
826
827 return actions;
828 }
829
830 private void handleGroupV2Context(final SignalServiceGroupV2 groupContext) {
831 final var groupMasterKey = groupContext.getMasterKey();
832
833 context.getGroupHelper()
834 .getOrMigrateGroup(groupMasterKey,
835 groupContext.getRevision(),
836 groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
837 }
838
839 private void handleIncomingProfileKey(final byte[] profileKeyBytes, final RecipientId source) {
840 if (profileKeyBytes.length != 32) {
841 logger.debug("Received invalid profile key of length {}", profileKeyBytes.length);
842 return;
843 }
844 final ProfileKey profileKey;
845 try {
846 profileKey = new ProfileKey(profileKeyBytes);
847 } catch (InvalidInputException e) {
848 throw new AssertionError(e);
849 }
850 if (account.getSelfRecipientId().equals(source)) {
851 this.account.setProfileKey(profileKey);
852 }
853 this.account.getProfileStore().storeProfileKey(source, profileKey);
854 }
855
856 private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
857 if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
858 return new DeviceAddress(context.getRecipientHelper().resolveRecipient(envelope.getSourceAddress()),
859 envelope.getSourceAddress().getServiceId(),
860 envelope.getSourceDevice());
861 } else {
862 return new DeviceAddress(context.getRecipientHelper().resolveRecipient(content.getSender()),
863 content.getSender().getServiceId(),
864 content.getSenderDevice());
865 }
866 }
867
868 private DeviceAddress getDestination(SignalServiceEnvelope envelope) {
869 if (!envelope.hasDestinationUuid()) {
870 return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
871 }
872 final var addressOptional = SignalServiceAddress.fromRaw(envelope.getDestinationUuid(), null);
873 if (addressOptional.isEmpty()) {
874 return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
875 }
876 final var address = addressOptional.get();
877 return new DeviceAddress(context.getRecipientHelper().resolveRecipient(address), address.getServiceId(), 0);
878 }
879
880 private record DeviceAddress(RecipientId recipientId, ServiceId serviceId, int deviceId) {}
881 }