1#[cfg(feature = "libstrophe-0_11_0")]
2use core::any::TypeId;
3use core::cell::RefCell;
4#[cfg(feature = "libstrophe-0_14")]
5use core::slice;
6use core::{fmt, ptr};
7#[cfg(feature = "libstrophe-0_12_0")]
8use std::ffi::CString;
9use std::ffi::c_void;
10#[cfg(feature = "libstrophe-0_11_0")]
11use std::os::raw::{c_char, c_int};
12use std::rc::{Rc, Weak};
13
14#[cfg(feature = "libstrophe-0_11_0")]
15pub use libstrophe_0_11::*;
16#[cfg(feature = "libstrophe-0_12_0")]
17pub use libstrophe_0_12::*;
18#[cfg(feature = "libstrophe-0_14")]
19pub use libstrophe_0_14::*;
20
21#[cfg(feature = "libstrophe-0_14")]
22use crate::SerializedSmStateRef;
23use crate::{Connection, ConnectionEvent, Context, Stanza, as_void_ptr, void_ptr_as};
24
25#[cfg(feature = "libstrophe-0_11_0")]
26mod libstrophe_0_11 {
27 use core::any::TypeId;
28 use std::collections::HashMap;
29 use std::sync::RwLock;
30
31 use once_cell::sync::Lazy;
32
33 use crate::TlsCert;
34
35 pub type CertFailCallback = dyn Fn(&TlsCert, &str) -> CertFailResult + Send + Sync;
36 pub static CERT_FAIL_HANDLERS: Lazy<RwLock<HashMap<TypeId, Box<CertFailCallback>>>> = Lazy::new(Default::default);
39
40 #[derive(Debug)]
41 #[repr(i32)]
42 pub enum CertFailResult {
43 TerminateConnection = 0,
44 EstablishConnection = 1,
45 }
46}
47
48#[cfg(feature = "libstrophe-0_12_0")]
49mod libstrophe_0_12 {
50 use core::any::TypeId;
51 use core::ffi::c_void;
52 use std::collections::HashMap;
53 use std::sync::RwLock;
54
55 use once_cell::sync::Lazy;
56
57 use super::FatHandler;
58 use crate::Connection;
59
60 pub type SockoptCallback = dyn Fn(*mut c_void) -> SockoptResult + Send + Sync;
61 pub static SOCKOPT_HANDLERS: Lazy<RwLock<HashMap<TypeId, Box<SockoptCallback>>>> = Lazy::new(Default::default);
62
63 #[derive(Debug)]
64 #[repr(i32)]
65 pub enum SockoptResult {
66 Ok = 0,
67 Error = -1,
68 }
69
70 pub type PasswordCallback<'cb, 'cx> = dyn Fn(&Connection<'cb, 'cx>, usize) -> Option<String> + Send + 'cb;
71 pub type PasswordFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, PasswordCallback<'cb, 'cx>, ()>;
72}
73
74#[cfg(feature = "libstrophe-0_14")]
75mod libstrophe_0_14 {
76 use super::FatHandler;
77 use crate::Connection;
78 use crate::sm_state::SerializedSmStateRef;
79
80 pub type SmStateCallback<'cb, 'cx> = dyn FnMut(&mut Connection<'cb, 'cx>, SerializedSmStateRef) + Send + 'cb;
81 pub type SmStateFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, SmStateCallback<'cb, 'cx>, ()>;
82}
83
84#[derive(Debug)]
85#[repr(i32)]
86pub enum HandlerResult {
87 RemoveHandler = 0,
88 KeepHandler = 1,
89}
90
91pub type ConnectionCallback<'cb, 'cx> = dyn FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb;
92pub type ConnectionFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, ConnectionCallback<'cb, 'cx>, ()>;
93
94pub type TimedCallback<'cb, 'cx> = dyn FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>) -> HandlerResult + Send + 'cb;
95pub type TimedFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, TimedCallback<'cb, 'cx>, ()>;
96
97pub type StanzaCallback<'cb, 'cx> =
98 dyn FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb;
99pub type StanzaFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, StanzaCallback<'cb, 'cx>, Option<String>>;
100
101pub struct ConnectionHandlers<'cb, 'cx> {
102 pub connection: Option<BoxedHandler<'cb, 'cx, ConnectionCallback<'cb, 'cx>, ()>>,
103 pub timed: BoxedHandlers<'cb, 'cx, TimedCallback<'cb, 'cx>, ()>,
104 pub stanza: BoxedHandlers<'cb, 'cx, StanzaCallback<'cb, 'cx>, Option<String>>,
105 #[cfg(feature = "libstrophe-0_11_0")]
106 pub cert_fail_handler_id: Option<TypeId>,
107 #[cfg(feature = "libstrophe-0_12_0")]
108 pub sockopt_handler_id: Option<TypeId>,
109 #[cfg(feature = "libstrophe-0_12_0")]
110 pub password: Option<BoxedHandler<'cb, 'cx, PasswordCallback<'cb, 'cx>, ()>>,
111 #[cfg(feature = "libstrophe-0_14")]
112 pub sm_state: Option<BoxedHandler<'cb, 'cx, SmStateCallback<'cb, 'cx>, ()>>,
113}
114
115impl fmt::Debug for ConnectionHandlers<'_, '_> {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 let mut s = f.debug_struct("FatHandlers");
118 s.field(
119 "connection",
120 &if self.connection.is_some() {
121 "set"
122 } else {
123 "unset"
124 },
125 );
126 s.field("timed", &format!("{} handlers", self.timed.count()));
127 s.field("stanza", &format!("{} handlers", self.stanza.count()));
128 #[cfg(feature = "libstrophe-0_11_0")]
129 s.field(
130 "cert_fail_handler_id",
131 &if self.connection.is_some() {
132 "set"
133 } else {
134 "unset"
135 },
136 );
137 #[cfg(feature = "libstrophe-0_12_0")]
138 s.field(
139 "sockopt_handler_id",
140 &if self.connection.is_some() {
141 "set"
142 } else {
143 "unset"
144 },
145 );
146 #[cfg(feature = "libstrophe-0_12_0")]
147 s.field(
148 "password",
149 &if self.password.is_some() {
150 "handler set"
151 } else {
152 "no handler"
153 },
154 );
155 #[cfg(feature = "libstrophe-0_14")]
156 s.field(
157 "sm_state",
158 &if self.sm_state.is_some() {
159 "handler set"
160 } else {
161 "no handler"
162 },
163 );
164 s.finish()
165 }
166}
167
168pub struct FatHandler<'cb, 'cx, CB: ?Sized, Extra> {
169 pub handlers: Weak<RefCell<ConnectionHandlers<'cb, 'cx>>>,
171 pub handler: Box<CB>,
173 pub cb_addr: CbAddr,
175 pub extra: Extra,
177}
178
179impl<CB: ?Sized, Extra> FatHandler<'_, '_, CB, Extra> {
180 pub fn as_userdata(&mut self) -> *mut c_void {
181 as_void_ptr(self)
182 }
183
184 pub fn cb_addr(&self) -> CbAddr {
185 self.cb_addr
186 }
187
188 pub unsafe fn from_userdata<'h>(userdata: *mut c_void) -> &'h mut Self {
189 unsafe { void_ptr_as(userdata) }
190 }
191}
192
193pub enum HandlerCb {}
194pub type CbAddr = *const HandlerCb;
195
196pub struct BoxedHandler<'cb, 'cx, CB: ?Sized, Extra> {
197 handler: Box<FatHandler<'cb, 'cx, CB, Extra>>,
198}
199
200impl<'cb, 'cx, CB: ?Sized, Extra> BoxedHandler<'cb, 'cx, CB, Extra> {
201 pub fn new(handler: FatHandler<'cb, 'cx, CB, Extra>) -> Self {
202 Self {
203 handler: Box::new(handler),
204 }
205 }
206
207 pub fn make(handlers: &Rc<RefCell<ConnectionHandlers<'cb, 'cx>>>, handler: Box<CB>, cb_addr: CbAddr, extra: Extra) -> Self {
208 Self::new(FatHandler {
209 handlers: Rc::downgrade(handlers),
210 handler,
211 cb_addr,
212 extra,
213 })
214 }
215
216 fn cb_addr(&self) -> CbAddr {
217 self.handler.cb_addr
218 }
219
220 fn as_ref(&self) -> &FatHandler<'cb, 'cx, CB, Extra> {
221 self.handler.as_ref()
222 }
223
224 fn as_mut(&mut self) -> &mut FatHandler<'cb, 'cx, CB, Extra> {
225 self.handler.as_mut()
226 }
227}
228
229pub trait MaybeBoxedHandler<'cb, 'cx, CB: ?Sized, Extra> {
230 fn set_handler(
231 &mut self,
232 handler: BoxedHandler<'cb, 'cx, CB, Extra>,
233 ) -> (
234 Option<BoxedHandler<'cb, 'cx, CB, Extra>>,
235 &mut FatHandler<'cb, 'cx, CB, Extra>,
236 );
237}
238
239impl<'cb, 'cx, CB: ?Sized, Extra> MaybeBoxedHandler<'cb, 'cx, CB, Extra> for Option<BoxedHandler<'cb, 'cx, CB, Extra>> {
240 fn set_handler(
241 &mut self,
242 handler: BoxedHandler<'cb, 'cx, CB, Extra>,
243 ) -> (
244 Option<BoxedHandler<'cb, 'cx, CB, Extra>>,
245 &mut FatHandler<'cb, 'cx, CB, Extra>,
246 ) {
247 let old_handler = self.take();
248 let self_ref = self.insert(handler).handler.as_mut();
249 (old_handler, self_ref)
250 }
251}
252
253pub struct BoxedHandlers<'cb, 'cx, CB: ?Sized, Extra> {
254 handlers: Vec<BoxedHandler<'cb, 'cx, CB, Extra>>,
255}
256
257impl<'cb, 'cx, CB: ?Sized, Extra> BoxedHandlers<'cb, 'cx, CB, Extra> {
258 pub fn new(capacity: usize) -> Self {
259 Self {
260 handlers: Vec::with_capacity(capacity),
261 }
262 }
263
264 pub fn count(&self) -> usize {
265 self.handlers.len()
266 }
267
268 pub fn retain(&mut self, mut cb: impl FnMut(&FatHandler<'cb, 'cx, CB, Extra>) -> bool) {
269 self.handlers.retain(|boxed| cb(boxed.as_ref()));
270 self.handlers.shrink_to_fit();
271 }
272
273 pub fn store(&mut self, handler: BoxedHandler<'cb, 'cx, CB, Extra>) -> Option<&mut FatHandler<'cb, 'cx, CB, Extra>> {
274 if !self
275 .handlers
276 .iter()
277 .any(|existing| ptr::eq(handler.cb_addr(), existing.cb_addr()))
278 {
279 self.handlers.push(handler);
280 Some(self.handlers.last_mut().expect("Impossible, just pushed it").as_mut())
281 } else {
282 None
283 }
284 }
285
286 pub fn validate(&self, cb_addr: CbAddr) -> Option<&FatHandler<'cb, 'cx, CB, Extra>> {
287 self
288 .handlers
289 .iter()
290 .find_map(|boxed| ptr::eq(cb_addr, boxed.cb_addr()).then_some(boxed.as_ref()))
291 }
292
293 pub fn drop_handler(&mut self, cb_addr: CbAddr) -> usize {
294 let mut removed_count = 0;
295 self.handlers.retain(|handler| {
296 let keep_it = !ptr::eq(cb_addr, handler.cb_addr());
297 if !keep_it {
298 removed_count += 1;
299 }
300 keep_it
301 });
302 removed_count
303 }
304}
305
306macro_rules! ensure_unique {
314 ($typ: ty, $conn_ptr: ident, $userdata: ident, $($args: expr),*) => {
315 if $conn_ptr.cast::<::core::ffi::c_void>() == $userdata { unsafe { $crate::void_ptr_as::<$typ>($userdata)($($args),*) };
317 }
318 };
319}
320
321#[cfg(feature = "libstrophe-0_11_0")]
322pub unsafe extern "C" fn certfail_handler_cb<CB: 'static>(cert: *const sys::xmpp_tlscert_t, errormsg: *const c_char) -> c_int {
323 if let Ok(handlers) = CERT_FAIL_HANDLERS.read() {
324 if let Some(handler) = handlers.get(&TypeId::of::<CB>()) {
325 let cert = unsafe { crate::TlsCert::from_ref(cert) };
326 let error_msg = unsafe { crate::FFI(errormsg).receive() }.unwrap_or("Can't process libstrophe error");
327 return handler(&cert, error_msg) as c_int;
328 }
329 }
330 CertFailResult::TerminateConnection as c_int
331}
332
333#[cfg(feature = "libstrophe-0_12_0")]
334pub unsafe extern "C" fn sockopt_callback<CB: 'static>(_conn: *mut sys::xmpp_conn_t, sock: *mut c_void) -> c_int {
335 if let Ok(handlers) = SOCKOPT_HANDLERS.read() {
336 if let Some(handler) = handlers.get(&TypeId::of::<CB>()) {
337 return handler(sock) as c_int;
338 }
339 }
340 SockoptResult::Error as c_int
341}
342
343#[cfg(feature = "libstrophe-0_12_0")]
344pub unsafe extern "C" fn password_handler_cb(
345 pw: *mut c_char,
346 pw_max: usize,
347 conn_ptr: *mut sys::xmpp_conn_t,
348 userdata: *mut c_void,
349) -> c_int {
350 let password_handler = unsafe { PasswordFatHandler::from_userdata(userdata) };
351 if let Some(fat_handlers) = password_handler.handlers.upgrade() {
352 let conn = unsafe { Connection::from_ref_mut(conn_ptr, fat_handlers) };
353 let max_password_len = pw_max.saturating_sub(1);
355 let result = (password_handler.handler)(&conn, max_password_len);
356 if let Some(password) = result {
357 if let Ok(password) = CString::new(password) {
358 if password.as_bytes().len() <= max_password_len {
359 let pass_len = password.as_bytes().len();
360 unsafe { ptr::copy_nonoverlapping(password.as_ptr(), pw, pass_len) };
361 return c_int::try_from(pass_len).unwrap_or(c_int::MAX);
362 }
363 }
364 }
365 }
366 -1
367}
368
369#[cfg(feature = "libstrophe-0_14")]
370pub unsafe extern "C" fn sm_state_handler_cb(
371 conn_ptr: *mut sys::xmpp_conn_t,
372 userdata: *mut c_void,
373 buf: *const std::ffi::c_uchar,
374 size: usize,
375) {
376 let buf = if buf.is_null() {
377 &[]
378 } else {
379 unsafe { slice::from_raw_parts(buf, size) }
380 };
381 let serialized = SerializedSmStateRef { buf };
382 let sm_state_handler = unsafe { SmStateFatHandler::from_userdata(userdata) };
383 if let Some(fat_handlers) = sm_state_handler.handlers.upgrade() {
384 let mut conn = unsafe { Connection::from_ref_mut(conn_ptr, fat_handlers) };
385 (sm_state_handler.handler)(&mut conn, serialized);
386 }
387}