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