1use super::*;
4
5#[cfg(feature = "tracing")]
8#[derive(Debug, Clone, Default)]
9pub struct IdentityTags {
10 pub lid: Option<String>,
11 pub pn: Option<String>,
12}
13
14impl Client {
15 pub(crate) async fn get_group_cache(&self) -> Arc<GroupCache> {
16 let mut guard = self.group_cache.lock().await;
17 if let Some(cache) = guard.as_ref() {
18 return cache.clone();
19 }
20 debug!("Initializing Group Cache for the first time.");
21 let cache = Arc::new(
22 self.cache_config
23 .group_cache
24 .build_typed_ttl(self.cache_config.cache_stores.group_cache.clone(), "group"),
25 );
26 *guard = Some(cache.clone());
27 cache
28 }
29
30 pub fn subscribe(
32 &self,
33 interest: wacore::types::events::EventInterest,
34 handler: Arc<dyn wacore::types::events::EventHandler>,
35 ) -> wacore::types::events::Subscription {
36 self.core.event_bus.subscribe(interest, handler)
37 }
38
39 pub fn subscribe_handler(
41 &self,
42 handler: Arc<dyn wacore::types::events::EventHandler>,
43 ) -> wacore::types::events::Subscription {
44 self.core.event_bus.subscribe_handler(handler)
45 }
46
47 pub fn acquire_raw_node_forwarding(self: &Arc<Self>) -> RawNodeLease {
51 let incremented = self
52 .raw_node_forwarding
53 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |count| {
54 count.checked_add(1)
55 })
56 .is_ok();
57 assert!(incremented, "raw-node forwarding lease counter overflow");
58 RawNodeLease {
59 client: Arc::downgrade(self),
60 }
61 }
62
63 pub(crate) fn raw_node_forwarding_enabled(&self) -> bool {
64 self.raw_node_forwarding.load(Ordering::Relaxed) != 0
65 }
66
67 pub fn set_skip_history_sync(&self, enabled: bool) {
72 self.skip_history_sync.store(enabled, Ordering::Relaxed);
73 }
74
75 pub fn skip_history_sync_enabled(&self) -> bool {
77 self.skip_history_sync.load(Ordering::Relaxed)
78 }
79
80 pub fn set_wanted_pre_key_count(&self, count: usize) {
87 self.wanted_pre_key_count.store(count, Ordering::Relaxed);
88 }
89
90 pub fn wanted_pre_key_count(&self) -> usize {
93 self.wanted_pre_key_count.load(Ordering::Relaxed)
94 }
95
96 pub fn set_resend_rate_limit(&self, burst: u32, refill_per_min: u32) {
107 self.resend_rate_limiter.set_rate(burst, refill_per_min);
108 }
109
110 pub fn set_retry_admission(
122 &self,
123 policy: Arc<dyn crate::types::retry_admission::RetryAdmission>,
124 ) -> bool {
125 self.retry_admission.set(policy).is_ok()
126 }
127
128 pub fn stats(&self) -> StatsSnapshot {
135 let mut snapshot = self.stats.snapshot();
136 snapshot.reconnect_errors = self.auto_reconnect_errors.load(Ordering::Relaxed);
137 snapshot.resends_throttled = self.resend_rate_limiter.throttled_total();
138 snapshot
139 }
140
141 pub async fn memory_report(&self) -> MemoryReport {
150 use wacore::stats::{CollectionStats, HeapSize};
151
152 let (signal_sessions, signal_identities, signal_sender_keys) =
153 self.signal_cache.memory_stats().await;
154 let (lid_pn_lid_entries, lid_pn_pn_entries) = self.lid_pn_cache.memory_stats().await;
155 let pending_retries_count = self
156 .pending_retries
157 .lock()
158 .unwrap_or_else(|p| p.into_inner())
159 .len();
160
161 let group_cache_arc = self.group_cache.lock().await.clone();
164 let group_cache = match group_cache_arc {
165 Some(cache) => {
167 cache
168 .memory_stats(|k, v| k.heap_bytes() + v.heap_bytes())
169 .await
170 }
171 None => CollectionStats::default(),
172 };
173
174 let recent_messages = self
175 .recent_messages
176 .memory_stats(|k, v| k.chat.heap_bytes() + k.id.heap_bytes() + v.heap_bytes())
177 .await;
178
179 let group_devices_memo = self
180 .group_devices_memo
181 .memory_stats(|k, v| k.heap_bytes() + v.heap_bytes())
182 .await;
183 let dm_devices_memo = self
184 .dm_devices_memo
185 .memory_stats(|k, v| k.heap_bytes() + v.heap_bytes())
186 .await;
187 let group_distribution_locks = self.group_distribution_locks.capacity_stats().await;
188
189 let response_waiters = self.response_waiters_guard().len();
191 let presence_subscriptions = self.presence_subscriptions.lock().await.len();
192 let app_state_key_requests = self.app_state_key_requests.lock().await.len();
193 let app_state_syncing = self.app_state_syncing.len();
194 let chatstate_handlers = self.chatstate_handlers.read().await.len();
195 let history_sync_activity = self.history_sync_activity.snapshot();
196 let history_sync_tasks = CollectionStats::new(
197 history_sync_activity.tasks as u64,
198 history_sync_activity.payload_bytes as u64,
199 );
200 #[cfg(feature = "voip-runtime")]
201 let pending_call_link_updates = self
202 .pending_call_link_joins
203 .lock()
204 .unwrap_or_else(|poisoned| poisoned.into_inner())
205 .memory_stats();
206 #[cfg(feature = "voip-runtime")]
207 let active_calls = self.call_registry.memory_stats();
208 #[cfg(feature = "plugins")]
209 let plugin_stats = self.plugin_stats();
210 #[cfg(feature = "plugins")]
211 let (
212 plugins,
213 plugin_install_tasks,
214 plugin_connection_tasks,
215 plugin_connection_generations,
216 plugin_core_event_subscriptions,
217 ) = plugin_stats
218 .as_ref()
219 .map(|host| {
220 host.plugins.iter().fold(
221 (
222 u64::try_from(host.plugins.len()).unwrap_or(u64::MAX),
223 0u64,
224 0u64,
225 0u64,
226 0u64,
227 ),
228 |(plugins, install, connection, generations, subscriptions), plugin| {
229 (
230 plugins,
231 install.saturating_add(plugin.install_tasks),
232 connection.saturating_add(plugin.connection_tasks),
233 generations.saturating_add(plugin.connection_generations),
234 subscriptions.saturating_add(plugin.core_event_subscriptions),
235 )
236 },
237 )
238 })
239 .unwrap_or_default();
240 #[cfg(feature = "plugins")]
241 let plugin_event_router = plugin_stats
242 .as_ref()
243 .and_then(|host| host.event_router)
244 .unwrap_or_default();
245
246 MemoryReport {
247 group_cache,
248 device_registry_cache: self.device_registry_cache.memory_stats().await,
249 lid_pn_lid_entries,
250 lid_pn_pn_entries,
251 recent_messages,
252 sender_key_device_cache: self.sender_key_device_cache.memory_stats().await,
253 group_devices_memo,
254 dm_devices_memo,
255 message_retry_counts: self.message_retry_counts.entry_count(),
256 undecryptable_dispatched: self.undecryptable_dispatched.entry_count(),
257 pdo_pending_requests: self.pdo_pending_requests.entry_count(),
258 pdo_requested: self.pdo_requested.entry_count(),
259 history_sync_tasks,
260 history_sync_tasks_peak: history_sync_activity.tasks_peak as u64,
261 history_sync_payload_bytes_peak: history_sync_activity.payload_bytes_peak as u64,
262 session_locks: self.session_locks.entry_count(),
263 chat_lanes: self.chat_lanes.entry_count(),
264 group_distribution_locks: group_distribution_locks.entries,
265 group_distribution_lock_evictions: group_distribution_locks.evictions,
266 group_distribution_lock_eviction_blocks: group_distribution_locks.eviction_blocks,
267 resend_rate_limiter_chats: self.resend_rate_limiter.entry_count(),
268 transport_ack_queue: self.transport_ack_queue.get().map_or(0, |tx| tx.len()),
269 delivery_receipt_queue: self.delivery_receipt_queue.get().map_or(0, |tx| tx.len()),
270 response_waiters,
271 node_waiters: self.node_waiter_count.load(Ordering::Relaxed),
272 pending_retries: pending_retries_count,
273 presence_subscriptions,
274 app_state_key_requests,
275 app_state_syncing,
276 signal_sessions,
277 signal_identities,
278 signal_sender_keys,
279 #[cfg(feature = "voip-runtime")]
280 pending_call_link_updates,
281 #[cfg(feature = "voip-runtime")]
282 active_calls,
283 #[cfg(feature = "plugins")]
284 plugins,
285 #[cfg(feature = "plugins")]
286 plugin_install_tasks,
287 #[cfg(feature = "plugins")]
288 plugin_connection_tasks,
289 #[cfg(feature = "plugins")]
290 plugin_connection_generations,
291 #[cfg(feature = "plugins")]
292 plugin_core_event_subscriptions,
293 #[cfg(feature = "plugins")]
294 plugin_event_endpoints: plugin_event_router.active_endpoints,
295 #[cfg(feature = "plugins")]
296 plugin_event_endpoint_capacity: plugin_event_router.endpoint_capacity,
297 #[cfg(feature = "plugins")]
298 plugin_event_queue: CollectionStats::new(
299 plugin_event_router.queued_events,
300 plugin_event_router.queued_payload_bytes,
301 ),
302 chatstate_handlers,
303 custom_enc_handlers: self.custom_enc_handlers.get().map_or(0, |m| m.len()),
304 }
305 }
306
307 pub async fn resource_report(&self) -> ResourceReport {
320 let client = self.memory_report().await;
321 let storage = self.persistence_manager.backend().resource_report().await;
322 let transport = {
323 let guard = self.transport.lock().await;
324 guard.as_ref().and_then(|t| t.resource_report())
325 };
326 let http = self.http_client.resource_report();
327 let alloc = self.alloc_meter.get().map(|m| m.snapshot());
328 ResourceReport {
329 client,
330 storage,
331 transport,
332 http,
333 alloc,
334 }
335 }
336
337 pub fn persistence_manager(&self) -> Arc<PersistenceManager> {
340 self.persistence_manager.clone()
341 }
342
343 pub fn push_name(&self) -> String {
349 self.persistence_manager
350 .get_device_snapshot()
351 .push_name
352 .clone()
353 }
354
355 pub fn pn(&self) -> Option<Jid> {
357 self.persistence_manager.get_device_snapshot().pn.clone()
358 }
359
360 pub fn lid(&self) -> Option<Jid> {
362 self.persistence_manager.get_device_snapshot().lid.clone()
363 }
364
365 #[cfg(feature = "tracing")]
368 pub fn identity_tags(&self) -> IdentityTags {
369 let snapshot = self.persistence_manager.get_device_snapshot();
370 IdentityTags {
371 lid: snapshot.lid.as_ref().map(|j| j.to_string()),
372 pn: snapshot.pn.as_ref().map(|j| j.observe().to_string()),
373 }
374 }
375
376 #[cfg(feature = "tracing")]
380 pub(crate) fn record_identity_on_span(&self, span: &tracing::Span) {
381 if span.is_disabled() {
382 return;
383 }
384 let tags = self.identity_tags();
385 if let Some(lid) = tags.lid {
386 span.record("lid", tracing::field::display(lid));
387 }
388 if let Some(pn) = tags.pn {
389 span.record("pn", tracing::field::display(pn));
390 }
391 }
392
393 pub(crate) fn require_pn(&self) -> Result<Jid> {
394 self.pn().ok_or(ClientError::NotLoggedIn.into())
395 }
396
397 pub(crate) async fn get_own_jid_for_group(
402 &self,
403 group_jid: &Jid,
404 ) -> Result<Jid, anyhow::Error> {
405 let device_snapshot = self.persistence_manager.get_device_snapshot();
406 let own_pn = device_snapshot
407 .pn
408 .clone()
409 .ok_or_else(|| anyhow::Error::from(ClientError::NotLoggedIn))?;
410
411 let addressing_mode = self
412 .groups()
413 .query_info(group_jid)
414 .await
415 .map(|info| info.addressing_mode)
416 .unwrap_or(crate::types::message::AddressingMode::Pn);
417
418 Ok(match addressing_mode {
419 crate::types::message::AddressingMode::Lid => {
420 device_snapshot.lid.clone().unwrap_or(own_pn)
421 }
422 crate::types::message::AddressingMode::Pn => own_pn,
423 })
424 }
425
426 pub(crate) async fn update_push_name_and_notify(self: &Arc<Self>, new_name: String) {
427 let device_snapshot = self.persistence_manager.get_device_snapshot();
428 let old_name = device_snapshot.push_name.clone();
429
430 if old_name == new_name {
431 return;
432 }
433
434 log::debug!("Updating push name from '{}' -> '{}'", old_name, new_name);
435 self.persistence_manager
436 .process_command(DeviceCommand::SetPushName(new_name.clone()))
437 .await;
438
439 self.core.event_bus.dispatch(Event::SelfPushNameUpdated(
440 crate::types::events::SelfPushNameUpdated::builder()
441 .from_server(true)
442 .old_name(old_name)
443 .new_name(new_name.clone())
444 .build(),
445 ));
446
447 let client_clone = self.clone();
448 self.runtime
449 .spawn(Box::pin(async move {
450 if let Err(e) = client_clone.presence().set_available().await {
451 log::warn!("Failed to send presence after push name update: {:?}", e);
452 } else {
453 log::debug!("Sent presence after push name update.");
454 }
455 }))
456 .detach();
457 }
458
459 pub fn wait_for_node(
477 &self,
478 filter: NodeFilter,
479 ) -> futures::channel::oneshot::Receiver<Arc<wacore_binary::OwnedNodeRef>> {
480 let (tx, rx) = futures::channel::oneshot::channel();
481 self.node_waiter_count.fetch_add(1, Ordering::Release);
482 let mut waiters = self
483 .node_waiters
484 .lock()
485 .unwrap_or_else(|poisoned| poisoned.into_inner());
486 waiters.push(NodeWaiter { filter, tx });
487 rx
488 }
489
490 pub fn wait_for_sent_node(
496 &self,
497 filter: NodeFilter,
498 ) -> futures::channel::oneshot::Receiver<Arc<Node>> {
499 let (tx, rx) = futures::channel::oneshot::channel();
500 self.sent_node_waiter_count.fetch_add(1, Ordering::Release);
501 let mut waiters = self
502 .sent_node_waiters
503 .lock()
504 .unwrap_or_else(|poisoned| poisoned.into_inner());
505 waiters.push(SentNodeWaiter { filter, tx });
506 rx
507 }
508
509 #[inline]
514 pub(crate) fn response_waiters_guard(&self) -> std::sync::MutexGuard<'_, ResponseWaiterMap> {
515 self.response_waiters
516 .lock()
517 .unwrap_or_else(|p| p.into_inner())
518 }
519
520 pub(crate) fn resolve_node_waiters(&self, node: &Arc<wacore_binary::OwnedNodeRef>) {
523 resolve_waiters(&self.node_waiters, &self.node_waiter_count, node);
524 }
525
526 pub(crate) fn resolve_sent_node_waiters(&self, node: &Arc<Node>) {
527 let nr = node.as_node_ref();
528 let mut waiters = self
529 .sent_node_waiters
530 .lock()
531 .unwrap_or_else(|poisoned| poisoned.into_inner());
532 let mut i = 0;
533 while i < waiters.len() {
534 if waiters[i].tx.is_canceled() {
535 waiters.swap_remove(i);
536 self.sent_node_waiter_count.fetch_sub(1, Ordering::Release);
537 } else if waiters[i].filter.matches(&nr) {
538 let w = waiters.swap_remove(i);
539 self.sent_node_waiter_count.fetch_sub(1, Ordering::Release);
540 let _ = w.tx.send(Arc::clone(node));
541 } else {
542 i += 1;
543 }
544 }
545 }
546
547 pub(crate) fn clear_sent_node_waiters(&self) {
548 let mut waiters = self
549 .sent_node_waiters
550 .lock()
551 .unwrap_or_else(|poisoned| poisoned.into_inner());
552 let count = waiters.len();
553 if count > 0 {
554 waiters.clear();
555 self.sent_node_waiter_count
556 .fetch_sub(count, Ordering::Release);
557 }
558 }
559
560 fn should_downgrade_sync_error(&self, err: &anyhow::Error) -> bool {
561 if self.is_shutting_down() {
562 return true;
563 }
564
565 matches!(
566 err.downcast_ref::<crate::request::IqError>(),
567 Some(
568 crate::request::IqError::NotConnected
569 | crate::request::IqError::InternalChannelClosed
570 )
571 )
572 }
573
574 pub(crate) fn log_sync_error(&self, context: &str, err: &anyhow::Error) {
576 if self.should_downgrade_sync_error(err) {
577 debug!("Skipping {context} during shutdown: {err}");
578 } else {
579 warn!("Failed {context}: {err}");
580 }
581 }
582
583 pub(crate) fn create_stanza_router() -> crate::handlers::router::StanzaRouter {
585 use crate::handlers::{
586 basic::{AckHandler, FailureHandler, StreamErrorHandler, SuccessHandler},
587 chatstate::ChatstateHandler,
588 ib::IbHandler,
589 iq::IqHandler,
590 message::MessageHandler,
591 notification::NotificationHandler,
592 receipt::ReceiptHandler,
593 router::StanzaRouter,
594 };
595
596 let mut router = StanzaRouter::new();
597
598 router.register(Arc::new(MessageHandler));
600 router.register(Arc::new(ReceiptHandler));
601 router.register(Arc::new(IqHandler));
602 router.register(Arc::new(SuccessHandler));
603 router.register(Arc::new(FailureHandler));
604 router.register(Arc::new(StreamErrorHandler));
605 router.register(Arc::new(IbHandler));
606 router.register(Arc::new(NotificationHandler));
607 router.register(Arc::new(AckHandler));
608 router.register(Arc::new(ChatstateHandler));
609
610 router.register(Arc::new(crate::handlers::call::CallHandler));
611
612 router.register(Arc::new(crate::handlers::presence::PresenceHandler));
614
615 router
616 }
617}
618
619#[cfg(test)]
620mod raw_node_tests {
621 #[tokio::test]
622 async fn raw_node_forwarding_stays_enabled_until_the_last_lease_drops() {
623 let client = crate::test_utils::create_test_client().await;
624 assert!(!client.raw_node_forwarding_enabled());
625
626 let first = client.acquire_raw_node_forwarding();
627 let second = client.acquire_raw_node_forwarding();
628 assert!(client.raw_node_forwarding_enabled());
629
630 drop(first);
631 assert!(client.raw_node_forwarding_enabled());
632 drop(second);
633 assert!(!client.raw_node_forwarding_enabled());
634 }
635}
636
637#[cfg(test)]
638mod send_checks {
639 fn assert_send<T: Send>(_: &T) {}
640
641 #[allow(dead_code)]
645 fn memory_report_future_is_send(c: &super::Client) {
646 assert_send(&c.memory_report());
647 }
648
649 #[allow(dead_code)]
653 fn resource_report_future_is_send(c: &super::Client) {
654 assert_send(&c.resource_report());
655 }
656}