1use crate::client::Client;
2use crate::types::events::{Event, PairError, PairSuccess};
3use log::{error, info, warn};
4use prost::Message;
5use rand::TryRngCore;
6use rand_core::OsRng;
7use std::sync::Arc;
8use std::sync::atomic::Ordering;
9use wacore::libsignal::protocol::KeyPair;
10use wacore_binary::jid::Jid;
11use wacore_binary::node::{Node, NodeContent};
12use waproto::whatsapp as wa;
13
14pub use wacore::pair::{DeviceState, PairCryptoError, PairUtils};
15
16pub fn make_qr_data(store: &crate::store::Device, ref_str: String) -> String {
17 let device_state = DeviceState {
18 identity_key: store.identity_key,
19 noise_key: store.noise_key,
20 adv_secret_key: store.adv_secret_key,
21 };
22 PairUtils::make_qr_data(&device_state, ref_str)
23}
24
25pub async fn handle_iq(client: &Arc<Client>, node: &Node) -> bool {
26 if node.attrs.get("from").cloned().unwrap_or_default() != "s.whatsapp.net" {
27 return false;
28 }
29
30 if let Some(children) = node.children() {
31 for child in children {
32 let handled = match child.tag.as_str() {
33 "pair-device" => {
34 if let Some(ack_node) = PairUtils::build_ack_node(node)
35 && let Err(e) = client.send_node(ack_node).await
36 {
37 warn!("Failed to send acknowledgement: {e:?}");
38 }
39
40 let mut codes = Vec::new();
41
42 let device_snapshot = client.persistence_manager.get_device_snapshot().await;
43 let device_state = DeviceState {
44 identity_key: device_snapshot.identity_key,
45 noise_key: device_snapshot.noise_key,
46 adv_secret_key: device_snapshot.adv_secret_key,
47 };
48
49 for grandchild in child.get_children_by_tag("ref") {
50 if let Some(NodeContent::Bytes(bytes)) = &grandchild.content
51 && let Ok(r) = String::from_utf8(bytes.clone())
52 {
53 codes.push(PairUtils::make_qr_data(&device_state, r));
54 }
55 }
56
57 let (stop_tx, stop_rx) = tokio::sync::watch::channel(());
58 let codes_clone = codes.clone();
59 let client_clone = client.clone();
60
61 tokio::spawn(async move {
62 let mut is_first = true;
64 let mut stop_rx_clone = stop_rx.clone();
65
66 for code in codes_clone {
67 let timeout = if is_first {
68 is_first = false;
69 std::time::Duration::from_secs(60)
70 } else {
71 std::time::Duration::from_secs(20)
72 };
73
74 client_clone
76 .core
77 .event_bus
78 .dispatch(&Event::PairingQrCode { code, timeout });
79
80 tokio::select! {
82 _ = tokio::time::sleep(timeout) => {}
83 _ = stop_rx_clone.changed() => {
84 info!("Pairing complete. Stopping QR code rotation.");
85 return;
86 }
87 }
88 }
89 info!("All QR codes for this session have expired.");
90 client_clone.disconnect().await;
91 });
92
93 *client.pairing_cancellation_tx.lock().await = Some(stop_tx);
94
95 true
98 }
99 "pair-success" => {
100 handle_pair_success(client, node, child).await;
101 true
102 }
103 _ => false,
104 };
105 if handled {
106 return true;
107 }
108 }
109 }
110
111 false
112}
113
114async fn handle_pair_success(client: &Arc<Client>, request_node: &Node, success_node: &Node) {
115 if let Some(tx) = client.pairing_cancellation_tx.lock().await.take() {
116 let _ = tx.send(());
117 }
118
119 let req_id = match request_node.attrs.get("id") {
120 Some(id) => id.to_string(),
121 None => {
122 error!("Received pair-success without request ID");
123 return;
124 }
125 };
126
127 let device_identity_bytes = match success_node
128 .get_optional_child_by_tag(&["device-identity"])
129 .and_then(|n| n.content.as_ref())
130 {
131 Some(NodeContent::Bytes(b)) => b.clone(),
132 _ => {
133 let error_node = PairUtils::build_pair_error_node(&req_id, 500, "internal-error");
134 if let Err(e) = client.send_node(error_node).await {
135 error!("Failed to send pair error node: {e}");
136 }
137 error!("pair-success is missing device-identity");
138 return;
139 }
140 };
141
142 let business_name = success_node
143 .get_optional_child_by_tag(&["biz"])
144 .map(|n| n.attrs().optional_string("name").unwrap_or("").to_string())
145 .unwrap_or_default();
146
147 let platform = success_node
148 .get_optional_child_by_tag(&["platform"])
149 .map(|n| n.attrs().optional_string("name").unwrap_or("").to_string())
150 .unwrap_or_default();
151
152 let (jid, lid) = if let Some(device_node) = success_node.get_optional_child_by_tag(&["device"])
154 {
155 let mut parser = device_node.attrs();
156 let parsed_jid = parser.optional_jid("jid").unwrap_or_default();
157 let parsed_lid = parser.optional_jid("lid").unwrap_or_default();
158
159 if let Err(e) = parser.finish() {
160 warn!(target: "Client/Pair", "Error parsing device node attributes: {e:?}");
161 (Jid::default(), Jid::default())
162 } else {
163 (parsed_jid, parsed_lid)
164 }
165 } else {
166 (Jid::default(), Jid::default())
167 };
168
169 let device_snapshot = client.persistence_manager.get_device_snapshot().await;
170 let device_state = DeviceState {
171 identity_key: device_snapshot.identity_key,
172 noise_key: device_snapshot.noise_key,
173 adv_secret_key: device_snapshot.adv_secret_key,
174 };
175
176 let result = PairUtils::do_pair_crypto(&device_state, &device_identity_bytes);
177
178 match result {
179 Ok((self_signed_identity_bytes, key_index)) => {
180 let signed_identity_for_event = match wa::AdvSignedDeviceIdentity::decode(
181 self_signed_identity_bytes.as_slice(),
182 ) {
183 Ok(identity) => identity,
184 Err(e) => {
185 error!(
186 "FATAL: Failed to re-decode self-signed identity for event, pairing cannot complete: {e}"
187 );
188 client.core.event_bus.dispatch(&Event::PairError(PairError {
189 id: jid.clone(),
190 lid: lid.clone(),
191 business_name: business_name.clone(),
192 platform: platform.clone(),
193 error: format!("internal error: failed to decode identity for event: {e}"),
194 }));
195 return;
196 }
197 };
198
199 client
200 .persistence_manager
201 .process_command(crate::store::commands::DeviceCommand::SetId(Some(
202 jid.clone(),
203 )))
204 .await;
205 client
206 .persistence_manager
207 .process_command(crate::store::commands::DeviceCommand::SetAccount(Some(
208 signed_identity_for_event.clone(),
209 )))
210 .await;
211 client
212 .persistence_manager
213 .process_command(crate::store::commands::DeviceCommand::SetLid(Some(
214 lid.clone(),
215 )))
216 .await;
217
218 if !business_name.is_empty() {
219 info!("✅ Setting push_name during pairing: '{}'", &business_name);
220 client
221 .persistence_manager
222 .process_command(crate::store::commands::DeviceCommand::SetPushName(
223 business_name.clone(),
224 ))
225 .await;
226 } else {
227 info!(
228 "⚠️ business_name not found in pair-success, push_name remains unset for now."
229 );
230 }
231
232 let response_node = PairUtils::build_pair_success_response(
233 &req_id,
234 self_signed_identity_bytes,
235 key_index,
236 );
237
238 if let Err(e) = client.send_node(response_node).await {
239 error!("Failed to send pair-device-sign: {e}");
240 return;
241 }
242
243 client
246 .needs_initial_full_sync
247 .store(true, Ordering::Relaxed);
248 client.expected_disconnect.store(true, Ordering::Relaxed);
251
252 info!("Successfully paired {jid}");
253
254 let success_event = PairSuccess {
255 id: jid,
256 lid,
257 business_name,
258 platform,
259 };
260 client
261 .core
262 .event_bus
263 .dispatch(&Event::PairSuccess(success_event));
264 }
265 Err(e) => {
266 error!("Pairing crypto failed: {e}");
267 let error_node = PairUtils::build_pair_error_node(&req_id, e.code, e.text);
268 if let Err(send_err) = client.send_node(error_node).await {
269 error!("Failed to send pair error node: {send_err}");
270 }
271
272 let pair_error_event = crate::types::events::PairError {
273 id: jid,
274 lid,
275 business_name,
276 platform,
277 error: e.to_string(),
278 };
279 client
280 .core
281 .event_bus
282 .dispatch(&Event::PairError(pair_error_event));
283 }
284 }
285}
286
287pub async fn pair_with_qr_code(client: &Arc<Client>, qr_code: &str) -> Result<(), anyhow::Error> {
288 info!(target: "Client/PairTest", "Master client attempting to pair with QR code.");
289
290 let (pairing_ref, dut_noise_pub, dut_identity_pub) = PairUtils::parse_qr_code(qr_code)?;
291
292 let master_ephemeral = KeyPair::generate(&mut OsRng::unwrap_err(OsRng));
293
294 let device_snapshot = client.persistence_manager.get_device_snapshot().await;
295 let device_state = DeviceState {
296 identity_key: device_snapshot.identity_key,
297 noise_key: device_snapshot.noise_key,
298 adv_secret_key: device_snapshot.adv_secret_key,
299 };
300
301 let encrypted = PairUtils::prepare_master_pairing_message(
302 &device_state,
303 &pairing_ref,
304 &dut_noise_pub,
305 &dut_identity_pub,
306 master_ephemeral,
307 )?;
308
309 let master_jid = device_snapshot.pn.clone().unwrap();
310 let req_id = client.generate_request_id();
311
312 let iq = PairUtils::build_master_pair_iq(&master_jid, encrypted, req_id);
313
314 client.send_node(iq).await?;
315
316 info!(target: "Client/PairTest", "Master client sent pairing confirmation.");
317 Ok(())
318}