libstrophe/lib.rs
1//! # libstrophe - ergonomic wrapper for Rust
2//!
3//! This library provides high level ergonomic bindings for [libstrophe], an XMPP client library.
4//!
5//!
6//! # Documentation
7//!
8//! The documentation for this library covers only Rust specific bits and refers to [original
9//! library documentation][docs] in most other cases.
10//!
11//!
12//! # Workflow
13//!
14//! The general workflow is quite similar to what you get with the C library. The topmost object is
15//! [Context]. It contains platform-specific bits like logging and memory allocation. Plus an event
16//! loop used to keep things going. This crate wraps logging with the facilities provided by [`log`]
17//! crate (provided the default `rust-log` feature is enabled). Memory allocation is also handled by
18//! Rust native means. When a [Connection] is created it will temporarily consume the [Context].
19//! After all the setup is done, call one of the `connect_*()` methods to retrieve the [Context]
20//! back. In this manner a single [Context] can be used for multiple [Connection]s consequently.
21//! When you're done with setting up [Connection]s for the [Context], use `run()` or `run_once()`
22//! methods to start the event loop rolling.
23//!
24//!
25//! # Safety
26//!
27//! This crate tries to be as safe as possible. Yet it's not always possible to guarantee that when
28//! wrapping a C library. The following assumptions are made which might not necessarily be true and
29//! thus might introduce unsafety:
30//!
31//! * [Context] event loop methods are borrowing `self` immutably considering it immutable (or
32//! more specifically having interior mutability)
33//!
34//! The main objects in this crate are marked as `Send` and it should be indeed be safe to send them
35//! between threads. Yet, no major investigation of the library source code has been performed to
36//! ensure that this is true.
37//!
38//!
39//! # Initialization and shutdown
40//!
41//! You don't need to call the initialization function, it's done automatically when creating a
42//! [Context]. Yet you might want to call the [shutdown()] function when your application
43//! terminates. Be aware though that the initialization can be called only once in the program
44//! lifetime so you won't be able to use the library properly after you called [shutdown()].
45//!
46//!
47//! # Callbacks
48//!
49//! The crate has the ability to store callbacks taking ownership of them so you can pass closures
50//! and not care about storing them externally. There are some things to note about it though. It's
51//! not always possible to know whether the underlying library accepted the callback. The crate will
52//! keep the closure internally in either case, though it may not ever be called by the library. You
53//! can still remove the callback with the corresponding `*handler_delete()` or `*handler_clear()`
54//! method.
55//!
56//! Due to the way the C libstrophe library is implemented and how Rust optimizes monomorphization,
57//! your callbacks must actually be compiled to different function with separate addresses when you
58//! pass them to the same handler setup method. So if you want to pass 2 callbacks `hander_add`
59//! ensure that their code is unique and rust didn't merge them into a single function behind the
60//! scenes. You can test whether 2 callbacks are same or not with the `Connection::*handlers_same()`
61//! family of functions. If it returns true then you will only be able to pass one of them to the
62//! corresponding handler function, the other will be silently ignored.
63//!
64//! Due to the fact that the crate uses `userdata` to pass the actual user callback, it's not possible
65//! to use `userdata` inside the callbacks for your own data. So if you need to have a state between
66//! callback invocations you must use closures.
67//!
68//! Because the main objects are marked as `Send` and we store callbacks inside them, all callbacks
69//! must also be `Send`.
70//!
71//!
72//! # Examples
73//! ```
74//! let connection_handler = |ctx: &libstrophe::Context,
75//! _conn: &mut libstrophe::Connection,
76//! _evt: libstrophe::ConnectionEvent| {
77//! ctx.stop();
78//! };
79//!
80//! let ctx = libstrophe::Context::new_with_default_logger();
81//! let mut conn = libstrophe::Connection::new(ctx);
82//! conn.set_jid("example@127.0.0.1");
83//! conn.set_pass("password");
84//! let mut ctx = conn.connect_client(None, None, connection_handler).unwrap();
85//! ctx.run();
86//! libstrophe::shutdown();
87//! ```
88//!
89//! For more complete examples see this crate `examples` directory and [libstrophe examples].
90//!
91//!
92//! # Crate features
93//!
94//! The following features are provided:
95//!
96//! * `rust-log` - enabled by default, makes the crate integrate into Rust logging facilities
97//! * `libstrophe-0_9_3` - enabled by default, enables functions specific to libstrophe-0.9.3
98//! * `libstrophe-0_10_0` - enabled by default, enables functions specific to libstrophe-0.10
99//! * `libstrophe-0_11_0` - enabled by default, enables functions specific to libstrophe-0.11
100//! * `libstrophe-0_12_0` - enabled by default, enables functions specific to libstrophe-0.12
101//! * `libstrophe-0_13` - enabled by default, enables functions specific to libstrophe-0.13
102//! * `libstrophe-0_14` - enabled by default, enables functions specific to libstrophe-0.14
103//! * `buildtime_bindgen` - forces regeneration of the bindings instead of relying on the
104//! pre-generated sources
105//!
106//! [libstrophe]: https://strophe.im/libstrophe/
107//! [`log`]: https://crates.io/crates/log
108//! [docs]: https://strophe.im/libstrophe/doc/0.13.0/
109//! [libstrophe examples]: https://github.com/strophe/libstrophe/tree/0.12.2/examples
110
111use core::ffi::{c_long, c_void};
112use std::sync::Once;
113
114pub use alloc_context::AllocContext;
115use bitflags::bitflags;
116#[cfg(feature = "libstrophe-0_11_0")]
117pub use connection::CertFailResult;
118#[cfg(feature = "libstrophe-0_12_0")]
119pub use connection::SockoptResult;
120pub use connection::{Connection, ConnectionEvent, HandlerId, HandlerResult, IdHandlerId, TimedHandlerId};
121pub use context::Context;
122pub use error::{
123 ConnectClientError, ConnectionError, Error, OwnedConnectionError, OwnedStreamError, Result, StreamError, ToTextError,
124};
125use ffi_types::FFI;
126pub use logger::Logger;
127use once_cell::sync::Lazy;
128#[cfg(feature = "libstrophe-0_12_0")]
129pub use sm_state::SMState;
130#[cfg(feature = "libstrophe-0_14")]
131pub use sm_state::{SerializedSmState, SerializedSmStateRef};
132pub use stanza::{Stanza, StanzaMutRef, StanzaRef, XMPP_STANZA_NAME_IN_NS};
133#[cfg(feature = "libstrophe-0_11_0")]
134pub use sys::xmpp_cert_element_t as CertElement;
135#[cfg(feature = "libstrophe-0_9_3")]
136pub use sys::xmpp_error_type_t as ErrorType;
137pub use sys::xmpp_log_level_t as LogLevel;
138#[cfg(feature = "libstrophe-0_12_0")]
139pub use sys::xmpp_queue_element_t as QueueElement;
140#[cfg(feature = "libstrophe-0_11_0")]
141pub use tls_cert::TlsCert;
142
143mod alloc_context;
144mod connection;
145mod context;
146mod error;
147mod ffi_types;
148pub mod jid;
149mod logger;
150#[cfg(feature = "libstrophe-0_12_0")]
151mod sm_state;
152mod stanza;
153#[cfg(feature = "libstrophe-0_11_0")]
154mod tls_cert;
155
156bitflags! {
157 pub struct ConnectionFlags: c_long {
158 const DISABLE_TLS = sys::XMPP_CONN_FLAG_DISABLE_TLS;
159 const MANDATORY_TLS = sys::XMPP_CONN_FLAG_MANDATORY_TLS;
160 const LEGACY_SSL = sys::XMPP_CONN_FLAG_LEGACY_SSL;
161 const TRUST_TLS = sys::XMPP_CONN_FLAG_TRUST_TLS;
162 #[cfg(feature = "libstrophe-0_9_3")]
163 const LEGACY_AUTH = sys::XMPP_CONN_FLAG_LEGACY_AUTH;
164 #[cfg(feature = "libstrophe-0_12_0")]
165 const DISABLE_SM = sys::XMPP_CONN_FLAG_DISABLE_SM;
166 #[cfg(feature = "libstrophe-0_13")]
167 const ENABLE_COMPRESSION = sys::XMPP_CONN_FLAG_ENABLE_COMPRESSION;
168 #[cfg(feature = "libstrophe-0_13")]
169 const COMPRESSION_DONT_RESET = sys::XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
170 }
171}
172
173static ALLOC_CONTEXT: Lazy<AllocContext> = Lazy::new(AllocContext::default);
174
175/// Convert type to `void*` for passing as `userdata`
176fn as_void_ptr<T>(cb: &mut T) -> *mut c_void {
177 (cb as *mut T).cast::<c_void>()
178}
179
180/// Convert `void*` from `userdata` to the appropriate type
181unsafe fn void_ptr_as<'cb, T>(ptr: *mut c_void) -> &'cb mut T {
182 unsafe { ptr.cast::<T>().as_mut() }.expect("userdata must be non-null")
183}
184
185/// Ensure that underlying C library is initialized
186///
187/// Must be called from every possible crate usage entry point.
188fn init() {
189 static INIT: Once = Once::new();
190 INIT.call_once(|| unsafe {
191 sys::xmpp_initialize();
192 });
193}
194
195fn deinit() {
196 static DEINIT: Once = Once::new();
197 DEINIT.call_once(|| unsafe { sys::xmpp_shutdown() });
198}
199
200/// [xmpp_version_check](https://strophe.im/libstrophe/doc/0.13.0/group___init.html#ga6cc7afca422acce51e0e7f52424f1db3)
201pub fn version_check(major: i32, minor: i32) -> bool {
202 unsafe { FFI(sys::xmpp_version_check(major, minor)).receive_bool() }
203}
204
205/// [xmpp_shutdown](https://strophe.im/libstrophe/doc/0.13.0/group___init.html#ga06e07524aee531de1ceb825541307963)
206///
207/// Call this function when your application terminates, but be aware that you can't use the library
208/// after you called `shutdown()` and there is now a way to reinitialize it again.
209///
210/// This function is thread safe, it's safe to call it several times, and it's safe to call it before
211/// doing any initialization.
212pub fn shutdown() {
213 init();
214 deinit();
215}