libstrophe/
stanza.rs

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/// Proxy to the underlying [sys::xmpp_stanza_t] struct.
15///
16/// Most of the methods in this struct mimic the methods of the underlying library. So please see
17/// libstrophe [docs] and [sources]. Only where it's not the case or there is some additional logic
18/// involved then you can see the method description.
19///
20/// This struct implements:
21///
22///   * `Display` ([xmpp_stanza_to_text])
23///   * `Eq` by comparing internal pointers
24///   * `Hash` by hashing internal pointer
25///   * `Drop` ([xmpp_stanza_release])
26///   * `Clone` ([xmpp_stanza_copy])
27///   * `Send`
28///
29/// [docs]: https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html
30/// [sources]: https://github.com/strophe/libstrophe/blob/0.14.0/src/stanza.c
31/// [xmpp_stanza_to_text]: https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga2918484877ac34d483cc14cf5e957fad
32/// [xmpp_stanza_release]: https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gaa231317e56af0b974d7a28793ebefb83
33/// [xmpp_stanza_copy]: https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga1ab1122fdf477d9d755fc094b4fbf6b6
34#[derive(Debug, Eq)]
35pub struct Stanza {
36	inner: NonNull<sys::xmpp_stanza_t>,
37	owned: bool,
38}
39
40impl Stanza {
41	#[inline]
42	/// [xmpp_stanza_new](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga0422973c3e2f6851c1ff1868e476f118)
43	///
44	/// The newly created stanza is not really useful until you assign an internal type to it. To do
45	/// that you must call [Self::set_text()] to make it `XMPP_STANZA_TEXT` stanza or [Self::set_name()] to make
46	/// it `XMPP_STANZA_TAG` stanza.
47	pub fn new() -> Self {
48		unsafe { Stanza::from_owned(sys::xmpp_stanza_new(ALLOC_CONTEXT.as_ptr())) }
49	}
50
51	#[inline]
52	/// [xmpp_presence_new](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gac47cc19072d39056d24d579b3a96b5db)
53	pub fn new_presence() -> Self {
54		unsafe { Stanza::from_owned(sys::xmpp_presence_new(ALLOC_CONTEXT.as_ptr())) }
55	}
56
57	#[inline]
58	/// [xmpp_iq_new](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gad3fccc89f1367c5014dce97238d75f4d)
59	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	/// [xmpp_message_new](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga850783185475f4324423f2af876774eb)
67	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	/// [xmpp_error_new](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga867086f16735eff5220a116d9d5e353b)
84	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	/// [xmpp_stanza_new_from_string](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga334bbf723f451d1ca7ffb747029c0b4a)
92	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	/// Create an owning stanza from the raw pointer
112	/// # Safety
113	/// inner must be a valid pointer to a previously allocated [sys::xmpp_stanza_t], and you must make sure
114	/// that there are no other usages of that pointer after calling this function.
115	pub unsafe fn from_owned(inner: *mut sys::xmpp_stanza_t) -> Self {
116		unsafe { Stanza::with_inner(inner, true) }
117	}
118
119	#[inline]
120	/// Create a borrowing stanza from the constant raw pointer
121	/// # Safety
122	/// inner must be a valid pointer to a previously allocated [sys::xmpp_stanza_t], and you must make sure
123	/// that Self doesn't outlive the stanza behind that pointer
124	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	/// Create a borrowing stanza from the mutable raw pointer
130	/// # Safety
131	/// inner must be a valid pointer to a previously allocated mutable [sys::xmpp_stanza_t], and you must
132	/// make sure that Self doesn't outlive the stanza behind that pointer
133	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	/// Return internal raw pointer to stanza, for internal use
138	pub(crate) fn as_ptr(&self) -> *mut sys::xmpp_stanza_t {
139		self.inner.as_ptr()
140	}
141
142	/// Reset Stanza context to the 'static global ALLOC_CONTEXT to make it independent of whatever context it was created with
143	///
144	/// Generally libstrophe's [sys::xmpp_stanza_t] needs [sys::xmpp_ctx_t] only for allocation so it's possible to make [Stanza] 'static
145	/// and not dependent on a particular context by using global [ALLOC_CONTEXT]. That's what is done when a new [Stanza] is
146	/// created through methods of this struct, but when [Stanza] is copied (through [Self::clone()] or [Self::reply()]) we don't control
147	/// the initial context, and it is set by `libstrophe` itself (e.g. in callback of `xmpp_handler_add`). In this case it
148	/// receives the context that is tied to the one running the connection, and it is not 'static. This function fixes that
149	/// situation by overwriting the `ctx` reference for current stanza (including one in attributes hash table) and all of
150	/// its children.
151	fn set_alloc_context(&mut self) {
152		// this is dependent on internal representation of _xmpp_stanza_t in common.h for versions 0.9.3 to 0.14.0 (libstrophe-0_14), update if needed
153		#[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	/// [xmpp_stanza_is_text](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga3607a9a49c3614b8599b5ec469a65740)
186	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	/// [xmpp_stanza_is_tag](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gae39816bfdd86e97ca2b03c52813a5a12)
192	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	/// [xmpp_stanza_to_text](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga2918484877ac34d483cc14cf5e957fad)
198	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	/// [xmpp_stanza_set_name](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga8331fbddc0f2fc7286a267ef60c69df2)
204	///
205	/// Be aware that calling this method changes the internal type of stanza to `XMPP_STANZA_TAG`.
206	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	/// [xmpp_stanza_get_name](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gabaffb70aab506a9f2912edb50c43c172)
213	pub fn name(&self) -> Option<&str> {
214		unsafe { FFI(sys::xmpp_stanza_get_name(self.inner.as_ptr())).receive() }
215	}
216
217	#[inline]
218	/// [xmpp_stanza_get_attribute_count](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga015147125e509724524c0aecb536c59c)
219	pub fn attribute_count(&self) -> i32 {
220		unsafe { sys::xmpp_stanza_get_attribute_count(self.inner.as_ptr()) }
221	}
222
223	#[inline]
224	/// [xmpp_stanza_set_attribute](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga06ab477beba98d2f5b66d54e530bfa2d)
225	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	/// [xmpp_stanza_get_attribute](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gad836688d17b7c5af148f32823e72b81b)
233	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	/// [xmpp_stanza_get_attributes](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga2eea8820dcf9b3e2440a06de55a35850)
239	///
240	/// This method returns data as `HashMap` unlike underlying function.
241	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	/// [xmpp_stanza_del_attribute](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gae335c3ea4b5517d2e4cdfdc5cc41e143)
261	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	/// [xmpp_stanza_set_text_with_size](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga779812852611947d0181cd4c58644ef1)
268	///
269	/// Be aware that calling this method changes the internal type of stanza to `XMPP_STANZA_TEXT`.
270	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	/// [xmpp_stanza_get_text](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga95476dfe39bb7acd8e6c534ef3877602)
278	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	/// [xmpp_stanza_set_id](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gaa19a4d40d3383881b3266631dd9f2a0d)
284	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	/// [xmpp_stanza_get_id](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga7aebb4b618deb9fc24b9dae5418a0267)
291	pub fn id(&self) -> Option<&str> {
292		unsafe { FFI(sys::xmpp_stanza_get_id(self.inner.as_ptr())).receive() }
293	}
294
295	#[inline]
296	/// [xmpp_stanza_set_ns](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga2e55fd5671aa9803959ec19518a9adcf)
297	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	/// [xmpp_stanza_get_ns](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gabea315ad8a9a924489701ec5496ce4a7)
304	pub fn ns(&self) -> Option<&str> {
305		unsafe { FFI(sys::xmpp_stanza_get_ns(self.inner.as_ptr())).receive() }
306	}
307
308	#[inline]
309	/// [xmpp_stanza_set_type](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga30d9a7a46ec52c8c8675d31a6af1273b)
310	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	/// [xmpp_stanza_get_type](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga017c704661831141826ed8b4d3daba75)
317	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	/// [xmpp_stanza_set_to](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga095ac729f5b65795cae689f7462a0a89)
323	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	/// [xmpp_stanza_get_to](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gabb04ada2be97b6d9440b28971dc5872d)
330	pub fn to(&self) -> Option<&str> {
331		unsafe { FFI(sys::xmpp_stanza_get_to(self.inner.as_ptr())).receive() }
332	}
333
334	#[inline]
335	/// [xmpp_stanza_set_from](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga368fd01efb2aa6ad0ec2c36233d2c551)
336	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	/// [xmpp_stanza_get_from](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga03f3a2873592536e69c16905edbcd228)
343	pub fn from(&self) -> Option<&str> {
344		unsafe { FFI(sys::xmpp_stanza_get_from(self.inner.as_ptr())).receive() }
345	}
346
347	#[inline]
348	/// [xmpp_stanza_get_children](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga35bbf13870c6551ec16a2d24b4d6b9e4)
349	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	/// [xmpp_stanza_get_children](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga35bbf13870c6551ec16a2d24b4d6b9e4)
355	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	/// [xmpp_stanza_get_child_by_ns](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga09791fe5c7a5b3f4d90a95a46621eb1d)
361	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	/// [xmpp_stanza_get_child_by_ns](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga09791fe5c7a5b3f4d90a95a46621eb1d)
368	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	/// [xmpp_stanza_get_child_by_name](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga19933d39585d91285e02c0c5fff41082)
376	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	/// [xmpp_stanza_get_child_by_name](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga19933d39585d91285e02c0c5fff41082)
384	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	/// [xmpp_stanza_get_child_by_name_and_ns](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gaf00933e114ada170c526f11589d3e072)
393	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	/// [xmpp_stanza_get_child_by_name_and_ns](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gaf00933e114ada170c526f11589d3e072)
403	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	/// [xmpp_stanza_get_child_by_path](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga12567a82abab6a54c396ea56cb895981)
412	///
413	/// Due to internal limitations (vararg call in C) this function supports a maximum of 10 elements
414	/// in the `path` slice.
415	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	/// [xmpp_stanza_get_child_by_path](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga12567a82abab6a54c396ea56cb895981)
423	///
424	/// Due to internal limitations (vararg call in C) this function supports a maximum of 10 elements
425	/// in the `path` slice.
426	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	/// [xmpp_stanza_get_next](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga4eceb55b6a939767d473f7faacfcc6e2)
447	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	/// [xmpp_stanza_get_next](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga4eceb55b6a939767d473f7faacfcc6e2)
453	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	/// [xmpp_stanza_add_child](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga9cfdeabcfc45409d2dfff4b364f84a0c)
459	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	/// [xmpp_stanza_reply](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga32c20758b86bf9c46688e58878a284b5)
465	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	/// [xmpp_stanza_reply_error](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga62a222d584f3890b957ab507070664ff)
472	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	/// [xmpp_message_set_body](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gace4a07d21a6700692d22ea13200d13f5)
488	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	/// [xmpp_message_get_body](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga6659d4692fdddc6758f99c1f867fa3e2)
495	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)]
502/// Helper function for [Stanza::get_child_by_path]
503/// [XMPP_STANZA_NAME_IN_NS](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#a3c83ba7062a2099e61d44c9226bdb286)
504pub 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	/// [xmpp_stanza_to_text](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#ga2918484877ac34d483cc14cf5e957fad)
520	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	/// [xmpp_stanza_release](https://strophe.im/libstrophe/doc/0.13.0/group___stanza.html#gaa231317e56af0b974d7a28793ebefb83)
549	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/// Wrapper for constant reference to [Stanza], implements `Deref` to [Stanza]
582///
583/// You can obtain such objects by calling [Stanza] child search methods.
584#[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/// Wrapper for mutable reference to [Stanza], implements `Deref` and `DerefMut` to [Stanza]
620///
621/// You can obtain such objects by calling [Stanza] child search methods.
622#[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}