whatsapp_rust/
message.rs

1use crate::client::Client;
2use crate::store::signal_adapter::SignalProtocolStoreAdapter;
3use crate::types::events::Event;
4use crate::types::message::MessageInfo;
5use chrono::DateTime;
6use log::warn;
7use prost::Message as ProtoMessage;
8use rand::TryRngCore;
9use std::sync::Arc;
10use wacore::libsignal::crypto::DecryptionError;
11use wacore::libsignal::protocol::SenderKeyDistributionMessage;
12use wacore::libsignal::protocol::group_decrypt;
13use wacore::libsignal::protocol::process_sender_key_distribution_message;
14use wacore::libsignal::protocol::{
15    PreKeySignalMessage, SignalMessage, SignalProtocolError, UsePQRatchet, message_decrypt,
16};
17use wacore::libsignal::protocol::{
18    PublicKey as SignalPublicKey, SENDERKEY_MESSAGE_CURRENT_VERSION,
19};
20use wacore::libsignal::store::sender_key_name::SenderKeyName;
21use wacore::messages::MessageUtils;
22use wacore::types::jid::JidExt;
23use wacore_binary::jid::Jid;
24use wacore_binary::jid::JidExt as _;
25use wacore_binary::node::Node;
26use waproto::whatsapp::{self as wa};
27
28impl Client {
29    pub(crate) async fn handle_encrypted_message(self: Arc<Self>, node: Arc<Node>) {
30        let info = match self.parse_message_info(&node).await {
31            Ok(info) => info,
32            Err(e) => {
33                log::warn!("Failed to parse message info: {e:?}");
34                return;
35            }
36        };
37
38        // Determine the JID to use for end-to-end decryption. Prefer phone-number alt JIDs
39        // for LID senders, but never "upgrade" a PN sender to a LID.
40        let sender_encryption_jid = {
41            let sender = &info.source.sender;
42            let alt = info.source.sender_alt.as_ref();
43            let pn_server = wacore_binary::jid::DEFAULT_USER_SERVER;
44            let lid_server = wacore_binary::jid::HIDDEN_USER_SERVER;
45
46            if sender.server == lid_server {
47                if let Some(alt_jid) = alt {
48                    if alt_jid.server == pn_server {
49                        alt_jid.clone()
50                    } else {
51                        // Alt is another LID variant; stick with the original LID sender.
52                        sender.clone()
53                    }
54                } else if info.source.is_from_me {
55                    // Self-sent LID message without PN alt — try to fall back to our PN identity.
56                    if let Some(own_pn) = self.get_pn().await {
57                        log::debug!(
58                            "Self-sent message from LID {}, using own phone number {}:{} for decryption",
59                            sender,
60                            own_pn.user,
61                            sender.device
62                        );
63                        Jid {
64                            user: own_pn.user,
65                            server: own_pn.server,
66                            agent: own_pn.agent,
67                            device: sender.device,
68                            integrator: own_pn.integrator,
69                        }
70                    } else {
71                        log::warn!("Self-sent message from LID but own phone number not available");
72                        sender.clone()
73                    }
74                } else {
75                    // No PN alt provided and not self-sent. Keep the original LID sender.
76                    sender.clone()
77                }
78            } else {
79                // Sender already uses PN (or another stable server). Never upgrade to LID.
80                sender.clone()
81            }
82        };
83
84        log::debug!(
85            "Message from {} (sender: {}, encryption JID: {}, is_from_me: {})",
86            info.source.chat,
87            info.source.sender,
88            sender_encryption_jid,
89            info.source.is_from_me
90        );
91
92        let mut all_enc_nodes = Vec::new();
93
94        let direct_enc_nodes = node.get_children_by_tag("enc");
95        all_enc_nodes.extend(direct_enc_nodes);
96
97        let participants = node.get_optional_child_by_tag(&["participants"]);
98        if let Some(participants_node) = participants {
99            let to_nodes = participants_node.get_children_by_tag("to");
100            for to_node in to_nodes {
101                let to_jid = to_node.attrs().string("jid");
102                let own_jid = self.get_pn().await;
103
104                if let Some(our_jid) = own_jid
105                    && to_jid == our_jid.to_string()
106                {
107                    let enc_children = to_node.get_children_by_tag("enc");
108                    all_enc_nodes.extend(enc_children);
109                }
110            }
111        }
112
113        if all_enc_nodes.is_empty() {
114            log::warn!("Received message without <enc> child: {}", node.tag);
115            return;
116        }
117
118        let mut session_enc_nodes = Vec::new();
119        let mut group_content_enc_nodes = Vec::new();
120
121        for &enc_node in &all_enc_nodes {
122            let enc_type = enc_node.attrs().string("type");
123
124            // First check for custom handlers
125            if let Some(handler) = self.custom_enc_handlers.get(&enc_type) {
126                let handler_clone = handler.clone();
127                let client_clone = self.clone();
128                let info_clone = info.clone();
129                let enc_node_clone = Arc::new(enc_node.clone());
130
131                tokio::spawn(async move {
132                    if let Err(e) = handler_clone
133                        .handle(client_clone, &enc_node_clone, &info_clone)
134                        .await
135                    {
136                        log::warn!("Custom handler for enc type '{}' failed: {e:?}", enc_type);
137                    }
138                });
139                continue;
140            }
141
142            // Fall back to built-in handlers
143            match enc_type.as_str() {
144                "pkmsg" | "msg" => session_enc_nodes.push(enc_node),
145                "skmsg" => group_content_enc_nodes.push(enc_node),
146                _ => log::warn!("Unknown enc type: {enc_type}"),
147            }
148        }
149
150        log::debug!(
151            "Starting PASS 1: Processing {} session establishment messages (pkmsg/msg)",
152            session_enc_nodes.len()
153        );
154        let (
155            session_decrypted_successfully,
156            session_had_duplicates,
157            session_dispatched_undecryptable,
158        ) = self
159            .clone()
160            .process_session_enc_batch(&session_enc_nodes, &info, &sender_encryption_jid)
161            .await;
162
163        log::debug!(
164            "Starting PASS 2: Processing {} group content messages (skmsg)",
165            group_content_enc_nodes.len()
166        );
167
168        // Only process group content if:
169        // 1. There were no session messages (session already exists), OR
170        // 2. Session messages were successfully decrypted, OR
171        // 3. Session messages were duplicates (already processed, so session exists)
172        // Skip only if session messages FAILED to decrypt (not duplicates, not absent)
173        if !group_content_enc_nodes.is_empty() {
174            let should_process_skmsg = session_enc_nodes.is_empty()
175                || session_decrypted_successfully
176                || session_had_duplicates;
177
178            if should_process_skmsg {
179                if let Err(e) = self
180                    .clone()
181                    .process_group_enc_batch(
182                        &group_content_enc_nodes,
183                        &info,
184                        &sender_encryption_jid,
185                    )
186                    .await
187                {
188                    log::warn!("Batch group decrypt encountered error (continuing): {e:?}");
189                }
190            } else {
191                // Only show warning if session messages actually FAILED (not duplicates)
192                if !session_had_duplicates {
193                    warn!(
194                        "Skipping skmsg decryption for message {} from {} because the initial session/senderkey message failed to decrypt. This prevents a retry loop.",
195                        info.id, info.source.sender
196                    );
197                    // Still dispatch an UndecryptableMessage event so the user knows
198                    // But only if we haven't already dispatched one in process_session_enc_batch
199                    if !session_dispatched_undecryptable {
200                        self.core.event_bus.dispatch(&Event::UndecryptableMessage(
201                            crate::types::events::UndecryptableMessage {
202                                info: info.clone(),
203                                is_unavailable: false,
204                                unavailable_type: crate::types::events::UnavailableType::Unknown,
205                                decrypt_fail_mode: crate::types::events::DecryptFailMode::Show,
206                            },
207                        ));
208                    }
209
210                    // Do NOT send a delivery receipt for undecryptable messages.
211                    // Per whatsmeow's implementation, delivery receipts are only sent for
212                    // successfully decrypted/handled messages. Sending a receipt here would
213                    // tell the server we processed it, incrementing the offline counter.
214                    // The transport <ack> is sufficient for acknowledgment.
215                }
216                // If session_had_duplicates is true, we silently skip (no warning, no event)
217                // because the message was already processed in a previous session
218            }
219        } else if !session_decrypted_successfully
220            && !session_had_duplicates
221            && !session_enc_nodes.is_empty()
222        {
223            // Edge case: message with only msg/pkmsg that failed to decrypt, no skmsg
224            warn!(
225                "Message {} from {} failed to decrypt and has no group content. Dispatching UndecryptableMessage event.",
226                info.id, info.source.sender
227            );
228            // Dispatch UndecryptableMessage event for messages that failed to decrypt
229            // (This should not cause double-dispatching since process_session_enc_batch
230            // already returned dispatched_undecryptable=false for this case)
231            self.core.event_bus.dispatch(&Event::UndecryptableMessage(
232                crate::types::events::UndecryptableMessage {
233                    info: info.clone(),
234                    is_unavailable: false,
235                    unavailable_type: crate::types::events::UnavailableType::Unknown,
236                    decrypt_fail_mode: crate::types::events::DecryptFailMode::Show,
237                },
238            ));
239            // Do NOT send delivery receipt - transport ack is sufficient
240        }
241    }
242
243    async fn process_session_enc_batch(
244        self: Arc<Self>,
245        enc_nodes: &[&wacore_binary::node::Node],
246        info: &MessageInfo,
247        sender_encryption_jid: &Jid,
248    ) -> (bool, bool, bool) {
249        // Returns (any_success, any_duplicate, dispatched_undecryptable)
250        use wacore::libsignal::protocol::CiphertextMessage;
251        if enc_nodes.is_empty() {
252            return (false, false, false);
253        }
254
255        let mut adapter =
256            SignalProtocolStoreAdapter::new(self.persistence_manager.get_device_arc().await);
257        let rng = rand::rngs::OsRng;
258        let mut any_success = false;
259        let mut any_duplicate = false;
260        let mut dispatched_undecryptable = false;
261
262        for enc_node in enc_nodes {
263            let ciphertext = match &enc_node.content {
264                Some(wacore_binary::node::NodeContent::Bytes(b)) => b.clone(),
265                _ => {
266                    log::warn!("Enc node has no byte content (batch session)");
267                    continue;
268                }
269            };
270            let enc_type = enc_node.attrs().string("type");
271            let padding_version = enc_node.attrs().optional_u64("v").unwrap_or(2) as u8;
272
273            let parsed_message = if enc_type == "pkmsg" {
274                match PreKeySignalMessage::try_from(ciphertext.as_slice()) {
275                    Ok(m) => CiphertextMessage::PreKeySignalMessage(m),
276                    Err(e) => {
277                        log::error!("Failed to parse PreKeySignalMessage: {e:?}");
278                        continue;
279                    }
280                }
281            } else {
282                match SignalMessage::try_from(ciphertext.as_slice()) {
283                    Ok(m) => CiphertextMessage::SignalMessage(m),
284                    Err(e) => {
285                        log::error!("Failed to parse SignalMessage: {e:?}");
286                        continue;
287                    }
288                }
289            };
290
291            let signal_address = sender_encryption_jid.to_protocol_address();
292
293            let decrypt_res = message_decrypt(
294                &parsed_message,
295                &signal_address,
296                &mut adapter.session_store,
297                &mut adapter.identity_store,
298                &mut adapter.pre_key_store,
299                &adapter.signed_pre_key_store,
300                &mut rng.unwrap_err(),
301                UsePQRatchet::No,
302            )
303            .await;
304
305            match decrypt_res {
306                Ok(padded_plaintext) => {
307                    any_success = true;
308                    if let Err(e) = self
309                        .clone()
310                        .handle_decrypted_plaintext(
311                            &enc_type,
312                            &padded_plaintext,
313                            padding_version,
314                            info,
315                        )
316                        .await
317                    {
318                        log::warn!("Failed processing plaintext (batch session): {e:?}");
319                    }
320                }
321                Err(e) => {
322                    // Handle DuplicatedMessage: This is expected when messages are redelivered during reconnection
323                    if let SignalProtocolError::DuplicatedMessage(chain, counter) = e {
324                        log::debug!(
325                            "Skipping already-processed message from {} (chain {}, counter {}). This is normal during reconnection.",
326                            info.source.sender,
327                            chain,
328                            counter
329                        );
330                        // Mark that we saw a duplicate so we can skip skmsg without showing error
331                        any_duplicate = true;
332                        continue;
333                    }
334                    // Handle SessionNotFound gracefully during offline sync
335                    if let SignalProtocolError::SessionNotFound(_) = e {
336                        warn!(
337                            "Gracefully failing decryption for {} from {} due to missing session. This is common during offline sync. Dispatching UndecryptableMessage event.",
338                            enc_type, info.source.sender
339                        );
340                        // Dispatch an event so the library user knows a message was missed.
341                        self.core.event_bus.dispatch(&Event::UndecryptableMessage(
342                            crate::types::events::UndecryptableMessage {
343                                info: info.clone(),
344                                is_unavailable: false,
345                                unavailable_type: crate::types::events::UnavailableType::Unknown,
346                                decrypt_fail_mode: crate::types::events::DecryptFailMode::Show,
347                            },
348                        ));
349                        dispatched_undecryptable = true;
350                        // IMPORTANT: Continue the loop instead of returning an error.
351                        continue;
352                    } else {
353                        // For other errors, log them but still don't crash the whole batch.
354                        log::error!("Batch session decrypt failed (type: {}): {:?}", enc_type, e);
355                    }
356                }
357            }
358        }
359        (any_success, any_duplicate, dispatched_undecryptable)
360    }
361
362    async fn process_group_enc_batch(
363        self: Arc<Self>,
364        enc_nodes: &[&wacore_binary::node::Node],
365        info: &MessageInfo,
366        _sender_encryption_jid: &Jid,
367    ) -> Result<(), DecryptionError> {
368        if enc_nodes.is_empty() {
369            return Ok(());
370        }
371        let device_arc = self.persistence_manager.get_device_arc().await;
372
373        for enc_node in enc_nodes {
374            let ciphertext = match &enc_node.content {
375                Some(wacore_binary::node::NodeContent::Bytes(b)) => b.clone(),
376                _ => {
377                    log::warn!("Enc node has no byte content (batch group)");
378                    continue;
379                }
380            };
381            let padding_version = enc_node.attrs().optional_u64("v").unwrap_or(2) as u8;
382
383            // CRITICAL: Use info.source.sender (display JID) for sender key operations, NOT sender_encryption_jid.
384            // The sender key is stored under the sender's display JID (e.g., LID), while sender_encryption_jid
385            // is the phone number used for E2E session decryption only.
386            // Using sender_encryption_jid here causes "No sender key state" errors for self-sent LID messages.
387            let sender_address = info.source.sender.to_protocol_address();
388            let sender_key_name =
389                SenderKeyName::new(info.source.chat.to_string(), sender_address.to_string());
390
391            log::debug!(
392                "Looking up sender key for group {} with sender address {} (from sender JID: {})",
393                info.source.chat,
394                sender_address,
395                info.source.sender
396            );
397
398            let decrypt_result = {
399                let mut device_guard = device_arc.write().await;
400                group_decrypt(ciphertext.as_slice(), &mut *device_guard, &sender_key_name).await
401            };
402
403            match decrypt_result {
404                Ok(padded_plaintext) => {
405                    if let Err(e) = self
406                        .clone()
407                        .handle_decrypted_plaintext(
408                            "skmsg",
409                            &padded_plaintext,
410                            padding_version,
411                            info,
412                        )
413                        .await
414                    {
415                        log::warn!("Failed processing group plaintext (batch): {e:?}");
416                    }
417                }
418                Err(SignalProtocolError::DuplicatedMessage(iteration, counter)) => {
419                    log::debug!(
420                        "Skipping already-processed sender key message from {} in group {} (iteration {}, counter {}). This is normal during reconnection.",
421                        info.source.sender,
422                        info.source.chat,
423                        iteration,
424                        counter
425                    );
426                    // This is expected when messages are redelivered, just continue silently
427                }
428                Err(SignalProtocolError::NoSenderKeyState) => {
429                    warn!(
430                        "No sender key state for batched group message from {}, sending retry receipt.",
431                        info.source.sender
432                    );
433                    let client_clone = self.clone();
434                    let info_clone = info.clone();
435                    tokio::spawn(async move {
436                        if let Err(e) = client_clone.send_retry_receipt(&info_clone).await {
437                            log::error!("Failed to send retry receipt (batch): {:?}", e);
438                        }
439                    });
440                }
441                Err(e) => {
442                    log::error!(
443                        "Group batch decrypt failed for group {} sender {}: {:?}",
444                        sender_key_name.group_id(),
445                        sender_key_name.sender_id(),
446                        e
447                    );
448                }
449            }
450        }
451        Ok(())
452    }
453
454    async fn handle_decrypted_plaintext(
455        self: Arc<Self>,
456        enc_type: &str,
457        padded_plaintext: &[u8],
458        padding_version: u8,
459        info: &MessageInfo,
460    ) -> Result<(), anyhow::Error> {
461        let plaintext_slice = MessageUtils::unpad_message_ref(padded_plaintext, padding_version)?;
462        log::info!(
463            "Successfully decrypted message from {}: {} bytes (type: {}) [batch path]",
464            info.source.sender,
465            plaintext_slice.len(),
466            enc_type
467        );
468
469        if enc_type == "skmsg" {
470            match wa::Message::decode(plaintext_slice) {
471                Ok(group_msg) => {
472                    self.core
473                        .event_bus
474                        .dispatch(&Event::Message(Box::new(group_msg), info.clone()));
475                }
476                Err(e) => log::warn!("Failed to unmarshal decrypted skmsg plaintext: {e}"),
477            }
478        } else {
479            match wa::Message::decode(plaintext_slice) {
480                Ok(original_msg) => {
481                    if let Some(skdm) = &original_msg.sender_key_distribution_message
482                        && let Some(axolotl_bytes) = &skdm.axolotl_sender_key_distribution_message
483                    {
484                        self.handle_sender_key_distribution_message(
485                            &info.source.chat,
486                            &info.source.sender,
487                            axolotl_bytes,
488                        )
489                        .await;
490                    }
491
492                    if let Some(protocol_msg) = &original_msg.protocol_message
493                        && let Some(keys) = &protocol_msg.app_state_sync_key_share
494                    {
495                        self.handle_app_state_sync_key_share(keys).await;
496                    }
497
498                    if let Some(protocol_msg) = &original_msg.protocol_message
499                        && let Some(history_sync) = &protocol_msg.history_sync_notification
500                    {
501                        log::info!(
502                            "Received HistorySyncNotification, dispatching for download and processing."
503                        );
504                        let client_clone = self.clone();
505                        let history_sync_clone = history_sync.clone();
506                        let msg_id = info.id.clone();
507                        tokio::spawn(async move {
508                            // Enqueue history sync task to dedicated worker
509                            client_clone
510                                .handle_history_sync(msg_id, history_sync_clone)
511                                .await;
512                        });
513                    }
514
515                    self.core
516                        .event_bus
517                        .dispatch(&Event::Message(Box::new(original_msg), info.clone()));
518                }
519                Err(e) => log::warn!("Failed to unmarshal decrypted pkmsg/msg plaintext: {e}"),
520            }
521        }
522        Ok(())
523    }
524
525    pub(crate) async fn parse_message_info(
526        &self,
527        node: &Node,
528    ) -> Result<MessageInfo, anyhow::Error> {
529        let mut attrs = node.attrs();
530        let device_snapshot = self.persistence_manager.get_device_snapshot().await;
531        let own_jid = device_snapshot.pn.clone().unwrap_or_default();
532        let own_lid = device_snapshot.lid.clone();
533        let from = attrs.jid("from");
534
535        let mut source = if from.server == wacore_binary::jid::BROADCAST_SERVER {
536            // This is the new logic block for handling all broadcast messages, including status.
537            let participant = attrs.jid("participant");
538            let is_from_me = participant.is_same_user_as(&own_jid)
539                || (own_lid.is_some() && participant.is_same_user_as(own_lid.as_ref().unwrap()));
540
541            crate::types::message::MessageSource {
542                chat: from.clone(),
543                sender: participant.clone(),
544                is_from_me,
545                is_group: true, // Treat as group-like for session handling
546                broadcast_list_owner: if from.user != wacore_binary::jid::STATUS_BROADCAST_USER {
547                    Some(participant.clone())
548                } else {
549                    None
550                },
551                ..Default::default()
552            }
553        } else if from.is_group() {
554            let sender = attrs.jid("participant");
555            let sender_alt = if let Some(addressing_mode) = attrs
556                .optional_string("addressing_mode")
557                .map(|s| s.to_ascii_lowercase())
558            {
559                match addressing_mode.as_str() {
560                    "lid" => attrs.optional_jid("participant_pn"),
561                    _ => attrs.optional_jid("participant_lid"),
562                }
563            } else {
564                None
565            };
566
567            let is_from_me = sender.is_same_user_as(&own_jid)
568                || (own_lid.is_some() && sender.is_same_user_as(own_lid.as_ref().unwrap()));
569
570            crate::types::message::MessageSource {
571                chat: from.clone(),
572                sender: sender.clone(),
573                is_from_me,
574                is_group: true,
575                sender_alt,
576                ..Default::default()
577            }
578        } else if from.is_same_user_as(&own_jid) {
579            crate::types::message::MessageSource {
580                chat: attrs.non_ad_jid("recipient"),
581                sender: from.clone(),
582                is_from_me: true,
583                ..Default::default()
584            }
585        } else {
586            crate::types::message::MessageSource {
587                chat: from.to_non_ad(),
588                sender: from.clone(),
589                is_from_me: false,
590                ..Default::default()
591            }
592        };
593
594        source.addressing_mode = attrs
595            .optional_string("addressing_mode")
596            .map(|s| s.to_ascii_lowercase())
597            .and_then(|s| match s.as_str() {
598                "pn" => Some(crate::types::message::AddressingMode::Pn),
599                "lid" => Some(crate::types::message::AddressingMode::Lid),
600                _ => None,
601            });
602
603        Ok(MessageInfo {
604            source,
605            id: attrs.string("id"),
606            push_name: attrs
607                .optional_string("notify")
608                .map(|s| s.to_string())
609                .unwrap_or_default(),
610            timestamp: DateTime::from_timestamp(attrs.unix_time("t"), 0).unwrap(),
611            ..Default::default()
612        })
613    }
614
615    pub(crate) async fn handle_app_state_sync_key_share(
616        &self,
617        keys: &wa::message::AppStateSyncKeyShare,
618    ) {
619        let device_snapshot = self.persistence_manager.get_device_snapshot().await;
620        let key_store = device_snapshot.backend.clone();
621
622        let mut stored_count = 0;
623        let mut failed_count = 0;
624
625        for key in &keys.keys {
626            if let Some(key_id_proto) = &key.key_id
627                && let Some(key_id) = &key_id_proto.key_id
628                && let Some(key_data) = &key.key_data
629                && let Some(fingerprint) = &key_data.fingerprint
630                && let Some(data) = &key_data.key_data
631            {
632                let fingerprint_bytes = fingerprint.encode_to_vec();
633                let new_key = crate::store::traits::AppStateSyncKey {
634                    key_data: data.clone(),
635                    fingerprint: fingerprint_bytes,
636                    timestamp: key_data.timestamp(),
637                };
638
639                if let Err(e) = key_store.set_app_state_sync_key(key_id, new_key).await {
640                    log::error!(
641                        "Failed to store app state sync key {:?}: {:?}",
642                        hex::encode(key_id),
643                        e
644                    );
645                    failed_count += 1;
646                } else {
647                    stored_count += 1;
648                }
649            }
650        }
651
652        if stored_count > 0 || failed_count > 0 {
653            log::info!(
654                target: "Client/AppState",
655                "Processed app state key share: {} stored, {} failed.",
656                stored_count,
657                failed_count
658            );
659        }
660
661        // Notify any waiters (initial full sync) that at least one key share was processed.
662        if stored_count > 0
663            && !self
664                .initial_app_state_keys_received
665                .swap(true, std::sync::atomic::Ordering::Relaxed)
666        {
667            // First time setting; notify any waiters
668            self.initial_keys_synced_notifier.notify_waiters();
669        }
670    }
671
672    async fn handle_sender_key_distribution_message(
673        self: &Arc<Self>,
674        group_jid: &Jid,
675        sender_jid: &Jid,
676        axolotl_bytes: &[u8],
677    ) {
678        let skdm = match SenderKeyDistributionMessage::try_from(axolotl_bytes) {
679            Ok(msg) => msg,
680            Err(e1) => match wa::SenderKeyDistributionMessage::decode(axolotl_bytes) {
681                Ok(go_msg) => {
682                    match SignalPublicKey::from_djb_public_key_bytes(&go_msg.signing_key.unwrap()) {
683                        Ok(pub_key) => {
684                            match SenderKeyDistributionMessage::new(
685                                SENDERKEY_MESSAGE_CURRENT_VERSION,
686                                go_msg.id.unwrap(),
687                                go_msg.iteration.unwrap(),
688                                go_msg.chain_key.unwrap(),
689                                pub_key,
690                            ) {
691                                Ok(skdm) => skdm,
692                                Err(e) => {
693                                    log::error!(
694                                        "Failed to construct SKDM from Go format from {}: {:?} (original parse error: {:?})",
695                                        sender_jid,
696                                        e,
697                                        e1
698                                    );
699                                    return;
700                                }
701                            }
702                        }
703                        Err(e) => {
704                            log::error!(
705                                "Failed to parse public key from Go SKDM for {}: {:?} (original parse error: {:?})",
706                                sender_jid,
707                                e,
708                                e1
709                            );
710                            return;
711                        }
712                    }
713                }
714                Err(e2) => {
715                    log::error!(
716                        "Failed to parse SenderKeyDistributionMessage (standard and Go fallback) from {}: primary: {:?}, fallback: {:?}",
717                        sender_jid,
718                        e1,
719                        e2
720                    );
721                    return;
722                }
723            },
724        };
725
726        let device_arc = self.persistence_manager.get_device_arc().await;
727        let mut device_guard = device_arc.write().await;
728
729        let sender_address = sender_jid.to_protocol_address();
730
731        let sender_key_name = SenderKeyName::new(group_jid.to_string(), sender_address.to_string());
732
733        if let Err(e) =
734            process_sender_key_distribution_message(&sender_key_name, &skdm, &mut *device_guard)
735                .await
736        {
737            log::error!(
738                "Failed to process SenderKeyDistributionMessage from {}: {:?}",
739                sender_jid,
740                e
741            );
742        } else {
743            log::info!(
744                "Successfully processed sender key distribution for group {} from {}",
745                group_jid,
746                sender_jid
747            );
748        }
749    }
750}
751
752#[cfg(test)]
753mod tests {
754    use super::*;
755    use crate::store::persistence_manager::PersistenceManager;
756    use crate::store::sqlite_store::SqliteStore;
757    use std::sync::Arc;
758    use wacore_binary::builder::NodeBuilder;
759    use wacore_binary::jid::Jid;
760
761    #[tokio::test]
762    async fn test_parse_message_info_for_status_broadcast() {
763        // 1. Setup
764        let backend = Arc::new(
765            SqliteStore::new("file:memdb_status_test?mode=memory&cache=shared")
766                .await
767                .expect("Failed to create test backend"),
768        );
769        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
770        let (client, _sync_rx) = Client::new(pm).await;
771
772        let participant_jid_str = "556899336555:42@s.whatsapp.net";
773        let status_broadcast_jid_str = "status@broadcast";
774
775        // 2. Create the test node mirroring the logs
776        let node = NodeBuilder::new("message")
777            .attr("from", status_broadcast_jid_str)
778            .attr("id", "8A8CCCC7E6E466D9EE8CA11A967E485A")
779            .attr("participant", participant_jid_str)
780            .attr("t", "1759295366")
781            .attr("type", "media")
782            .build();
783
784        // 3. Run the function under test
785        let info = client
786            .parse_message_info(&node)
787            .await
788            .expect("parse_message_info should not fail");
789
790        // 4. Assert the correct behavior
791        let expected_sender: Jid = participant_jid_str.parse().unwrap();
792        let expected_chat: Jid = status_broadcast_jid_str.parse().unwrap();
793
794        assert_eq!(
795            info.source.sender, expected_sender,
796            "The sender should be the 'participant' JID, not 'status@broadcast'"
797        );
798        assert_eq!(
799            info.source.chat, expected_chat,
800            "The chat should be 'status@broadcast'"
801        );
802        assert!(
803            info.source.is_group,
804            "Broadcast messages should be treated as group-like"
805        );
806    }
807
808    #[tokio::test]
809    async fn test_process_session_enc_batch_handles_session_not_found_gracefully() {
810        use wacore::libsignal::protocol::{IdentityKeyPair, KeyPair, SignalMessage};
811
812        // 1. Setup
813        let backend = Arc::new(
814            SqliteStore::new("file:memdb_graceful_fail?mode=memory&cache=shared")
815                .await
816                .expect("Failed to create test backend"),
817        );
818        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
819        let (client, _sync_rx) = Client::new(pm).await;
820
821        let sender_jid: Jid = "1234567890@s.whatsapp.net".parse().unwrap();
822        let info = MessageInfo {
823            source: crate::types::message::MessageSource {
824                sender: sender_jid.clone(),
825                chat: sender_jid.clone(),
826                ..Default::default()
827            },
828            ..Default::default()
829        };
830
831        // 2. Create a valid but undecryptable SignalMessage (encrypted with a dummy key)
832        let dummy_key = [0u8; 32];
833        let sender_ratchet = KeyPair::generate(&mut rand::rngs::OsRng.unwrap_err()).public_key;
834        let sender_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
835        let receiver_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
836        let signal_message = SignalMessage::new(
837            4,
838            &dummy_key,
839            sender_ratchet,
840            0,
841            0,
842            b"test",
843            sender_identity_pair.identity_key(),
844            receiver_identity_pair.identity_key(),
845        )
846        .unwrap();
847
848        let enc_node = NodeBuilder::new("enc")
849            .attr("type", "msg")
850            .bytes(signal_message.serialized().to_vec())
851            .build();
852        let enc_nodes = vec![&enc_node];
853
854        // 3. Run the function under test
855        // The function now returns (any_success, any_duplicate, dispatched_undecryptable).
856        // With a SessionNotFound error, it should return (false, false, true) since it dispatches an event.
857        let (success, had_duplicates, dispatched) = client
858            .process_session_enc_batch(&enc_nodes, &info, &sender_jid)
859            .await;
860
861        // 4. Assert the desired behavior: the function continues gracefully
862        // The function should return (false, false, true) (no successful decryption, no duplicates, but dispatched event)
863        assert!(
864            !success && !had_duplicates && dispatched,
865            "process_session_enc_batch should return (false, false, true) when SessionNotFound occurs and dispatches event"
866        );
867
868        // Note: Verifying event dispatch would require adding a test event handler.
869        // For this test, we're just ensuring the function doesn't panic and returns the correct status.
870    }
871
872    #[tokio::test]
873    async fn test_handle_encrypted_message_skips_skmsg_after_msg_failure() {
874        use wacore::libsignal::protocol::{IdentityKeyPair, KeyPair, SignalMessage};
875
876        // 1. Setup
877        let backend = Arc::new(
878            SqliteStore::new("file:memdb_skip_skmsg_test?mode=memory&cache=shared")
879                .await
880                .expect("Failed to create test backend"),
881        );
882        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
883        let (client, _sync_rx) = Client::new(pm).await;
884
885        let sender_jid: Jid = "1234567890@s.whatsapp.net".parse().unwrap();
886        let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
887
888        // 2. Create a message node with both msg and skmsg
889        // The msg will fail to decrypt (no session), so skmsg should be skipped
890        let dummy_key = [0u8; 32];
891        let sender_ratchet = KeyPair::generate(&mut rand::rngs::OsRng.unwrap_err()).public_key;
892        let sender_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
893        let receiver_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
894        let signal_message = SignalMessage::new(
895            4,
896            &dummy_key,
897            sender_ratchet,
898            0,
899            0,
900            b"test",
901            sender_identity_pair.identity_key(),
902            receiver_identity_pair.identity_key(),
903        )
904        .unwrap();
905
906        let msg_node = NodeBuilder::new("enc")
907            .attr("type", "msg")
908            .bytes(signal_message.serialized().to_vec())
909            .build();
910
911        let skmsg_node = NodeBuilder::new("enc")
912            .attr("type", "skmsg")
913            .bytes(vec![4, 5, 6])
914            .build();
915
916        let message_node = Arc::new(
917            NodeBuilder::new("message")
918                .attr("from", group_jid.to_string())
919                .attr("participant", sender_jid.to_string())
920                .attr("id", "test-id-123")
921                .attr("t", "12345")
922                .children(vec![msg_node, skmsg_node])
923                .build(),
924        );
925
926        // 3. Run the function
927        // This should NOT panic or cause a retry loop. The skmsg should be skipped.
928        client.handle_encrypted_message(message_node).await;
929
930        // 4. Assert
931        // If we get here without panicking, the test passes.
932        // The key improvement is that we won't send a retry receipt for the skmsg
933        // since we detected the msg failure and skipped skmsg processing entirely.
934    }
935
936    /// Test case for reproducing LID group message decryption failure
937    /// when no 1-on-1 Signal session exists with the LID sender.
938    ///
939    /// Context:
940    /// - LID (Lightweight Identity) is WhatsApp's new identity system
941    /// - LID JIDs use format: `236395184570386.1:75@lid` (note the dot)
942    /// - Group messages from LID users fail to decrypt if we lack a Signal session
943    /// - This causes SessionNotFound errors which we now handle gracefully
944    ///
945    /// Expected behavior:
946    /// - No crash or panic
947    /// - UndecryptableMessage event dispatched
948    /// - No delivery receipt sent (only transport ack)
949    /// - Offline counter increments on reconnection (expected)
950    ///
951    /// Solution needed:
952    /// - Implement proactive session establishment with LID contacts
953    /// - Possibly fetch pre-keys when encountering new LID senders in groups
954    #[tokio::test]
955    #[ignore = "Requires valid whatsapp.db with active session to reproduce. This is a known issue documented in README.md"]
956    async fn test_lid_group_message_without_session() {
957        use crate::store::sqlite_store::SqliteStore;
958        use std::sync::Arc;
959        use wacore_binary::builder::NodeBuilder;
960        use wacore_binary::jid::Jid;
961
962        // This test reproduces the real-world scenario where:
963        // 1. A LID user (e.g., 236395184570386.1:75@lid) sends a group message
964        // 2. We don't have a 1-on-1 Signal session with this LID user
965        // 3. The message contains both PreKeySignalMessage (pkmsg) and SenderKeyDistributionMessage (skmsg)
966        // 4. Decryption fails with SessionNotFound
967
968        // Setup: Create client with real database
969        let backend = Arc::new(
970            SqliteStore::new("whatsapp.db")
971                .await
972                .expect("Failed to open whatsapp.db - ensure you have an authenticated session"),
973        );
974        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
975        let (client, _sync_rx) = Client::new(pm).await;
976
977        // Simulate a group message from a LID user we haven't chatted with 1-on-1
978        let lid_sender: Jid = "236395184570386.1:75@lid".parse().unwrap();
979        let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
980
981        // In reality, this would contain actual encrypted pkmsg and skmsg bytes
982        // For now, we're just documenting the structure
983        let pkmsg_node = NodeBuilder::new("enc")
984            .attr("type", "pkmsg")
985            .attr("v", "2")
986            .bytes(vec![/* actual pkmsg bytes */])
987            .build();
988
989        let skmsg_node = NodeBuilder::new("enc")
990            .attr("type", "skmsg")
991            .attr("v", "2")
992            .bytes(vec![/* actual skmsg bytes */])
993            .build();
994
995        let message_node = Arc::new(
996            NodeBuilder::new("message")
997                .attr("from", group_jid.to_string())
998                .attr("participant", lid_sender.to_string())
999                .attr("id", "LID_TEST_MSG_001")
1000                .attr("t", "1759296831")
1001                .attr("type", "text")
1002                .attr("notify", "LID Test User")
1003                .attr("offline", "1")
1004                .attr("addressing_mode", "lid")
1005                .children(vec![pkmsg_node, skmsg_node])
1006                .build(),
1007        );
1008
1009        // Attempt to handle the message
1010        // Expected: Graceful failure, no crash, UndecryptableMessage event dispatched
1011        client.handle_encrypted_message(message_node).await;
1012
1013        // If we reach here without panicking, the graceful handling works
1014        // However, the message remains undecryptable until we:
1015        // 1. Establish a Signal session with the LID user (send them a 1-on-1 message)
1016        // 2. OR implement proactive pre-key fetching for LID contacts
1017        // 3. OR implement session-less SKDM decryption if protocol allows
1018
1019        println!("āœ… LID message handled gracefully (but not decrypted - this is the known issue)");
1020    }
1021
1022    /// Test case for reproducing sender key JID mismatch in LID group messages
1023    ///
1024    /// Problem:
1025    /// - When we process sender key distribution from a self-sent LID message, we store it under the LID JID
1026    /// - But when we try to decrypt the group content (skmsg), we look it up using the phone number JID
1027    /// - This causes "No sender key state" errors even though we just processed the sender key!
1028    ///
1029    /// This test verifies the fix by:
1030    /// 1. Creating a sender key and storing it under the LID address (mimicking SKDM processing)
1031    /// 2. Attempting retrieval with phone number address (the bug) - should fail
1032    /// 3. Attempting retrieval with LID address (the fix) - should succeed
1033    #[tokio::test]
1034    async fn test_self_sent_lid_group_message_sender_key_mismatch() {
1035        use crate::store::sqlite_store::SqliteStore;
1036        use std::sync::Arc;
1037        use wacore::libsignal::protocol::{
1038            SenderKeyStore, create_sender_key_distribution_message,
1039            process_sender_key_distribution_message,
1040        };
1041        use wacore::libsignal::store::sender_key_name::SenderKeyName;
1042
1043        // Setup
1044        let backend = Arc::new(
1045            SqliteStore::new("file:memdb_sender_key_test?mode=memory&cache=shared")
1046                .await
1047                .expect("Failed to create test backend"),
1048        );
1049        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1050        let (_client, _sync_rx) = Client::new(pm.clone()).await;
1051
1052        // Simulate own LID: 236395184570386.1:75@lid (note: using device 75 to match real scenario)
1053        // Phone number: 559984726662:75@s.whatsapp.net
1054        let own_lid: Jid = "236395184570386.1:75@lid".parse().unwrap();
1055        let own_phone: Jid = "559984726662:75@s.whatsapp.net".parse().unwrap();
1056        let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1057
1058        // Step 1: Create a real sender key distribution message using LID address
1059        // This mimics what happens in handle_sender_key_distribution_message
1060        let lid_protocol_address = own_lid.to_protocol_address();
1061        let lid_sender_key_name =
1062            SenderKeyName::new(group_jid.to_string(), lid_protocol_address.to_string());
1063
1064        let device_arc = pm.get_device_arc().await;
1065        let skdm = {
1066            let mut device_guard = device_arc.write().await;
1067            create_sender_key_distribution_message(
1068                &lid_sender_key_name,
1069                &mut *device_guard,
1070                &mut rand::rngs::OsRng.unwrap_err(),
1071            )
1072            .await
1073            .expect("Failed to create SKDM")
1074        };
1075
1076        // Step 2: Process the SKDM to ensure it's stored properly
1077        {
1078            let mut device_guard = device_arc.write().await;
1079            process_sender_key_distribution_message(
1080                &lid_sender_key_name,
1081                &skdm,
1082                &mut *device_guard,
1083            )
1084            .await
1085            .expect("Failed to process SKDM with LID address");
1086        }
1087
1088        println!(
1089            "āœ… Step 1: Stored sender key under LID address: {}",
1090            lid_protocol_address
1091        );
1092
1093        // Step 3: Try to retrieve using PHONE NUMBER address (THE BUG)
1094        let phone_protocol_address = own_phone.to_protocol_address();
1095        let phone_sender_key_name =
1096            SenderKeyName::new(group_jid.to_string(), phone_protocol_address.to_string());
1097
1098        let phone_lookup_result = {
1099            let mut device_guard = device_arc.write().await;
1100            device_guard.load_sender_key(&phone_sender_key_name).await
1101        };
1102
1103        println!(
1104            "āŒ Step 2: Lookup with phone number address failed (expected): {}",
1105            phone_protocol_address
1106        );
1107        assert!(
1108            phone_lookup_result.unwrap().is_none(),
1109            "Sender key should NOT be found when looking up with phone number address (this demonstrates the bug)"
1110        );
1111
1112        // Step 4: Try to retrieve using LID address (THE FIX)
1113        let lid_lookup_result = {
1114            let mut device_guard = device_arc.write().await;
1115            device_guard.load_sender_key(&lid_sender_key_name).await
1116        };
1117
1118        println!("āœ… Step 3: Lookup with LID address succeeded (this is the fix)");
1119        assert!(
1120            lid_lookup_result.unwrap().is_some(),
1121            "Sender key SHOULD be found when looking up with LID address (same as storage)"
1122        );
1123
1124        println!("\nšŸŽÆ Summary:");
1125        println!("   - LID protocol address: {}", lid_protocol_address);
1126        println!("   - Phone protocol address: {}", phone_protocol_address);
1127        println!(
1128            "   - Storage key format: {}:{}",
1129            group_jid, lid_protocol_address
1130        );
1131        println!("   - Bug: Using phone address for lookup after storing with LID address");
1132        println!("   - Fix: Always use info.source.sender (LID) for both storage and retrieval");
1133    }
1134
1135    /// Test that sender key consistency is maintained for multiple LID participants
1136    ///
1137    /// Edge case: Group with multiple LID participants, each should have their own
1138    /// sender key stored under their LID address, not mixed up with phone numbers.
1139    #[tokio::test]
1140    async fn test_multiple_lid_participants_sender_key_isolation() {
1141        use crate::store::sqlite_store::SqliteStore;
1142        use std::sync::Arc;
1143        use wacore::libsignal::protocol::{
1144            SenderKeyStore, create_sender_key_distribution_message,
1145            process_sender_key_distribution_message,
1146        };
1147        use wacore::libsignal::store::sender_key_name::SenderKeyName;
1148
1149        let backend = Arc::new(
1150            SqliteStore::new("file:memdb_multi_lid_test?mode=memory&cache=shared")
1151                .await
1152                .expect("Failed to create test backend"),
1153        );
1154        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1155        let (_client, _sync_rx) = Client::new(pm.clone()).await;
1156
1157        let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1158
1159        // Simulate three LID participants
1160        let participants = vec![
1161            ("236395184570386.1:75@lid", "559984726662:75@s.whatsapp.net"),
1162            ("987654321000000.2:42@lid", "551234567890:42@s.whatsapp.net"),
1163            ("111222333444555.3:10@lid", "559876543210:10@s.whatsapp.net"),
1164        ];
1165
1166        let device_arc = pm.get_device_arc().await;
1167
1168        // Create and store sender keys for each participant under their LID address
1169        for (lid_str, _phone_str) in &participants {
1170            let lid_jid: Jid = lid_str.parse().unwrap();
1171            let lid_protocol_address = lid_jid.to_protocol_address();
1172            let lid_sender_key_name =
1173                SenderKeyName::new(group_jid.to_string(), lid_protocol_address.to_string());
1174
1175            let skdm = {
1176                let mut device_guard = device_arc.write().await;
1177                create_sender_key_distribution_message(
1178                    &lid_sender_key_name,
1179                    &mut *device_guard,
1180                    &mut rand::rngs::OsRng.unwrap_err(),
1181                )
1182                .await
1183                .expect("Failed to create SKDM")
1184            };
1185
1186            let mut device_guard = device_arc.write().await;
1187            process_sender_key_distribution_message(
1188                &lid_sender_key_name,
1189                &skdm,
1190                &mut *device_guard,
1191            )
1192            .await
1193            .expect("Failed to process SKDM");
1194        }
1195
1196        // Verify each participant's sender key can be retrieved using their LID address
1197        for (lid_str, phone_str) in &participants {
1198            let lid_jid: Jid = lid_str.parse().unwrap();
1199            let phone_jid: Jid = phone_str.parse().unwrap();
1200
1201            let lid_protocol_address = lid_jid.to_protocol_address();
1202            let phone_protocol_address = phone_jid.to_protocol_address();
1203
1204            let lid_sender_key_name =
1205                SenderKeyName::new(group_jid.to_string(), lid_protocol_address.to_string());
1206            let phone_sender_key_name =
1207                SenderKeyName::new(group_jid.to_string(), phone_protocol_address.to_string());
1208
1209            // Should find with LID address
1210            let lid_lookup = {
1211                let mut device_guard = device_arc.write().await;
1212                device_guard.load_sender_key(&lid_sender_key_name).await
1213            };
1214            assert!(
1215                lid_lookup.unwrap().is_some(),
1216                "Sender key for {} should be found with LID address",
1217                lid_str
1218            );
1219
1220            // Should NOT find with phone number address (the bug)
1221            let phone_lookup = {
1222                let mut device_guard = device_arc.write().await;
1223                device_guard.load_sender_key(&phone_sender_key_name).await
1224            };
1225            assert!(
1226                phone_lookup.unwrap().is_none(),
1227                "Sender key for {} should NOT be found with phone number address",
1228                lid_str
1229            );
1230        }
1231
1232        println!(
1233            "āœ… All {} LID participants have isolated sender keys",
1234            participants.len()
1235        );
1236    }
1237
1238    /// Test that LID JID parsing handles various edge cases correctly
1239    ///
1240    /// Edge cases:
1241    /// - LID with multiple dots in user portion
1242    /// - LID with device numbers
1243    /// - LID without device numbers
1244    #[test]
1245    fn test_lid_jid_parsing_edge_cases() {
1246        use wacore_binary::jid::Jid;
1247
1248        // Single dot in user portion
1249        let lid1: Jid = "236395184570386.1:75@lid".parse().unwrap();
1250        assert_eq!(lid1.user, "236395184570386.1");
1251        assert_eq!(lid1.device, 75);
1252        assert_eq!(lid1.agent, 0);
1253
1254        // Multiple dots in user portion (extreme edge case)
1255        let lid2: Jid = "123.456.789.0:50@lid".parse().unwrap();
1256        assert_eq!(lid2.user, "123.456.789.0");
1257        assert_eq!(lid2.device, 50);
1258        assert_eq!(lid2.agent, 0);
1259
1260        // No device number (device 0)
1261        let lid3: Jid = "987654321000000.5@lid".parse().unwrap();
1262        assert_eq!(lid3.user, "987654321000000.5");
1263        assert_eq!(lid3.device, 0);
1264        assert_eq!(lid3.agent, 0);
1265
1266        // Very long user portion with dot
1267        let lid4: Jid = "111222333444555666777.999:1@lid".parse().unwrap();
1268        assert_eq!(lid4.user, "111222333444555666777.999");
1269        assert_eq!(lid4.device, 1);
1270        assert_eq!(lid4.agent, 0);
1271    }
1272
1273    /// Test that protocol address generation from LID JIDs is consistent
1274    ///
1275    /// Critical: The protocol address must not add unwanted suffixes for LID addresses
1276    /// with dots in the user portion, which was causing sender key lookup failures.
1277    #[test]
1278    fn test_lid_protocol_address_consistency() {
1279        use wacore::types::jid::JidExt as CoreJidExt;
1280        use wacore_binary::jid::Jid;
1281
1282        let test_cases = vec![
1283            ("236395184570386.1:75@lid", "236395184570386.1", 75),
1284            ("987654321000000.2:42@lid", "987654321000000.2", 42),
1285            ("111.222.333:10@lid", "111.222.333", 10),
1286        ];
1287
1288        for (jid_str, expected_name, expected_device) in test_cases {
1289            let lid_jid: Jid = jid_str.parse().unwrap();
1290            let protocol_addr = lid_jid.to_protocol_address();
1291
1292            assert_eq!(
1293                protocol_addr.name(),
1294                expected_name,
1295                "Protocol address name should match user portion exactly for {}",
1296                jid_str
1297            );
1298            assert_eq!(
1299                u32::from(protocol_addr.device_id()),
1300                expected_device,
1301                "Protocol address device should match for {}",
1302                jid_str
1303            );
1304        }
1305    }
1306
1307    /// Test sender_alt extraction from message attributes in LID groups
1308    ///
1309    /// Edge cases:
1310    /// - LID group with participant_pn attribute
1311    /// - PN group with participant_lid attribute
1312    /// - Mixed addressing modes
1313    #[tokio::test]
1314    async fn test_parse_message_info_sender_alt_extraction() {
1315        use crate::store::sqlite_store::SqliteStore;
1316        use std::sync::Arc;
1317        use wacore_binary::builder::NodeBuilder;
1318
1319        let backend = Arc::new(
1320            SqliteStore::new("file:memdb_sender_alt_test?mode=memory&cache=shared")
1321                .await
1322                .expect("Failed to create test backend"),
1323        );
1324        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1325
1326        // Set up own phone number and LID
1327        {
1328            let device_arc = pm.get_device_arc().await;
1329            let mut device = device_arc.write().await;
1330            device.pn = Some("559984726662@s.whatsapp.net".parse().unwrap());
1331            device.lid = Some("236395184570386.1@lid".parse().unwrap());
1332        }
1333
1334        let (client, _sync_rx) = Client::new(pm).await;
1335
1336        // Test case 1: LID group message with participant_pn
1337        let lid_group_node = NodeBuilder::new("message")
1338            .attr("from", "120363021033254949@g.us")
1339            .attr("participant", "987654321000000.2:42@lid")
1340            .attr("participant_pn", "551234567890:42@s.whatsapp.net")
1341            .attr("addressing_mode", "lid")
1342            .attr("id", "test1")
1343            .attr("t", "12345")
1344            .build();
1345
1346        let info1 = client.parse_message_info(&lid_group_node).await.unwrap();
1347        assert_eq!(info1.source.sender.user, "987654321000000.2");
1348        assert!(info1.source.sender_alt.is_some());
1349        assert_eq!(
1350            info1.source.sender_alt.as_ref().unwrap().user,
1351            "551234567890"
1352        );
1353
1354        // Test case 2: Self-sent LID group message
1355        let self_lid_node = NodeBuilder::new("message")
1356            .attr("from", "120363021033254949@g.us")
1357            .attr("participant", "236395184570386.1:75@lid")
1358            .attr("participant_pn", "559984726662:75@s.whatsapp.net")
1359            .attr("addressing_mode", "lid")
1360            .attr("id", "test2")
1361            .attr("t", "12346")
1362            .build();
1363
1364        let info2 = client.parse_message_info(&self_lid_node).await.unwrap();
1365        assert!(
1366            info2.source.is_from_me,
1367            "Should detect self-sent LID message"
1368        );
1369        assert_eq!(info2.source.sender.user, "236395184570386.1");
1370        assert!(info2.source.sender_alt.is_some());
1371        assert_eq!(
1372            info2.source.sender_alt.as_ref().unwrap().user,
1373            "559984726662"
1374        );
1375
1376        println!("āœ… sender_alt extraction working correctly for LID groups");
1377    }
1378
1379    /// Test that device query logic uses phone numbers for LID participants
1380    ///
1381    /// This is a unit test for the logic in wacore/src/send.rs that converts
1382    /// LID JIDs to phone number JIDs for device queries.
1383    #[test]
1384    fn test_lid_to_phone_mapping_for_device_queries() {
1385        use std::collections::HashMap;
1386        use wacore::client::context::GroupInfo;
1387        use wacore::types::message::AddressingMode;
1388        use wacore_binary::jid::Jid;
1389
1390        // Simulate a LID group with phone number mappings
1391        let mut lid_to_pn_map = HashMap::new();
1392        lid_to_pn_map.insert(
1393            "236395184570386.1".to_string(),
1394            "559984726662@s.whatsapp.net".parse().unwrap(),
1395        );
1396        lid_to_pn_map.insert(
1397            "987654321000000.2".to_string(),
1398            "551234567890@s.whatsapp.net".parse().unwrap(),
1399        );
1400
1401        let mut group_info = GroupInfo::new(
1402            vec![
1403                "236395184570386.1:75@lid".parse().unwrap(),
1404                "987654321000000.2:42@lid".parse().unwrap(),
1405            ],
1406            AddressingMode::Lid,
1407        );
1408        group_info.set_lid_to_pn_map(lid_to_pn_map.clone());
1409
1410        // Simulate the device query logic
1411        let jids_to_query: Vec<Jid> = group_info
1412            .participants
1413            .iter()
1414            .map(|jid| {
1415                let base_jid = jid.to_non_ad();
1416                if base_jid.server == "lid"
1417                    && let Some(phone_jid) = group_info.phone_jid_for_lid_user(&base_jid.user)
1418                {
1419                    return phone_jid.to_non_ad();
1420                }
1421                base_jid
1422            })
1423            .collect();
1424
1425        // Verify all queries use phone numbers, not LID JIDs
1426        for jid in &jids_to_query {
1427            assert_eq!(
1428                jid.server, "s.whatsapp.net",
1429                "Device query should use phone number, got: {}",
1430                jid
1431            );
1432        }
1433
1434        assert_eq!(jids_to_query.len(), 2);
1435        assert!(jids_to_query.iter().any(|j| j.user == "559984726662"));
1436        assert!(jids_to_query.iter().any(|j| j.user == "551234567890"));
1437
1438        println!("āœ… LID-to-phone mapping working correctly for device queries");
1439    }
1440
1441    /// Test edge case: Group with mixed LID and phone number participants
1442    ///
1443    /// Some participants may still use phone numbers even in a LID group.
1444    /// The code should handle both correctly.
1445    #[test]
1446    fn test_mixed_lid_and_phone_participants() {
1447        use std::collections::HashMap;
1448        use wacore::client::context::GroupInfo;
1449        use wacore::types::message::AddressingMode;
1450        use wacore_binary::jid::Jid;
1451
1452        let mut lid_to_pn_map = HashMap::new();
1453        lid_to_pn_map.insert(
1454            "236395184570386.1".to_string(),
1455            "559984726662@s.whatsapp.net".parse().unwrap(),
1456        );
1457
1458        let mut group_info = GroupInfo::new(
1459            vec![
1460                "236395184570386.1:75@lid".parse().unwrap(), // LID participant
1461                "551234567890:42@s.whatsapp.net".parse().unwrap(), // Phone number participant
1462            ],
1463            AddressingMode::Lid,
1464        );
1465        group_info.set_lid_to_pn_map(lid_to_pn_map.clone());
1466
1467        let jids_to_query: Vec<Jid> = group_info
1468            .participants
1469            .iter()
1470            .map(|jid| {
1471                let base_jid = jid.to_non_ad();
1472                if base_jid.server == "lid"
1473                    && let Some(phone_jid) = group_info.phone_jid_for_lid_user(&base_jid.user)
1474                {
1475                    return phone_jid.to_non_ad();
1476                }
1477                base_jid
1478            })
1479            .collect();
1480
1481        // Both should end up as phone numbers
1482        assert_eq!(jids_to_query.len(), 2);
1483        for jid in &jids_to_query {
1484            assert_eq!(jid.server, "s.whatsapp.net");
1485        }
1486
1487        println!("āœ… Mixed LID and phone number participants handled correctly");
1488    }
1489
1490    /// Test edge case: Own JID check in LID mode
1491    ///
1492    /// When checking if own JID is in the participant list, we must use
1493    /// the phone number equivalent if in LID mode, not the LID itself.
1494    #[test]
1495    fn test_own_jid_check_in_lid_mode() {
1496        use std::collections::HashMap;
1497        use wacore_binary::jid::Jid;
1498
1499        let own_lid: Jid = "236395184570386.1@lid".parse().unwrap();
1500        let own_phone: Jid = "559984726662@s.whatsapp.net".parse().unwrap();
1501
1502        let mut lid_to_pn_map = HashMap::new();
1503        lid_to_pn_map.insert("236395184570386.1".to_string(), own_phone.clone());
1504
1505        // Simulate the own JID check logic from wacore/src/send.rs
1506        let own_base_jid = own_lid.to_non_ad();
1507        let own_jid_to_check = if own_base_jid.server == "lid" {
1508            lid_to_pn_map
1509                .get(&own_base_jid.user)
1510                .map(|pn| pn.to_non_ad())
1511                .unwrap_or_else(|| own_base_jid.clone())
1512        } else {
1513            own_base_jid.clone()
1514        };
1515
1516        // Verify we're checking using the phone number
1517        assert_eq!(own_jid_to_check.user, "559984726662");
1518        assert_eq!(own_jid_to_check.server, "s.whatsapp.net");
1519
1520        println!("āœ… Own JID check correctly uses phone number in LID mode");
1521    }
1522
1523    /// Test that sender key operations always use the display JID (LID)
1524    /// regardless of what JID is used for E2E session decryption
1525    #[tokio::test]
1526    async fn test_sender_key_always_uses_display_jid() {
1527        use crate::store::sqlite_store::SqliteStore;
1528        use std::sync::Arc;
1529        use wacore::libsignal::protocol::{SenderKeyStore, create_sender_key_distribution_message};
1530        use wacore::libsignal::store::sender_key_name::SenderKeyName;
1531
1532        let backend = Arc::new(
1533            SqliteStore::new("file:memdb_display_jid_test?mode=memory&cache=shared")
1534                .await
1535                .expect("Failed to create test backend"),
1536        );
1537        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1538        let (_client, _sync_rx) = Client::new(pm.clone()).await;
1539
1540        let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1541        let display_jid: Jid = "236395184570386.1:75@lid".parse().unwrap();
1542        let encryption_jid: Jid = "559984726662:75@s.whatsapp.net".parse().unwrap();
1543
1544        // Store sender key using display JID (LID)
1545        let display_protocol_address = display_jid.to_protocol_address();
1546        let display_sender_key_name =
1547            SenderKeyName::new(group_jid.to_string(), display_protocol_address.to_string());
1548
1549        let device_arc = pm.get_device_arc().await;
1550        {
1551            let mut device_guard = device_arc.write().await;
1552            create_sender_key_distribution_message(
1553                &display_sender_key_name,
1554                &mut *device_guard,
1555                &mut rand::rngs::OsRng.unwrap_err(),
1556            )
1557            .await
1558            .expect("Failed to create SKDM");
1559        }
1560
1561        // Verify it's stored under display JID
1562        let lookup_with_display = {
1563            let mut device_guard = device_arc.write().await;
1564            device_guard.load_sender_key(&display_sender_key_name).await
1565        };
1566        assert!(
1567            lookup_with_display.unwrap().is_some(),
1568            "Sender key should be found with display JID (LID)"
1569        );
1570
1571        // Verify it's NOT accessible via encryption JID (phone number)
1572        let encryption_protocol_address = encryption_jid.to_protocol_address();
1573        let encryption_sender_key_name = SenderKeyName::new(
1574            group_jid.to_string(),
1575            encryption_protocol_address.to_string(),
1576        );
1577
1578        let lookup_with_encryption = {
1579            let mut device_guard = device_arc.write().await;
1580            device_guard
1581                .load_sender_key(&encryption_sender_key_name)
1582                .await
1583        };
1584        assert!(
1585            lookup_with_encryption.unwrap().is_none(),
1586            "Sender key should NOT be found with encryption JID (phone number)"
1587        );
1588
1589        println!("āœ… Sender key operations correctly use display JID, not encryption JID");
1590    }
1591
1592    /// Test edge case: Second message with only skmsg (no pkmsg/msg)
1593    ///
1594    /// After the first message establishes a session and sender key,
1595    /// subsequent messages may contain only skmsg. These should still
1596    /// be decrypted successfully, not skipped.
1597    ///
1598    /// Bug: The code was treating "no session messages" as "session failed",
1599    /// causing it to skip skmsg decryption for all messages after the first.
1600    #[tokio::test]
1601    async fn test_second_message_with_only_skmsg_decrypts() {
1602        use crate::store::sqlite_store::SqliteStore;
1603        use std::sync::Arc;
1604        use wacore::libsignal::protocol::{
1605            create_sender_key_distribution_message, process_sender_key_distribution_message,
1606        };
1607        use wacore::libsignal::store::sender_key_name::SenderKeyName;
1608        use wacore_binary::builder::NodeBuilder;
1609
1610        let backend = Arc::new(
1611            SqliteStore::new("file:memdb_second_msg_test?mode=memory&cache=shared")
1612                .await
1613                .expect("Failed to create test backend"),
1614        );
1615        let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1616        let (client, _sync_rx) = Client::new(pm.clone()).await;
1617
1618        let sender_jid: Jid = "236395184570386.1:75@lid".parse().unwrap();
1619        let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1620
1621        // Step 1: Create and store a sender key (simulating first message processing)
1622        let sender_protocol_address = sender_jid.to_protocol_address();
1623        let sender_key_name =
1624            SenderKeyName::new(group_jid.to_string(), sender_protocol_address.to_string());
1625
1626        let device_arc = pm.get_device_arc().await;
1627        {
1628            let mut device_guard = device_arc.write().await;
1629            let skdm = create_sender_key_distribution_message(
1630                &sender_key_name,
1631                &mut *device_guard,
1632                &mut rand::rngs::OsRng.unwrap_err(),
1633            )
1634            .await
1635            .expect("Failed to create SKDM");
1636
1637            process_sender_key_distribution_message(&sender_key_name, &skdm, &mut *device_guard)
1638                .await
1639                .expect("Failed to process SKDM");
1640        }
1641
1642        println!("āœ… Step 1: Sender key established for {}", sender_jid);
1643
1644        // Step 2: Create a message with ONLY skmsg (no pkmsg/msg)
1645        // This simulates the second message after session is established
1646        let skmsg_ciphertext = {
1647            let mut device_guard = device_arc.write().await;
1648            let sender_key_msg = wacore::libsignal::protocol::group_encrypt(
1649                &mut *device_guard,
1650                &sender_key_name,
1651                b"ping",
1652                &mut rand::rngs::OsRng.unwrap_err(),
1653            )
1654            .await
1655            .expect("Failed to encrypt with sender key");
1656            sender_key_msg.serialized().to_vec()
1657        };
1658
1659        let skmsg_node = NodeBuilder::new("enc")
1660            .attr("type", "skmsg")
1661            .attr("v", "2")
1662            .bytes(skmsg_ciphertext)
1663            .build();
1664
1665        let message_node = Arc::new(
1666            NodeBuilder::new("message")
1667                .attr("from", group_jid.to_string())
1668                .attr("participant", sender_jid.to_string())
1669                .attr("id", "SECOND_MSG_TEST")
1670                .attr("t", "1759306493")
1671                .attr("type", "text")
1672                .attr("addressing_mode", "lid")
1673                .children(vec![skmsg_node])
1674                .build(),
1675        );
1676
1677        // Step 3: Handle the message (should NOT skip skmsg)
1678        // Before the fix, this would log:
1679        // "Skipping skmsg decryption for message SECOND_MSG_TEST from 236395184570386.1:75@lid
1680        //  because the initial session/senderkey message failed to decrypt."
1681        //
1682        // After the fix, it should decrypt successfully.
1683        client.handle_encrypted_message(message_node).await;
1684
1685        println!("āœ… Step 2: Second message with only skmsg processed successfully");
1686
1687        // The test passes if we reach here without errors
1688        // In a real scenario, we'd verify the message was decrypted and the event was dispatched
1689        // For now, we're just ensuring the code path doesn't skip the skmsg incorrectly
1690    }
1691}