1use core::ffi::{CStr, c_char, c_int, c_uint};
2use core::fmt::Display;
3use core::hash::{Hash, Hasher};
4use core::marker::PhantomData;
5use core::ptr::NonNull;
6use core::{fmt, ops, ptr, slice};
7use std::collections::HashMap;
8
9use crate::error::IntoResult;
10use crate::{ALLOC_CONTEXT, Error, ErrorType, FFI, Result, ToTextError};
11
12mod internals;
13
14#[derive(Debug, Eq)]
35pub struct Stanza {
36 inner: NonNull<sys::xmpp_stanza_t>,
37 owned: bool,
38}
39
40impl Stanza {
41 #[inline]
42 pub fn new() -> Self {
48 unsafe { Stanza::from_owned(sys::xmpp_stanza_new(ALLOC_CONTEXT.as_ptr())) }
49 }
50
51 #[inline]
52 pub fn new_presence() -> Self {
54 unsafe { Stanza::from_owned(sys::xmpp_presence_new(ALLOC_CONTEXT.as_ptr())) }
55 }
56
57 #[inline]
58 pub fn new_iq(typ: Option<&str>, id: Option<&str>) -> Self {
60 let typ = FFI(typ).send();
61 let id = FFI(id).send();
62 unsafe { Stanza::from_owned(sys::xmpp_iq_new(ALLOC_CONTEXT.as_ptr(), typ.as_ptr(), id.as_ptr())) }
63 }
64
65 #[inline]
66 pub fn new_message(typ: Option<&str>, id: Option<&str>, to: Option<&str>) -> Self {
68 let typ = FFI(typ).send();
69 let to = FFI(to).send();
70 let id = FFI(id).send();
71 unsafe {
72 Stanza::from_owned(sys::xmpp_message_new(
73 ALLOC_CONTEXT.as_ptr(),
74 typ.as_ptr(),
75 to.as_ptr(),
76 id.as_ptr(),
77 ))
78 }
79 }
80
81 #[inline]
82 #[cfg(feature = "libstrophe-0_9_3")]
83 pub fn new_error(typ: ErrorType, text: Option<&str>) -> Self {
85 let text = FFI(text).send();
86 unsafe { Stanza::from_owned(sys::xmpp_error_new(ALLOC_CONTEXT.as_ptr(), typ, text.as_ptr())) }
87 }
88
89 #[inline]
90 #[cfg(feature = "libstrophe-0_10_0")]
91 pub fn from_str(s: impl AsRef<str>) -> Self {
93 #![allow(clippy::should_implement_trait)]
94 let s = FFI(s.as_ref()).send();
95 unsafe { Stanza::from_owned(sys::xmpp_stanza_new_from_string(ALLOC_CONTEXT.as_ptr(), s.as_ptr())) }
96 }
97
98 #[inline]
99 unsafe fn with_inner(inner: *mut sys::xmpp_stanza_t, owned: bool) -> Self {
100 let mut out = Stanza {
101 inner: NonNull::new(inner).expect("Cannot allocate memory for Stanza"),
102 owned,
103 };
104 if owned {
105 out.set_alloc_context();
106 }
107 out
108 }
109
110 #[inline]
111 pub unsafe fn from_owned(inner: *mut sys::xmpp_stanza_t) -> Self {
116 unsafe { Stanza::with_inner(inner, true) }
117 }
118
119 #[inline]
120 pub unsafe fn from_ref<'st>(inner: *const sys::xmpp_stanza_t) -> StanzaRef<'st> {
125 unsafe { Stanza::with_inner(inner.cast_mut(), false).into() }
126 }
127
128 #[inline]
129 pub unsafe fn from_ref_mut<'st>(inner: *mut sys::xmpp_stanza_t) -> StanzaMutRef<'st> {
134 unsafe { Stanza::with_inner(inner, false).into() }
135 }
136
137 pub(crate) fn as_ptr(&self) -> *mut sys::xmpp_stanza_t {
139 self.inner.as_ptr()
140 }
141
142 fn set_alloc_context(&mut self) {
152 #[repr(C)]
154 struct XmppStanzaRepr {
155 rf: c_int,
156 ctx: *mut sys::xmpp_ctx_t,
157 typ: c_int,
158 prev: *mut sys::xmpp_stanza_t,
159 next: *mut sys::xmpp_stanza_t,
160 children: *mut sys::xmpp_stanza_t,
161 parent: *mut sys::xmpp_stanza_t,
162 data: *mut c_char,
163 attributes: *mut hash_t,
164 }
165
166 #[allow(non_camel_case_types)]
167 #[repr(C)]
168 struct hash_t {
169 rf: c_uint,
170 ctx: *mut sys::xmpp_ctx_t,
171 }
172
173 let inner = unsafe { self.inner.as_ptr().cast::<XmppStanzaRepr>().as_mut() }.expect("Null pointer for Stanza context");
174 let alloc_ctx = ALLOC_CONTEXT.as_ptr();
175 inner.ctx = alloc_ctx;
176 if let Some(attrs) = unsafe { inner.attributes.as_mut() } {
177 attrs.ctx = alloc_ctx;
178 }
179 for mut child in self.children_mut() {
180 child.set_alloc_context();
181 }
182 }
183
184 #[inline]
185 pub fn is_text(&self) -> bool {
187 FFI(unsafe { sys::xmpp_stanza_is_text(self.inner.as_ptr()) }).receive_bool()
188 }
189
190 #[inline]
191 pub fn is_tag(&self) -> bool {
193 FFI(unsafe { sys::xmpp_stanza_is_tag(self.inner.as_ptr()) }).receive_bool()
194 }
195
196 #[inline]
197 pub fn to_text(&self) -> Result<String, ToTextError> {
199 stanza_to_text(self.inner.as_ptr(), |buf| Ok(buf.to_str()?.to_owned()))
200 }
201
202 #[inline]
203 pub fn set_name(&mut self, name: impl AsRef<str>) -> Result<()> {
207 let name = FFI(name.as_ref()).send();
208 unsafe { sys::xmpp_stanza_set_name(self.inner.as_mut(), name.as_ptr()) }.into_result()
209 }
210
211 #[inline]
212 pub fn name(&self) -> Option<&str> {
214 unsafe { FFI(sys::xmpp_stanza_get_name(self.inner.as_ptr())).receive() }
215 }
216
217 #[inline]
218 pub fn attribute_count(&self) -> i32 {
220 unsafe { sys::xmpp_stanza_get_attribute_count(self.inner.as_ptr()) }
221 }
222
223 #[inline]
224 pub fn set_attribute(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> Result<()> {
226 let name = FFI(name.as_ref()).send();
227 let value = FFI(value.as_ref()).send();
228 unsafe { sys::xmpp_stanza_set_attribute(self.inner.as_mut(), name.as_ptr(), value.as_ptr()) }.into_result()
229 }
230
231 #[inline]
232 pub fn get_attribute(&self, name: impl AsRef<str>) -> Option<&str> {
234 let name = FFI(name.as_ref()).send();
235 unsafe { FFI(sys::xmpp_stanza_get_attribute(self.inner.as_ptr(), name.as_ptr())).receive() }
236 }
237
238 pub fn attributes(&self) -> HashMap<&str, &str> {
242 let count = self.attribute_count();
243 let count_usize = usize::try_from(count).unwrap_or(usize::MIN);
244 let mut out = HashMap::with_capacity(count_usize);
245 let mut arr = vec![ptr::null::<c_char>(); count_usize * 2];
246 unsafe {
247 sys::xmpp_stanza_get_attributes(self.inner.as_ptr(), arr.as_mut_ptr(), count * 2);
248 }
249 let mut iter = arr.into_iter();
250 while let (Some(key), Some(val)) = (iter.next(), iter.next()) {
251 out.insert(
252 unsafe { FFI(key).receive() }.expect("Null pointer received for key in attributes() call"),
253 unsafe { FFI(val).receive() }.expect("Null pointer received for value in attributes() call"),
254 );
255 }
256 out
257 }
258
259 #[inline]
260 pub fn del_attribute(&mut self, name: impl AsRef<str>) -> Result<()> {
262 let name = FFI(name.as_ref()).send();
263 unsafe { sys::xmpp_stanza_del_attribute(self.inner.as_mut(), name.as_ptr()) }.into_result()
264 }
265
266 #[inline]
267 pub fn set_text(&mut self, text: impl AsRef<str>) -> Result<()> {
271 let text = text.as_ref();
272 unsafe { sys::xmpp_stanza_set_text_with_size(self.inner.as_mut(), text.as_ptr().cast::<c_char>(), text.len()) }
273 .into_result()
274 }
275
276 #[inline]
277 pub fn text(&self) -> Option<String> {
279 unsafe { FFI(sys::xmpp_stanza_get_text(self.inner.as_ptr())).receive_with_free(|x| ALLOC_CONTEXT.free(x)) }
280 }
281
282 #[inline]
283 pub fn set_id(&mut self, id: impl AsRef<str>) -> Result<()> {
285 let id = FFI(id.as_ref()).send();
286 unsafe { sys::xmpp_stanza_set_id(self.inner.as_mut(), id.as_ptr()) }.into_result()
287 }
288
289 #[inline]
290 pub fn id(&self) -> Option<&str> {
292 unsafe { FFI(sys::xmpp_stanza_get_id(self.inner.as_ptr())).receive() }
293 }
294
295 #[inline]
296 pub fn set_ns(&mut self, ns: impl AsRef<str>) -> Result<()> {
298 let ns = FFI(ns.as_ref()).send();
299 unsafe { sys::xmpp_stanza_set_ns(self.inner.as_mut(), ns.as_ptr()) }.into_result()
300 }
301
302 #[inline]
303 pub fn ns(&self) -> Option<&str> {
305 unsafe { FFI(sys::xmpp_stanza_get_ns(self.inner.as_ptr())).receive() }
306 }
307
308 #[inline]
309 pub fn set_stanza_type(&mut self, typ: impl AsRef<str>) -> Result<()> {
311 let typ = FFI(typ.as_ref()).send();
312 unsafe { sys::xmpp_stanza_set_type(self.inner.as_mut(), typ.as_ptr()) }.into_result()
313 }
314
315 #[inline]
316 pub fn stanza_type(&self) -> Option<&str> {
318 unsafe { FFI(sys::xmpp_stanza_get_type(self.inner.as_ptr())).receive() }
319 }
320
321 #[inline]
322 pub fn set_to(&mut self, to: impl AsRef<str>) -> Result<()> {
324 let to = FFI(to.as_ref()).send();
325 unsafe { sys::xmpp_stanza_set_to(self.inner.as_mut(), to.as_ptr()) }.into_result()
326 }
327
328 #[inline]
329 pub fn to(&self) -> Option<&str> {
331 unsafe { FFI(sys::xmpp_stanza_get_to(self.inner.as_ptr())).receive() }
332 }
333
334 #[inline]
335 pub fn set_from(&mut self, from: impl AsRef<str>) -> Result<()> {
337 let from = FFI(from.as_ref()).send();
338 unsafe { sys::xmpp_stanza_set_from(self.inner.as_mut(), from.as_ptr()) }.into_result()
339 }
340
341 #[inline]
342 pub fn from(&self) -> Option<&str> {
344 unsafe { FFI(sys::xmpp_stanza_get_from(self.inner.as_ptr())).receive() }
345 }
346
347 #[inline]
348 pub fn get_first_child(&self) -> Option<StanzaRef> {
350 unsafe { sys::xmpp_stanza_get_children(self.inner.as_ptr()).as_ref() }.map(|x| unsafe { Self::from_ref(x) })
351 }
352
353 #[inline]
354 pub fn get_first_child_mut(&mut self) -> Option<StanzaMutRef> {
356 unsafe { sys::xmpp_stanza_get_children(self.inner.as_mut()).as_mut() }.map(|x| unsafe { Self::from_ref_mut(x) })
357 }
358
359 #[inline]
360 pub fn get_child_by_ns(&self, ns: impl AsRef<str>) -> Option<StanzaRef> {
362 let ns = FFI(ns.as_ref()).send();
363 unsafe { sys::xmpp_stanza_get_child_by_ns(self.inner.as_ptr(), ns.as_ptr()).as_ref() }.map(|x| unsafe { Self::from_ref(x) })
364 }
365
366 #[inline]
367 pub fn get_child_by_ns_mut(&mut self, ns: impl AsRef<str>) -> Option<StanzaMutRef> {
369 let ns = FFI(ns.as_ref()).send();
370 unsafe { sys::xmpp_stanza_get_child_by_ns(self.inner.as_mut(), ns.as_ptr()).as_mut() }
371 .map(|x| unsafe { Self::from_ref_mut(x) })
372 }
373
374 #[inline]
375 pub fn get_child_by_name(&self, name: impl AsRef<str>) -> Option<StanzaRef> {
377 let name = FFI(name.as_ref()).send();
378 unsafe { sys::xmpp_stanza_get_child_by_name(self.inner.as_ptr(), name.as_ptr()).as_ref() }
379 .map(|x| unsafe { Self::from_ref(x) })
380 }
381
382 #[inline]
383 pub fn get_child_by_name_mut(&mut self, name: impl AsRef<str>) -> Option<StanzaMutRef> {
385 let name = FFI(name.as_ref()).send();
386 unsafe { sys::xmpp_stanza_get_child_by_name(self.inner.as_mut(), name.as_ptr()).as_mut() }
387 .map(|x| unsafe { Self::from_ref_mut(x) })
388 }
389
390 #[inline]
391 #[cfg(feature = "libstrophe-0_10_0")]
392 pub fn get_child_by_name_and_ns(&self, name: impl AsRef<str>, ns: impl AsRef<str>) -> Option<StanzaRef> {
394 let name = FFI(name.as_ref()).send();
395 let ns = FFI(ns.as_ref()).send();
396 unsafe { sys::xmpp_stanza_get_child_by_name_and_ns(self.inner.as_ptr(), name.as_ptr(), ns.as_ptr()).as_ref() }
397 .map(|x| unsafe { Self::from_ref(x) })
398 }
399
400 #[inline]
401 #[cfg(feature = "libstrophe-0_10_0")]
402 pub fn get_child_by_name_and_ns_mut(&mut self, name: impl AsRef<str>, ns: impl AsRef<str>) -> Option<StanzaMutRef> {
404 let name = FFI(name.as_ref()).send();
405 let ns = FFI(ns.as_ref()).send();
406 unsafe { sys::xmpp_stanza_get_child_by_name_and_ns(self.inner.as_mut(), name.as_ptr(), ns.as_ptr()).as_mut() }
407 .map(|x| unsafe { Self::from_ref_mut(x) })
408 }
409
410 #[cfg(feature = "libstrophe-0_12_0")]
411 pub fn get_child_by_path(&self, path: &[&str]) -> Option<StanzaRef> {
416 let res = internals::stanza_get_child_by_path(self.inner.as_ptr(), path);
417 unsafe { res.as_ref() }.map(|x| unsafe { Self::from_ref(x) })
418 }
419
420 #[inline]
421 #[cfg(feature = "libstrophe-0_12_0")]
422 pub fn get_child_by_path_mut(&mut self, path: &[&str]) -> Option<StanzaMutRef> {
427 let res = internals::stanza_get_child_by_path(unsafe { self.inner.as_mut() }, path);
428 unsafe { res.as_mut() }.map(|x| unsafe { Self::from_ref_mut(x) })
429 }
430
431 #[inline]
432 pub fn children(&self) -> impl Iterator<Item = StanzaRef> {
433 ChildIterator {
434 cur: self.get_first_child().map(StanzaChildRef),
435 }
436 }
437
438 #[inline]
439 pub fn children_mut(&mut self) -> impl Iterator<Item = StanzaMutRef> {
440 ChildIteratorMut {
441 cur: self.get_first_child_mut().map(StanzaChildMutRef),
442 }
443 }
444
445 #[inline]
446 pub fn get_next(&self) -> Option<StanzaRef> {
448 unsafe { sys::xmpp_stanza_get_next(self.inner.as_ptr()).as_ref() }.map(|x| unsafe { Self::from_ref(x) })
449 }
450
451 #[inline]
452 pub fn get_next_mut(&mut self) -> Option<StanzaMutRef> {
454 unsafe { sys::xmpp_stanza_get_next(self.inner.as_mut()).as_mut() }.map(|x| unsafe { Self::from_ref_mut(x) })
455 }
456
457 #[inline]
458 pub fn add_child(&mut self, mut child: Stanza) -> Result<()> {
460 unsafe { sys::xmpp_stanza_add_child(self.inner.as_mut(), child.inner.as_mut()) }.into_result()
461 }
462
463 #[inline]
464 pub fn reply(&self) -> Self {
466 unsafe { Self::from_owned(sys::xmpp_stanza_reply(self.inner.as_ptr())) }
467 }
468
469 #[inline]
470 #[cfg(feature = "libstrophe-0_10_0")]
471 pub fn reply_error(&self, error_type: impl AsRef<str>, condition: impl AsRef<str>, text: impl AsRef<str>) -> Self {
473 let error_type = FFI(error_type.as_ref()).send();
474 let condition = FFI(condition.as_ref()).send();
475 let text = FFI(text.as_ref()).send();
476 unsafe {
477 Self::from_owned(sys::xmpp_stanza_reply_error(
478 self.inner.as_ptr(),
479 error_type.as_ptr(),
480 condition.as_ptr(),
481 text.as_ptr(),
482 ))
483 }
484 }
485
486 #[inline]
487 pub fn set_body(&mut self, body: impl AsRef<str>) -> Result<()> {
489 let body = FFI(body.as_ref()).send();
490 unsafe { sys::xmpp_message_set_body(self.inner.as_mut(), body.as_ptr()) }.into_result()
491 }
492
493 #[inline]
494 pub fn body(&self) -> Option<String> {
496 unsafe { FFI(sys::xmpp_message_get_body(self.inner.as_ptr())).receive_with_free(|x| ALLOC_CONTEXT.free(x)) }
497 }
498}
499
500#[inline]
501#[allow(non_snake_case)]
502pub fn XMPP_STANZA_NAME_IN_NS(name: &str, ns: &str) -> String {
505 format!("{name}[@ns='{ns}']")
506}
507
508#[cfg(feature = "libstrophe-0_10_0")]
509impl std::str::FromStr for Stanza {
510 type Err = ();
511
512 #[inline]
513 fn from_str(s: &str) -> Result<Self, Self::Err> {
514 Ok(Self::from_str(s))
515 }
516}
517
518impl Display for Stanza {
519 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
521 stanza_to_text(self.inner.as_ptr(), |buf| f.write_str(buf.to_str().map_err(|_| fmt::Error)?))
522 }
523}
524
525impl Clone for Stanza {
526 #[inline]
527 fn clone(&self) -> Self {
528 unsafe { Stanza::from_owned(sys::xmpp_stanza_copy(self.inner.as_ptr())) }
529 }
530}
531
532impl PartialEq for Stanza {
533 #[inline]
534 fn eq(&self, other: &Stanza) -> bool {
535 self.inner == other.inner
536 }
537}
538
539impl Hash for Stanza {
540 #[inline]
541 fn hash<H: Hasher>(&self, state: &mut H) {
542 self.inner.hash(state);
543 }
544}
545
546impl Drop for Stanza {
547 #[inline]
548 fn drop(&mut self) {
550 if self.owned {
551 unsafe {
552 sys::xmpp_stanza_release(self.inner.as_mut());
553 }
554 }
555 }
556}
557
558impl Default for Stanza {
559 #[inline]
560 fn default() -> Self {
561 Self::new()
562 }
563}
564
565unsafe impl Send for Stanza {}
566
567impl From<Stanza> for StanzaRef<'_> {
568 #[inline]
569 fn from(s: Stanza) -> Self {
570 StanzaRef(s, PhantomData)
571 }
572}
573
574impl From<Stanza> for StanzaMutRef<'_> {
575 #[inline]
576 fn from(s: Stanza) -> Self {
577 StanzaMutRef(s, PhantomData)
578 }
579}
580
581#[derive(Debug, Eq)]
585pub struct StanzaRef<'st>(Stanza, PhantomData<&'st Stanza>);
586
587impl ops::Deref for StanzaRef<'_> {
588 type Target = Stanza;
589
590 #[inline]
591 fn deref(&self) -> &Self::Target {
592 &self.0
593 }
594}
595
596impl PartialEq for StanzaRef<'_> {
597 #[inline]
598 fn eq(&self, other: &StanzaRef) -> bool {
599 self.inner == other.inner
600 }
601}
602
603impl Display for StanzaRef<'_> {
604 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605 self.0.fmt(f)
606 }
607}
608
609#[derive(Debug)]
610struct StanzaChildRef<'parent>(StanzaRef<'parent>);
611
612impl<'parent> StanzaChildRef<'parent> {
613 #[inline]
614 pub fn get_next(&self) -> Option<StanzaChildRef<'parent>> {
615 unsafe { sys::xmpp_stanza_get_next(self.0.inner.as_ptr()).as_ref() }.map(|x| StanzaChildRef(unsafe { Stanza::from_ref(x) }))
616 }
617}
618
619#[derive(Debug)]
623pub struct StanzaMutRef<'st>(Stanza, PhantomData<&'st mut Stanza>);
624
625impl ops::Deref for StanzaMutRef<'_> {
626 type Target = Stanza;
627
628 #[inline]
629 fn deref(&self) -> &Self::Target {
630 &self.0
631 }
632}
633
634impl ops::DerefMut for StanzaMutRef<'_> {
635 #[inline]
636 fn deref_mut(&mut self) -> &mut Self::Target {
637 &mut self.0
638 }
639}
640
641impl Display for StanzaMutRef<'_> {
642 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643 self.0.fmt(f)
644 }
645}
646
647#[derive(Debug)]
648pub struct StanzaChildMutRef<'parent>(StanzaMutRef<'parent>);
649
650impl<'parent> StanzaChildMutRef<'parent> {
651 #[inline]
652 pub fn get_next_mut(&mut self) -> Option<StanzaChildMutRef<'parent>> {
653 unsafe { sys::xmpp_stanza_get_next(self.0.inner.as_ptr()).as_mut() }
654 .map(|x| StanzaChildMutRef(unsafe { Stanza::from_ref_mut(x) }))
655 }
656}
657
658struct ChildIterator<'st> {
659 cur: Option<StanzaChildRef<'st>>,
660}
661
662impl<'st> Iterator for ChildIterator<'st> {
663 type Item = StanzaRef<'st>;
664
665 fn next(&mut self) -> Option<<Self as Iterator>::Item> {
666 self.cur.take().map(|cur| {
667 self.cur = cur.get_next();
668 cur.0
669 })
670 }
671}
672
673struct ChildIteratorMut<'st> {
674 cur: Option<StanzaChildMutRef<'st>>,
675}
676
677impl<'st> Iterator for ChildIteratorMut<'st> {
678 type Item = StanzaMutRef<'st>;
679
680 fn next(&mut self) -> Option<<Self as Iterator>::Item> {
681 self.cur.take().map(|mut cur| {
682 self.cur = cur.get_next_mut();
683 cur.0
684 })
685 }
686}
687
688fn stanza_to_text<T, E>(stanza: *mut sys::xmpp_stanza_t, cb: impl FnOnce(&CStr) -> Result<T, E>) -> Result<T, E>
689where
690 E: From<Error>,
691{
692 let mut buf = ptr::null_mut::<c_char>();
693 let mut buflen = 0;
694 let res = unsafe { sys::xmpp_stanza_to_text(stanza, &mut buf, &mut buflen) };
695 let _free_buf = scopeguard::guard(buf, |buf| {
696 if !buf.is_null() {
697 unsafe {
698 ALLOC_CONTEXT.free(buf);
699 }
700 }
701 });
702 res.into_result().map_err(E::from).and_then(|_| {
703 let text = unsafe { CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(buf.cast::<u8>(), buflen + 1)) };
704 cb(text)
705 })
706}