libstrophe/
connection.rs

1use core::ffi::{c_char, c_int, c_ulong, c_void};
2use core::ptr::NonNull;
3use core::time::Duration;
4use core::{fmt, mem, ptr, str};
5use std::cell::RefCell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9#[cfg(feature = "libstrophe-0_11_0")]
10mod libstrophe_0_11 {
11	pub use std::any::TypeId;
12
13	pub use super::internals::CERT_FAIL_HANDLERS;
14	pub use crate::TlsCert;
15}
16#[cfg(feature = "libstrophe-0_11_0")]
17pub use internals::CertFailResult;
18#[cfg(feature = "libstrophe-0_11_0")]
19use libstrophe_0_11::*;
20
21#[cfg(feature = "libstrophe-0_12_0")]
22mod libstrophe_0_12 {
23	pub use super::internals::{PasswordCallback, SOCKOPT_HANDLERS};
24	pub use crate::{QueueElement, SMState};
25}
26#[cfg(feature = "libstrophe-0_12_0")]
27pub use internals::SockoptResult;
28#[cfg(feature = "libstrophe-0_12_0")]
29use libstrophe_0_12::*;
30
31#[cfg(feature = "libstrophe-0_14")]
32mod libstrophe_0_14 {
33	pub use super::internals::SmStateCallback;
34	pub use crate::sm_state::{SerializedSmState, SerializedSmStateRef};
35}
36pub use internals::HandlerResult;
37use internals::{
38	BoxedHandler, BoxedHandlers, CbAddr, ConnectionCallback, ConnectionFatHandler, ConnectionHandlers, StanzaCallback,
39	StanzaFatHandler, TimedCallback, TimedFatHandler,
40};
41#[cfg(feature = "libstrophe-0_14")]
42use libstrophe_0_14::*;
43
44use crate::connection::internals::MaybeBoxedHandler;
45use crate::error::IntoResult;
46use crate::ffi_types::Nullable;
47use crate::{ConnectClientError, ConnectionError, ConnectionFlags, Context, Error, FFI, Result, Stanza, StreamError};
48
49#[macro_use]
50mod internals;
51
52/// Proxy to the underlying [sys::xmpp_conn_t] struct.
53///
54/// Most of the methods in this struct mimic the methods of the underlying library. So please see
55/// libstrophe docs for [connection] and [handlers] plus [conn.c] and [handler.c] sources.
56/// Only where it's not the case or there is some additional logic involved then you can see the
57/// method description.
58///
59/// This struct implements:
60///
61///   * `Drop` ([xmpp_conn_release]).
62///   * `Eq` by comparing internal pointers
63///   * `Send`
64///
65/// [connection]: https://strophe.im/libstrophe/doc/0.13.0/group___connections.html
66/// [handlers]: https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html
67/// [conn.c]: https://github.com/strophe/libstrophe/blob/0.14.0/src/conn.c
68/// [handler.c]: https://github.com/strophe/libstrophe/blob/0.14.0/src/handler.c
69/// [xmpp_conn_release]: https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga87b076b11589bc23123096dc83cde6a8
70#[derive(Debug)]
71pub struct Connection<'cb, 'cx> {
72	inner: NonNull<sys::xmpp_conn_t>,
73	ctx: Option<Context<'cx, 'cb>>,
74	owned: bool,
75	handlers: Rc<RefCell<ConnectionHandlers<'cb, 'cx>>>,
76}
77
78impl<'cb, 'cx> Connection<'cb, 'cx> {
79	#[inline]
80	/// [xmpp_conn_new](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga0bc7c0e07b52bb7470a97e8d9f9542be)
81	pub fn new(ctx: Context<'cx, 'cb>) -> Self {
82		unsafe {
83			Self::from_owned(
84				sys::xmpp_conn_new(ctx.as_ptr()),
85				ctx,
86				Rc::new(RefCell::new(ConnectionHandlers {
87					connection: None,
88					timed: BoxedHandlers::new(4),
89					stanza: BoxedHandlers::new(4),
90					#[cfg(feature = "libstrophe-0_11_0")]
91					cert_fail_handler_id: None,
92					#[cfg(feature = "libstrophe-0_12_0")]
93					sockopt_handler_id: None,
94					#[cfg(feature = "libstrophe-0_12_0")]
95					password: None,
96					#[cfg(feature = "libstrophe-0_14")]
97					sm_state: None,
98				})),
99			)
100		}
101	}
102
103	unsafe fn with_inner(
104		inner: *mut sys::xmpp_conn_t,
105		ctx: Context<'cx, 'cb>,
106		owned: bool,
107		handlers: Rc<RefCell<ConnectionHandlers<'cb, 'cx>>>,
108	) -> Self {
109		Connection {
110			inner: NonNull::new(inner).expect("Cannot allocate memory for Connection"),
111			ctx: Some(ctx),
112			owned,
113			handlers,
114		}
115	}
116
117	unsafe fn from_owned(
118		inner: *mut sys::xmpp_conn_t,
119		ctx: Context<'cx, 'cb>,
120		handlers: Rc<RefCell<ConnectionHandlers<'cb, 'cx>>>,
121	) -> Self {
122		unsafe { Self::with_inner(inner, ctx, true, handlers) }
123	}
124
125	unsafe fn from_ref_mut(inner: *mut sys::xmpp_conn_t, handlers: Rc<RefCell<ConnectionHandlers<'cb, 'cx>>>) -> Self {
126		unsafe {
127			let ctx = Context::from_ref(sys::xmpp_conn_get_context(inner));
128			Self::with_inner(inner, ctx, false, handlers)
129		}
130	}
131
132	unsafe extern "C" fn connection_handler_cb<CB>(
133		conn_ptr: *mut sys::xmpp_conn_t,
134		event: sys::xmpp_conn_event_t,
135		error: c_int,
136		stream_error: *mut sys::xmpp_stream_error_t,
137		userdata: *mut c_void,
138	) where
139		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb,
140	{
141		let connection_handler = unsafe { ConnectionFatHandler::from_userdata(userdata) };
142		if let Some(fat_handlers) = connection_handler.handlers.upgrade() {
143			let mut conn = unsafe { Self::from_ref_mut(conn_ptr, fat_handlers) };
144			let event = match event {
145				sys::xmpp_conn_event_t::XMPP_CONN_RAW_CONNECT => ConnectionEvent::RawConnect,
146				sys::xmpp_conn_event_t::XMPP_CONN_CONNECT => ConnectionEvent::Connect,
147				sys::xmpp_conn_event_t::XMPP_CONN_DISCONNECT => {
148					let stream_error = unsafe { stream_error.as_ref() }.map(StreamError::from);
149					ConnectionEvent::Disconnect(ConnectionError::from((error, stream_error)))
150				}
151				sys::xmpp_conn_event_t::XMPP_CONN_FAIL => unreachable!("XMPP_CONN_FAIL is never used in the underlying library"),
152			};
153			let ctx = unsafe { conn.context_detached() };
154			ensure_unique!(CB, conn_ptr, userdata, ctx, &mut conn, ConnectionEvent::Connect);
155			(connection_handler.handler)(ctx, &mut conn, event);
156		}
157	}
158
159	unsafe extern "C" fn timed_handler_cb<CB>(conn_ptr: *mut sys::xmpp_conn_t, userdata: *mut c_void) -> c_int
160	where
161		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>) -> HandlerResult + Send + 'cb,
162	{
163		let timed_handler = unsafe { TimedFatHandler::from_userdata(userdata) };
164		if let Some(fat_handlers) = timed_handler.handlers.upgrade() {
165			let mut conn = unsafe { Self::from_ref_mut(conn_ptr, fat_handlers) };
166			let ctx = unsafe { conn.context_detached() };
167			ensure_unique!(CB, conn_ptr, userdata, ctx, &mut conn);
168			let res = (timed_handler.handler)(ctx, &mut conn);
169			if matches!(res, HandlerResult::RemoveHandler) {
170				conn
171					.handlers
172					.borrow_mut()
173					.timed
174					.drop_handler(Self::timed_handler_cb::<CB> as CbAddr);
175			}
176			res as c_int
177		} else {
178			HandlerResult::RemoveHandler as c_int
179		}
180	}
181
182	unsafe extern "C" fn handler_cb<CB>(
183		conn_ptr: *mut sys::xmpp_conn_t,
184		stanza: *mut sys::xmpp_stanza_t,
185		userdata: *mut c_void,
186	) -> c_int
187	where
188		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb,
189	{
190		let stanza_handler = unsafe { StanzaFatHandler::from_userdata(userdata) };
191		if let Some(fat_handlers) = stanza_handler.handlers.upgrade() {
192			let mut conn = unsafe { Self::from_ref_mut(conn_ptr, fat_handlers) };
193			let stanza = unsafe { Stanza::from_ref(stanza) };
194			let ctx = unsafe { conn.context_detached() };
195			ensure_unique!(CB, conn_ptr, userdata, ctx, &mut conn, &stanza);
196			let res = (stanza_handler.handler)(ctx, &mut conn, &stanza);
197			if matches!(res, HandlerResult::RemoveHandler) {
198				conn
199					.handlers
200					.borrow_mut()
201					.stanza
202					.drop_handler(Self::handler_cb::<CB> as CbAddr);
203			}
204			res as c_int
205		} else {
206			HandlerResult::RemoveHandler as c_int
207		}
208	}
209
210	#[inline]
211	unsafe fn context_detached<'a>(&self) -> &'a Context<'cx, 'cb> {
212		self
213			.ctx
214			.as_ref()
215			.map(|ctx| ctx as *const Context)
216			.and_then(|ctx| unsafe { ctx.as_ref() })
217			.unwrap()
218	}
219
220	#[inline]
221	/// [xmpp_conn_get_flags](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga8acc2ae11389af17229b41b4c39ed16e)
222	pub fn flags(&self) -> ConnectionFlags {
223		ConnectionFlags::from_bits(unsafe { sys::xmpp_conn_get_flags(self.inner.as_ptr()) }).unwrap()
224	}
225
226	#[inline]
227	/// [xmpp_conn_set_flags](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga6e36f1cb6ba2e8870ace8d91dd0b1535)
228	pub fn set_flags(&mut self, flags: ConnectionFlags) -> Result<()> {
229		unsafe { sys::xmpp_conn_set_flags(self.inner.as_mut(), flags.bits()) }.into_result()
230	}
231
232	#[inline]
233	/// [xmpp_conn_get_jid](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga37a4edf0ec15c78e570165eb65a3cbad)
234	pub fn jid(&self) -> Option<&str> {
235		unsafe { FFI(sys::xmpp_conn_get_jid(self.inner.as_ptr())).receive() }
236	}
237
238	#[inline]
239	/// [xmpp_conn_get_bound_jid](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga9b055bfeb4d81c009e4d0fcf60c3596a)
240	pub fn bound_jid(&self) -> Option<&str> {
241		unsafe { FFI(sys::xmpp_conn_get_bound_jid(self.inner.as_ptr())).receive() }
242	}
243
244	#[inline]
245	/// [xmpp_conn_set_jid](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gab78bfef71b5c04ba1086da20f79ca61f)
246	pub fn set_jid(&mut self, jid: impl AsRef<str>) {
247		let jid = FFI(jid.as_ref()).send();
248		unsafe { sys::xmpp_conn_set_jid(self.inner.as_mut(), jid.as_ptr()) }
249	}
250
251	#[inline]
252	/// [xmpp_conn_get_pass](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga6b84d1f6f3ef644378138c163b58ed75)
253	pub fn pass(&self) -> Option<&str> {
254		unsafe { FFI(sys::xmpp_conn_get_pass(self.inner.as_ptr())).receive() }
255	}
256
257	#[inline]
258	/// [xmpp_conn_set_pass](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gac5069924deadf5f2e38db01e6e960979)
259	pub fn set_pass(&mut self, pass: impl AsRef<str>) {
260		let pass = FFI(pass.as_ref()).send();
261		unsafe { sys::xmpp_conn_set_pass(self.inner.as_mut(), pass.as_ptr()) }
262	}
263
264	#[inline]
265	#[deprecated = "replaced by set_flags()"]
266	/// [xmpp_conn_disable_tls](https://strophe.im/libstrophe/doc/0.12.2/group___connections.html#ga7f3012810acf47f6713032a26cb97a42)
267	pub fn disable_tls(&mut self) {
268		unsafe { sys::xmpp_conn_disable_tls(self.inner.as_mut()) }
269	}
270
271	#[inline]
272	/// [xmpp_conn_is_secured](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaf37c90a76c0840ace266630025c88a82)
273	pub fn is_secured(&self) -> bool {
274		unsafe { FFI(sys::xmpp_conn_is_secured(self.inner.as_ptr())).receive_bool() }
275	}
276
277	#[inline]
278	#[cfg_attr(feature = "libstrophe-0_12_0", deprecated = "replaced by set_sockopt_callback()")]
279	/// [xmpp_conn_set_keepalive](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga044f1e5d519bff84066317cf8b9fe607)
280	pub fn set_keepalive(&mut self, timeout: Duration, interval: Duration) {
281		unsafe {
282			sys::xmpp_conn_set_keepalive(
283				self.inner.as_mut(),
284				c_int::try_from(timeout.as_secs()).unwrap_or(c_int::MAX),
285				c_int::try_from(interval.as_secs()).unwrap_or(c_int::MAX),
286			)
287		}
288	}
289
290	#[cfg(feature = "libstrophe-0_10_0")]
291	#[inline]
292	/// [xmpp_conn_is_connecting](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaafdd92d7678050c4bf225fa85a442c46)
293	pub fn is_connecting(&self) -> bool {
294		unsafe { FFI(sys::xmpp_conn_is_connecting(self.inner.as_ptr())).receive_bool() }
295	}
296
297	#[cfg(feature = "libstrophe-0_10_0")]
298	#[inline]
299	/// [xmpp_conn_is_connected](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga1956e25131d68c0134a2ee676ee3be3f)
300	pub fn is_connected(&self) -> bool {
301		unsafe { FFI(sys::xmpp_conn_is_connected(self.inner.as_ptr())).receive_bool() }
302	}
303
304	#[cfg(feature = "libstrophe-0_10_0")]
305	#[inline]
306	/// [xmpp_conn_is_disconnected](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gae6560b1fe5f4f637035d379e1c9c1007)
307	pub fn is_disconnected(&self) -> bool {
308		unsafe { FFI(sys::xmpp_conn_is_disconnected(self.inner.as_ptr())).receive_bool() }
309	}
310
311	#[cfg(feature = "libstrophe-0_11_0")]
312	#[inline]
313	/// [xmpp_conn_set_cafile](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga508e9d6fa6b993e337b62af42cb655f6)
314	pub fn set_cafile(&mut self, path: impl AsRef<str>) {
315		let path = FFI(path.as_ref()).send();
316		unsafe { sys::xmpp_conn_set_cafile(self.inner.as_ptr(), path.as_ptr()) }
317	}
318
319	#[cfg(feature = "libstrophe-0_11_0")]
320	#[inline]
321	/// [xmpp_conn_set_capath](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga80b4ecf6a1a364acd26eadd5ab54cb71)
322	pub fn set_capath(&mut self, path: impl AsRef<str>) {
323		let path = FFI(path.as_ref()).send();
324		unsafe { sys::xmpp_conn_set_capath(self.inner.as_ptr(), path.as_ptr()) }
325	}
326
327	#[cfg(feature = "libstrophe-0_11_0")]
328	/// [xmpp_conn_set_certfail_handler](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga4f24b0fb42ab541f902d5e15b3b59b33)
329	/// [xmpp_certfail_handler](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga2e4aa651337c0aaf25b60ea160c2f4bd)
330	///
331	/// Callback function receives [TlsCert] object and an error message.
332	pub fn set_certfail_handler<CB>(&mut self, handler: CB)
333	where
334		CB: Fn(&TlsCert, &str) -> CertFailResult + Send + Sync + 'static,
335	{
336		let callback = internals::certfail_handler_cb::<CB>;
337		if let Ok(mut handlers) = CERT_FAIL_HANDLERS.write() {
338			let type_id = TypeId::of::<CB>();
339			handlers.insert(type_id, Box::new(handler));
340			if let Some(prev_handler_id) = self.handlers.borrow_mut().cert_fail_handler_id.replace(type_id) {
341				handlers.remove(&prev_handler_id);
342			}
343		};
344		unsafe { sys::xmpp_conn_set_certfail_handler(self.inner.as_ptr(), Some(callback)) }
345	}
346
347	#[cfg(feature = "libstrophe-0_11_0")]
348	#[inline]
349	/// [xmpp_conn_get_peer_cert](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga99415d183ffc99de3157876448d3282a)
350	pub fn peer_cert(&self) -> Option<TlsCert> {
351		unsafe {
352			let cert = sys::xmpp_conn_get_peer_cert(self.inner.as_ptr());
353			if cert.is_null() {
354				None
355			} else {
356				Some(TlsCert::from_owned(cert))
357			}
358		}
359	}
360
361	#[cfg(feature = "libstrophe-0_11_0")]
362	#[inline]
363	/// [xmpp_conn_set_client_cert](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#gac3d770588b083d2053a6361c9e49f235)
364	pub fn set_client_cert(&mut self, cert_path: &str, key_path: &str) {
365		let cert_path = FFI(cert_path).send();
366		let key_path = FFI(key_path).send();
367		unsafe {
368			sys::xmpp_conn_set_client_cert(self.inner.as_ptr(), cert_path.as_ptr(), key_path.as_ptr());
369		}
370	}
371
372	#[cfg(feature = "libstrophe-0_11_0")]
373	#[inline]
374	/// [xmpp_conn_cert_xmppaddr_num](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#gaad61d0db95b0f22876df9403a728c806)
375	pub fn cert_xmppaddr_num(&self) -> u32 {
376		unsafe { sys::xmpp_conn_cert_xmppaddr_num(self.inner.as_ptr()) }
377	}
378
379	#[cfg(feature = "libstrophe-0_11_0")]
380	#[inline]
381	/// [xmpp_conn_cert_xmppaddr](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga755f47fb1fbe8ce8e43ea93e5bc103a7)
382	pub fn cert_xmppaddr(&self, n: u32) -> Option<String> {
383		unsafe { FFI(sys::xmpp_conn_cert_xmppaddr(self.inner.as_ptr(), n)).receive_with_free(|x| crate::ALLOC_CONTEXT.free(x)) }
384	}
385
386	#[cfg(feature = "libstrophe-0_12_0")]
387	#[inline]
388	/// [xmpp_conn_set_password_callback](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#gadcd27378977412d49ede93a5542f01e4)
389	/// [xmpp_password_callback](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga140b726d8daf4175a009b3f7cb414593)
390	///
391	/// Callback function receives Connection object and maximum allowed length of the password, it returns `Some(String)` with password
392	/// on success or None in case of error. If the returned `String` is longer than maximum allowed length it is ignored and the error
393	/// is returned.
394	pub fn set_password_callback<CB>(&mut self, handler: CB)
395	where
396		CB: Fn(&Connection<'cb, 'cx>, usize) -> Option<String> + Send + 'cb,
397	{
398		let callback = internals::password_handler_cb;
399		let handler = BoxedHandler::make(
400			&self.handlers,
401			Box::new(handler) as Box<PasswordCallback>,
402			callback as CbAddr,
403			(),
404		);
405		let mut handlers = self.handlers.borrow_mut();
406		// keep the old handler alive until the new one is set to avoid referencing the freed memory if it gets called in-between
407		let (_old_handler, new_handler) = handlers.password.set_handler(handler);
408		unsafe {
409			sys::xmpp_conn_set_password_callback(self.inner.as_mut(), Some(callback), new_handler.as_userdata());
410		}
411	}
412
413	#[cfg(feature = "libstrophe-0_12_0")]
414	#[inline]
415	/// Remove the callback set by [Self::set_password_callback()]
416	///
417	/// Returns the amount of the removed handlers.
418	pub fn clear_password_callback(&mut self) -> usize {
419		unsafe { sys::xmpp_conn_set_password_callback(self.inner.as_mut(), None, ptr::null_mut()) }
420		self.handlers.borrow_mut().password.take().into_iter().count()
421	}
422
423	#[cfg(feature = "libstrophe-0_12_0")]
424	/// [xmpp_conn_set_sockopt_callback](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga40d4c1bc7dbd22d356067fd2105ba685)
425	/// [xmpp_sockopt_callback](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gab69556790910b0875d9aa8564c415384)
426	///
427	/// Callback function receives pointer to a system-dependent socket object. See docs above for more details.
428	pub fn set_sockopt_callback<CB>(&mut self, handler: CB)
429	where
430		CB: Fn(*mut c_void) -> SockoptResult + Send + Sync + 'static,
431	{
432		let callback = internals::sockopt_callback::<CB>;
433		if let Ok(mut handlers) = SOCKOPT_HANDLERS.write() {
434			let type_id = TypeId::of::<CB>();
435			handlers.insert(type_id, Box::new(handler));
436			if let Some(prev_handler_id) = self.handlers.borrow_mut().sockopt_handler_id.replace(type_id) {
437				handlers.remove(&prev_handler_id);
438			}
439		};
440		unsafe { sys::xmpp_conn_set_sockopt_callback(self.inner.as_mut(), Some(callback)) }
441	}
442
443	#[cfg(feature = "libstrophe-0_12_0")]
444	#[inline]
445	/// [xmpp_sockopt_cb_keepalive](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga044f1e5d519bff84066317cf8b9fe607)
446	///
447	/// Sets default sockopt_callback function that just uses compile-time internal defaults for the socket timeout. Those
448	/// values can be changed with a deprecated [Connection::set_keepalive()]. If you use that function then you don't need to call
449	/// [Connection::set_default_sockopt_callback()] manually because it will be called internally.
450	pub fn set_default_sockopt_callback(&mut self) {
451		unsafe { sys::xmpp_conn_set_sockopt_callback(self.inner.as_mut(), Some(sys::xmpp_sockopt_cb_keepalive)) }
452	}
453
454	#[cfg(feature = "libstrophe-0_12_0")]
455	#[inline]
456	/// [xmpp_conn_set_password_retries](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#ga0908b5362c1169db0867c5f01e8a64ae)
457	pub fn set_password_retries(&mut self, n: u32) {
458		unsafe { sys::xmpp_conn_set_password_retries(self.inner.as_ptr(), n) }
459	}
460
461	#[cfg(feature = "libstrophe-0_12_0")]
462	#[inline]
463	/// [xmpp_conn_get_keyfile](https://strophe.im/libstrophe/doc/0.13.0/group___t_l_s.html#gab105ceda87046748f8e9275b066438a3)
464	pub fn get_keyfile(&self) -> Option<&str> {
465		unsafe { FFI(sys::xmpp_conn_get_keyfile(self.inner.as_ptr())).receive() }
466	}
467
468	#[cfg(feature = "libstrophe-0_12_0")]
469	#[inline]
470	/// [xmpp_conn_send_queue_len](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga769256a8b1460721deaf19c84b78ead9)
471	pub fn send_queue_len(&self) -> i32 {
472		unsafe { sys::xmpp_conn_send_queue_len(self.inner.as_ptr()) }
473	}
474
475	#[cfg(feature = "libstrophe-0_12_0")]
476	#[inline]
477	/// [xmpp_conn_send_queue_drop_element](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga0fc31cf27113a905934c7cf8bb9b9c19)
478	pub fn send_queue_drop_element(&mut self, which: QueueElement) -> Option<String> {
479		unsafe {
480			FFI(sys::xmpp_conn_send_queue_drop_element(self.inner.as_ptr(), which))
481				.receive_with_free(|x| crate::ALLOC_CONTEXT.free(x))
482		}
483	}
484
485	#[cfg(feature = "libstrophe-0_12_0")]
486	#[inline]
487	/// [xmpp_conn_get_sm_state](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaf990c2fd8867258545b182f52df1465e)
488	///
489	/// Because in the underlying library the output [SMState] object retains the reference to the [Connection] we need to prevent
490	/// modifications to the [Connection] while a particular instance of [SMState] is alive.
491	pub fn sm_state(&mut self) -> Option<SMState<'_, '_>> {
492		let inner = unsafe { sys::xmpp_conn_get_sm_state(self.inner.as_mut()) };
493		if inner.is_null() {
494			None
495		} else {
496			Some(unsafe { SMState::new(inner) })
497		}
498	}
499
500	#[cfg(feature = "libstrophe-0_14")]
501	#[inline]
502	/// [xmpp_conn_set_sm_callback](https://github.com/strophe/libstrophe/blob/0.14.0/src/conn.c#L1258)
503	pub fn set_sm_callback<CB>(&mut self, handler: CB)
504	where
505		// todo: change that to Fn and try to use Arc for sharing the FatHandler
506		CB: FnMut(&mut Connection<'cb, 'cx>, SerializedSmStateRef) + Send + 'cb,
507	{
508		let callback = internals::sm_state_handler_cb;
509		let new_handler = BoxedHandler::make(
510			&self.handlers,
511			Box::new(handler) as Box<SmStateCallback>,
512			callback as CbAddr,
513			(),
514		);
515		let mut handlers = self.handlers.borrow_mut();
516		// keep the old handler alive until the new one is set to avoid referencing the freed memory if it gets called in-between
517		let (_old_handler, new_handler) = handlers.sm_state.set_handler(new_handler);
518		unsafe { sys::xmpp_conn_set_sm_callback(self.inner.as_mut(), Some(callback), new_handler.as_userdata()) }
519	}
520
521	#[cfg(feature = "libstrophe-0_14")]
522	#[inline]
523	/// Remove the callback set by [Self::set_sm_callback()]
524	///
525	/// Returns the amount of the removed handlers.
526	pub fn clear_sm_callback(&mut self) -> usize {
527		unsafe { sys::xmpp_conn_set_sm_callback(self.inner.as_mut(), None, ptr::null_mut()) }
528		self.handlers.borrow_mut().sm_state.take().into_iter().count()
529	}
530
531	#[cfg(feature = "libstrophe-0_14")]
532	#[inline]
533	/// [xmpp_conn_restore_sm_state](https://github.com/strophe/libstrophe/blob/0.14.0/src/conn.c#L1332)
534	pub fn restore_sm_state(&mut self, sm_state: SerializedSmState) -> Result<()> {
535		unsafe { sys::xmpp_conn_restore_sm_state(self.inner.as_mut(), sm_state.buf.as_ptr(), sm_state.buf.len()) }.into_result()
536	}
537
538	/// [xmpp_connect_client](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga9354fc82ccbbce2840fca7efa9603c13)
539	/// [xmpp_conn_handler](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#aad7c657ae239a87e2c2b746f99138e99)
540	pub fn connect_client<CB>(
541		mut self,
542		alt_host: Option<&str>,
543		alt_port: impl Into<Option<u16>>,
544		handler: CB,
545	) -> Result<Context<'cx, 'cb>, ConnectClientError<'cb, 'cx>>
546	where
547		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb,
548	{
549		let alt_host = FFI(alt_host).send();
550		let alt_port = Nullable::from(alt_port.into());
551		if self.jid().is_none() {
552			return Err(ConnectClientError {
553				conn: self,
554				error: Error::InvalidOperation,
555			});
556		}
557		let callback = Self::connection_handler_cb::<CB>;
558		let new_handler = BoxedHandler::make(
559			&self.handlers,
560			Box::new(handler) as Box<ConnectionCallback>,
561			callback as CbAddr,
562			(),
563		);
564		let (res, old_handler) = {
565			let mut handlers = self.handlers.borrow_mut();
566			let (old_handler, fat_handler) = handlers.connection.set_handler(new_handler);
567			let res = unsafe {
568				sys::xmpp_connect_client(
569					self.inner.as_mut(),
570					alt_host.as_ptr(),
571					alt_port.val(),
572					Some(callback),
573					fat_handler.as_userdata(),
574				)
575			}
576			.into_result();
577			(res, old_handler)
578		};
579		match res {
580			Ok(_) => {
581				let mut out = self.ctx.take().expect("Internal context is empty, it must never happen");
582				out.consume_connection(self);
583				Ok(out)
584			}
585			Err(e) => {
586				self.handlers.borrow_mut().connection = old_handler;
587				Err(ConnectClientError { conn: self, error: e })
588			}
589		}
590	}
591
592	/// [xmpp_connect_component](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaa1cfa1189fdf64bb443c68f0590fd069)
593	/// [xmpp_conn_handler](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#aad7c657ae239a87e2c2b746f99138e99)
594	///
595	/// See also [Self::connect_client()] for additional info.
596	pub fn connect_component<CB>(
597		mut self,
598		host: impl AsRef<str>,
599		port: impl Into<Option<u16>>,
600		handler: CB,
601	) -> Result<Context<'cx, 'cb>, ConnectClientError<'cb, 'cx>>
602	where
603		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb,
604	{
605		let host = FFI(host.as_ref()).send();
606		let port = Nullable::from(port.into());
607		let callback = Self::connection_handler_cb::<CB>;
608		let new_handler = BoxedHandler::make(
609			&self.handlers,
610			Box::new(handler) as Box<ConnectionCallback>,
611			callback as CbAddr,
612			(),
613		);
614		let (res, old_handler) = {
615			let mut handlers = self.handlers.borrow_mut();
616			let (old_handler, new_handler) = handlers.connection.set_handler(new_handler);
617			let res = unsafe {
618				sys::xmpp_connect_component(
619					self.inner.as_mut(),
620					host.as_ptr(),
621					port.val(),
622					Some(callback),
623					new_handler.as_userdata(),
624				)
625			}
626			.into_result();
627			(res, old_handler)
628		};
629		match res {
630			Ok(_) => {
631				let mut out = self.ctx.take().expect("Internal context is empty, it must never happen");
632				out.consume_connection(self);
633				Ok(out)
634			}
635			Err(e) => {
636				self.handlers.borrow_mut().connection = old_handler;
637				Err(ConnectClientError { conn: self, error: e })
638			}
639		}
640	}
641
642	/// [xmpp_connect_raw](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga3873544638e8123c667f074d86dbad5a)
643	/// [xmpp_conn_handler](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#aad7c657ae239a87e2c2b746f99138e99)
644	///
645	/// See also [Self::connect_client()] for additional info.
646	pub fn connect_raw<CB>(
647		mut self,
648		alt_host: Option<&str>,
649		alt_port: impl Into<Option<u16>>,
650		handler: CB,
651	) -> Result<Context<'cx, 'cb>, ConnectClientError<'cb, 'cx>>
652	where
653		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb,
654	{
655		let alt_host = FFI(alt_host).send();
656		let alt_port = Nullable::from(alt_port.into());
657		if self.jid().is_none() {
658			return Err(ConnectClientError {
659				conn: self,
660				error: Error::InvalidOperation,
661			});
662		}
663		let callback = Self::connection_handler_cb::<CB>;
664		let new_handler = BoxedHandler::make(
665			&self.handlers,
666			Box::new(handler) as Box<ConnectionCallback>,
667			callback as CbAddr,
668			(),
669		);
670		let (res, old_handler) = {
671			let mut handlers = self.handlers.borrow_mut();
672			let (old_handler, new_handler) = handlers.connection.set_handler(new_handler);
673			let res = unsafe {
674				sys::xmpp_connect_raw(
675					self.inner.as_mut(),
676					alt_host.as_ptr(),
677					alt_port.val(),
678					Some(callback),
679					new_handler.as_userdata(),
680				)
681			}
682			.into_result();
683			(res, old_handler)
684		};
685		match res {
686			Ok(_) => {
687				let mut out = self.ctx.take().expect("Internal context is empty, it must never happen");
688				out.consume_connection(self);
689				Ok(out)
690			}
691			Err(e) => {
692				self.handlers.borrow_mut().connection = old_handler;
693				Err(ConnectClientError { conn: self, error: e })
694			}
695		}
696	}
697
698	#[inline]
699	/// [xmpp_conn_open_stream_default](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga73e477d4abfd439bcd27ddf78d601c0f)
700	///
701	/// Related to [Self::connect_raw()].
702	pub fn open_stream_default(&self) -> Result<()> {
703		unsafe { sys::xmpp_conn_open_stream_default(self.inner.as_ptr()) }.into_result()
704	}
705
706	/// [xmpp_conn_open_stream](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga747589e1fdf44891c601958742d115b7)
707	///
708	/// Related to [Self::connect_raw()].
709	pub fn open_stream(&self, attributes: &HashMap<&str, &str>) -> Result<()> {
710		let mut storage = Vec::with_capacity(attributes.len() * 2);
711		for (name, val) in attributes {
712			storage.push(FFI(*name).send());
713			storage.push(FFI(*val).send());
714		}
715		let mut attrs = storage.iter().map(|s| s.as_ptr().cast_mut()).collect::<Vec<_>>();
716		unsafe { sys::xmpp_conn_open_stream(self.inner.as_ptr(), attrs.as_mut_ptr(), attrs.len()) }.into_result()
717	}
718
719	#[inline]
720	/// [xmpp_conn_tls_start](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga65a92215a59a365f89e908e90178f7b8)
721	///
722	/// Related to [Self::connect_raw()].
723	pub fn tls_start(&self) -> Result<()> {
724		unsafe { sys::xmpp_conn_tls_start(self.inner.as_ptr()) }.into_result()
725	}
726
727	#[inline]
728	/// [xmpp_disconnect](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaa635ceddb5941d011e290073f7552355)
729	pub fn disconnect(&mut self) {
730		unsafe { sys::xmpp_disconnect(self.inner.as_mut()) }
731	}
732
733	#[inline]
734	/// [xmpp_send_raw_string](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaf67110aced5d20909069d33d17bec025)
735	///
736	/// Be aware that this method performs a lot of allocations internally so you might want to use [Self::send_raw()] instead.
737	pub fn send_raw_string(&mut self, data: impl AsRef<str>) {
738		let data = FFI(data.as_ref()).send();
739		unsafe {
740			sys::xmpp_send_raw_string(self.inner.as_mut(), data.as_ptr());
741		}
742	}
743
744	/// [xmpp_send_raw](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#gaa1be7bdb58f3610b7997f1186d87c896)
745	pub fn send_raw(&mut self, data: impl AsRef<[u8]>) {
746		let data = data.as_ref();
747		#[cfg(feature = "log")]
748		if log::log_enabled!(log::Level::Debug) {
749			use std::fmt::Write;
750
751			use crate::LogLevel;
752
753			let ctx = unsafe { sys::xmpp_conn_get_context(self.inner.as_ptr()) };
754			let mut data_str = "SENT: ".to_owned();
755			if let Ok(data) = str::from_utf8(data) {
756				data_str.push_str(data);
757			} else {
758				write!(&mut data_str, "{data:?}").expect("Can't write to string");
759			}
760			unsafe {
761				crate::context::ctx_log(ctx, LogLevel::XMPP_LEVEL_DEBUG, "conn", &data_str);
762			}
763		}
764		unsafe {
765			sys::xmpp_send_raw(self.inner.as_mut(), data.as_ptr().cast::<c_char>(), data.len());
766		}
767	}
768
769	#[inline]
770	/// [xmpp_send](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga0e879d34b2ea28c08cacbb012eadfbc1)
771	pub fn send(&mut self, stanza: &Stanza) {
772		unsafe { sys::xmpp_send(self.inner.as_mut(), stanza.as_ptr()) }
773	}
774
775	/// [xmpp_timed_handler_add](https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html#ga5835cd8c81174d06d35953e8b13edccb)
776	/// [xmpp_timed_handler](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#a94af0b39027071eca8c16e9891314bb4)
777	///
778	/// See [Self::handler_add] for additional information.
779	pub fn timed_handler_add<CB>(&mut self, handler: CB, period: Duration) -> Option<TimedHandlerId>
780	where
781		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>) -> HandlerResult + Send + 'cb,
782	{
783		let callback = Self::timed_handler_cb::<CB>;
784		let handler = BoxedHandler::make(
785			&self.handlers,
786			Box::new(handler) as Box<TimedCallback>,
787			callback as CbAddr,
788			(),
789		);
790		self.handlers.borrow_mut().timed.store(handler).map(|handler| {
791			unsafe {
792				sys::xmpp_timed_handler_add(
793					self.inner.as_mut(),
794					Some(callback),
795					c_ulong::try_from(period.as_millis()).unwrap_or(c_ulong::MAX),
796					handler.as_userdata(),
797				);
798			}
799			TimedHandlerId(handler.cb_addr())
800		})
801	}
802
803	/// [xmpp_timed_handler_delete](https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html#gadbc8e82d9d3ee6ab4166ce4dba0ea8dd)
804	///
805	/// See [Self::handler_delete()] for additional information.
806	pub fn timed_handler_delete(&mut self, handler_id: TimedHandlerId) -> usize {
807		#![allow(clippy::needless_pass_by_value)]
808		unsafe {
809			sys::xmpp_timed_handler_delete(
810				self.inner.as_mut(),
811				Some(mem::transmute::<
812					CbAddr,
813					unsafe extern "C" fn(conn: *mut sys::xmpp_conn_t, userdata: *mut c_void) -> c_int,
814				>(handler_id.0)),
815			)
816		}
817		self.handlers.borrow_mut().timed.drop_handler(handler_id.0)
818	}
819
820	/// See [Self::handlers_clear()] for additional information.
821	pub fn timed_handlers_clear(&mut self) -> usize {
822		let mut removed_count = 0;
823		self.handlers.borrow_mut().timed.retain(|handler| {
824			unsafe {
825				sys::xmpp_timed_handler_delete(
826					self.inner.as_mut(),
827					Some(mem::transmute::<
828						CbAddr,
829						unsafe extern "C" fn(conn: *mut sys::xmpp_conn_t, userdata: *mut c_void) -> c_int,
830					>(handler.cb_addr)),
831				)
832			};
833			removed_count += 1;
834			false
835		});
836		removed_count
837	}
838
839	/// [xmpp_id_handler_add](https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html#gafaa44ec48db44b45c5d240c7df4bfaac)
840	/// [xmpp_handler](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#a079ae14399be93d363164ad35d434496)
841	///
842	/// See [Self::handler_add()] for additional information.
843	pub fn id_handler_add<CB>(&mut self, handler: CB, id: impl Into<String>) -> Option<IdHandlerId>
844	where
845		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb,
846	{
847		let id = id.into();
848		let ffi_id = FFI(id.as_str()).send();
849		let callback = Self::handler_cb::<CB>;
850		let handler = BoxedHandler::make(
851			&self.handlers,
852			Box::new(handler) as Box<StanzaCallback>,
853			callback as CbAddr,
854			Some(id),
855		);
856		self.handlers.borrow_mut().stanza.store(handler).map(|handler| {
857			unsafe {
858				sys::xmpp_id_handler_add(self.inner.as_mut(), Some(callback), ffi_id.as_ptr(), handler.as_userdata());
859			}
860			IdHandlerId(handler.cb_addr())
861		})
862	}
863
864	/// [xmpp_id_handler_delete](https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html#gaee081149b7c6889b6b692a44b407d42d)
865	///
866	/// See [Self::handler_delete()] for additional information.
867	pub fn id_handler_delete(&mut self, handler_id: IdHandlerId) -> usize {
868		#![allow(clippy::needless_pass_by_value)]
869		if let Some(fat_handler) = self.handlers.borrow().stanza.validate(handler_id.0) {
870			let id = FFI(fat_handler.extra.as_ref().unwrap().as_str()).send();
871			unsafe {
872				sys::xmpp_id_handler_delete(
873					self.inner.as_mut(),
874					Some(mem::transmute::<
875						CbAddr,
876						unsafe extern "C" fn(
877							conn: *mut sys::xmpp_conn_t,
878							stanza: *mut sys::xmpp_stanza_t,
879							userdata: *mut c_void,
880						) -> c_int,
881					>(handler_id.0)),
882					id.as_ptr(),
883				)
884			}
885		}
886		self.handlers.borrow_mut().stanza.drop_handler(handler_id.0)
887	}
888
889	/// See [Self::handlers_clear()] for additional information.
890	pub fn id_handlers_clear(&mut self) -> usize {
891		let mut removed_count = 0;
892		self.handlers.borrow_mut().stanza.retain(|handler| {
893			let keep_it = if let Some(ref id) = handler.extra {
894				unsafe {
895					sys::xmpp_id_handler_delete(
896						self.inner.as_ptr(),
897						Some(mem::transmute::<
898							CbAddr,
899							unsafe extern "C" fn(
900								conn: *mut sys::xmpp_conn_t,
901								stanza: *mut sys::xmpp_stanza_t,
902								userdata: *mut c_void,
903							) -> c_int,
904						>(handler.cb_addr)),
905						FFI(id.as_str()).send().as_ptr(),
906					)
907				};
908				false
909			} else {
910				true
911			};
912			if !keep_it {
913				removed_count += 1;
914			}
915			keep_it
916		});
917		removed_count
918	}
919
920	/// [xmpp_handler_add](https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html#ga73235438899b51d265c1d35915c5cd7c)
921	/// [xmpp_handler](https://strophe.im/libstrophe/doc/0.13.0/strophe_8h.html#a079ae14399be93d363164ad35d434496)
922	///
923	/// This function returns [HandlerId] which is later can be used to remove the handler using [Self::handler_delete()].
924	pub fn handler_add<CB>(&mut self, handler: CB, ns: Option<&str>, name: Option<&str>, typ: Option<&str>) -> Option<HandlerId>
925	where
926		CB: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb,
927	{
928		let ns = FFI(ns).send();
929		let name = FFI(name).send();
930		let typ = FFI(typ).send();
931		let callback = Self::handler_cb::<CB>;
932		let handler = BoxedHandler::make(
933			&self.handlers,
934			Box::new(handler) as Box<StanzaCallback>,
935			callback as CbAddr,
936			None,
937		);
938		self.handlers.borrow_mut().stanza.store(handler).map(|handler| {
939			unsafe {
940				sys::xmpp_handler_add(
941					self.inner.as_mut(),
942					Some(callback),
943					ns.as_ptr(),
944					name.as_ptr(),
945					typ.as_ptr(),
946					handler.as_userdata(),
947				)
948			}
949			HandlerId(handler.cb_addr())
950		})
951	}
952
953	/// [xmpp_handler_delete](https://strophe.im/libstrophe/doc/0.13.0/group___handlers.html#gaf4fa6f67b11dee0158739c907ba71adb)
954	///
955	/// This version of this function accepts [HandlerId] returned from [Self::add_handler()] function instead of function
956	/// reference as the underlying library does. If you can't keep track of those handles, but still want ability to remove
957	/// handlers, check [Self::handlers_clear()] function. Returns the amount of the removed handlers.
958	pub fn handler_delete(&mut self, handler_id: HandlerId) -> usize {
959		#![allow(clippy::needless_pass_by_value)]
960		unsafe {
961			sys::xmpp_handler_delete(
962				self.inner.as_mut(),
963				Some(mem::transmute::<
964					CbAddr,
965					unsafe extern "C" fn(conn: *mut sys::xmpp_conn_t, stanza: *mut sys::xmpp_stanza_t, userdata: *mut c_void) -> c_int,
966				>(handler_id.0)),
967			)
968		}
969		self.handlers.borrow_mut().stanza.drop_handler(handler_id.0)
970	}
971
972	/// Removes all handlers that were set up with [Self::handler_add()].
973	///
974	/// This function does *not* remove handlers added via [Self::id_handler_add()]. You can use this function if you can't keep
975	/// track of specific closure handles returned from [Self::handler_add()], but want to remove handlers anyway. Returns the
976	/// amount of the removed handlers.
977	pub fn handlers_clear(&mut self) -> usize {
978		let mut removed_count = 0;
979		self.handlers.borrow_mut().stanza.retain(|handler| {
980			let keep_it = if handler.extra.is_none() {
981				unsafe {
982					sys::xmpp_handler_delete(
983						self.inner.as_ptr(),
984						Some(mem::transmute::<
985							CbAddr,
986							unsafe extern "C" fn(
987								conn: *mut sys::xmpp_conn_t,
988								stanza: *mut sys::xmpp_stanza_t,
989								userdata: *mut c_void,
990							) -> c_int,
991						>(handler.cb_addr)),
992					)
993				};
994				false
995			} else {
996				true
997			};
998			if !keep_it {
999				removed_count += 1;
1000			}
1001			keep_it
1002		});
1003		removed_count
1004	}
1005
1006	#[allow(dead_code)]
1007	pub(crate) fn timed_handlers_same<L, R>(_left: L, _right: R) -> bool
1008	where
1009		L: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>) -> HandlerResult + Send + 'cb,
1010		R: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>) -> HandlerResult + Send + 'cb,
1011	{
1012		ptr::eq(
1013			Self::timed_handler_cb::<L> as *const (),
1014			Self::timed_handler_cb::<R> as *const (),
1015		)
1016	}
1017
1018	#[allow(dead_code)]
1019	pub(crate) fn stanza_handlers_same<L, R>(_left: L, _right: R) -> bool
1020	where
1021		L: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb,
1022		R: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb,
1023	{
1024		ptr::eq(Self::handler_cb::<L> as *const (), Self::handler_cb::<R> as *const ())
1025	}
1026
1027	#[allow(dead_code)]
1028	pub(crate) fn connection_handlers_same<L, R>(_left: L, _right: R) -> bool
1029	where
1030		L: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb,
1031		R: FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb,
1032	{
1033		ptr::eq(
1034			Self::connection_handler_cb::<L> as *const (),
1035			Self::connection_handler_cb::<R> as *const (),
1036		)
1037	}
1038}
1039
1040impl PartialEq for Connection<'_, '_> {
1041	fn eq(&self, other: &Connection) -> bool {
1042		self.inner == other.inner
1043	}
1044}
1045
1046impl Eq for Connection<'_, '_> {}
1047
1048impl Drop for Connection<'_, '_> {
1049	/// [xmpp_conn_release](https://strophe.im/libstrophe/doc/0.13.0/group___connections.html#ga87b076b11589bc23123096dc83cde6a8)
1050	fn drop(&mut self) {
1051		if self.owned {
1052			unsafe {
1053				sys::xmpp_conn_release(self.inner.as_mut());
1054			}
1055			#[cfg(feature = "libstrophe-0_11_0")]
1056			if let Ok(mut handlers) = CERT_FAIL_HANDLERS.write() {
1057				if let Some(handler_id) = self.handlers.borrow_mut().cert_fail_handler_id.take() {
1058					handlers.remove(&handler_id);
1059				}
1060			}
1061			#[cfg(feature = "libstrophe-0_12_0")]
1062			if let Ok(mut handlers) = SOCKOPT_HANDLERS.write() {
1063				if let Some(handler_id) = self.handlers.borrow_mut().sockopt_handler_id.take() {
1064					handlers.remove(&handler_id);
1065				}
1066			}
1067		}
1068	}
1069}
1070
1071unsafe impl Send for Connection<'_, '_> {}
1072
1073#[derive(Debug)]
1074pub struct HandlerId(CbAddr);
1075
1076#[derive(Debug)]
1077pub struct TimedHandlerId(CbAddr);
1078
1079#[derive(Debug)]
1080pub struct IdHandlerId(CbAddr);
1081
1082#[derive(Debug)]
1083pub enum ConnectionEvent<'t, 's> {
1084	RawConnect,
1085	Connect,
1086	Disconnect(Option<ConnectionError<'t, 's>>),
1087	//	Fail(ConnectionError<'t, 's>), // never actually used in the underlying library
1088}
1089
1090impl fmt::Display for ConnectionEvent<'_, '_> {
1091	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1092		match self {
1093			ConnectionEvent::RawConnect => write!(f, "Raw connect"),
1094			ConnectionEvent::Connect => write!(f, "Connect"),
1095			ConnectionEvent::Disconnect(None) => write!(f, "Disconnect"),
1096			ConnectionEvent::Disconnect(Some(e)) => write!(f, "Disconnect, error: {e}"),
1097		}
1098	}
1099}
1100
1101#[test]
1102fn callbacks() {
1103	{
1104		let a = |_: &Context, _: &mut Connection| {
1105			print!("1");
1106			HandlerResult::KeepHandler
1107		};
1108		let b = |_: &Context, _: &mut Connection| {
1109			print!("2");
1110			HandlerResult::RemoveHandler
1111		};
1112
1113		assert!(Connection::timed_handlers_same(a, a));
1114		assert!(!Connection::timed_handlers_same(a, b));
1115	}
1116
1117	{
1118		let a = |_: &Context, _: &mut Connection, _: &Stanza| {
1119			print!("1");
1120			HandlerResult::KeepHandler
1121		};
1122		let b = |_: &Context, _: &mut Connection, _: &Stanza| {
1123			print!("2");
1124			HandlerResult::KeepHandler
1125		};
1126
1127		assert!(Connection::stanza_handlers_same(a, a));
1128		assert!(!Connection::stanza_handlers_same(a, b));
1129	}
1130
1131	{
1132		let a = |_: &Context, _: &mut Connection, _: ConnectionEvent| print!("1");
1133		let b = |_: &Context, _: &mut Connection, _: ConnectionEvent| print!("2");
1134
1135		assert!(Connection::connection_handlers_same(a, a));
1136		assert!(!Connection::connection_handlers_same(a, b));
1137	}
1138}