mosquitto_plugin/
mosquitto_calls.rs

1use crate::mosquitto_dev::{
2    mosquitto_broker_publish, mosquitto_kick_client_by_clientid, mosquitto_kick_client_by_username,
3    mosquitto_log_printf, mosquitto_property,
4};
5use crate::Error;
6use crate::{Success, QOS};
7use libc::c_void;
8use std::ffi::CString;
9use std::os::raw::c_char;
10use std::ptr::null;
11
12/// Broadcast a message from the broker
13/// If called in a username and password check the connecting client will not get the message
14/// Use the publish_to_client combined with this if you want to send to all clients including the one that is connecting
15pub fn publish_broadcast(
16    topic: &str,
17    payload: &[u8],
18    qos: QOS,
19    retain: bool,
20) -> Result<Success, Error> {
21    let cstr = &CString::new(topic).expect("no cstring for u");
22    let bytes = cstr.as_bytes_with_nul();
23    let topic = bytes.as_ptr();
24
25    let nullptr: *const c_void = null();
26    let properties: *mut mosquitto_property = std::ptr::null_mut();
27
28    // let payload: *mut c_void = std::ptr::null_mut(); // payload bytes, non-null if payload length > 0, must be heap allocated
29    let payload_len = payload.len();
30    let payload: *const c_void = Box::new(payload).as_ptr() as *const c_void; // payload bytes, non-null if payload length > 0, must be heap allocated
31
32    unsafe {
33        let c_payload: *mut c_void =
34            libc::malloc(std::mem::size_of::<u8>() * payload_len) as *mut c_void;
35        payload.copy_to(c_payload, payload_len);
36        /*
37         * https://mosquitto.org/api2/files/mosquitto_broker-h.html#mosquitto_broker_publish
38         * maybe want to switch to mosquitto_broker_publish to maintain ownership over
39         * payload memory.
40         * payload: payload bytes.  If payloadlen > 0 this must not be NULL.  Must be allocated on the heap.  Will be freed by mosquitto after use if the function returns success."
41         * What happens if it is not successfull? Do i need to free the memory myself? This is a leak if if i dont' free memory  in all cases except 0 (Success) below?
42         */
43        let res = mosquitto_broker_publish(
44            nullptr as *const c_char, // client id to send to, null = all clients
45            topic as *const c_char,
46            payload_len as i32, // payload length in bytes, 0 for empty payload
47            c_payload, // payload bytes, non-null if payload length > 0, must be heap allocated
48            qos.to_i32(), // qos
49            retain,    // retain
50            properties, //mqtt5 properties
51        );
52        match res {
53            0 => Ok(Success),
54            1 => Err(Error::NoMem),
55            3 => Err(Error::Inval),
56            _default => Err(Error::Unknown),
57        }
58    }
59}
60
61/// To be called from implementations of the plugin when
62/// a plugin wants to publish to a specific client.
63pub fn publish_to_client(
64    client_id: &str,
65    topic: &str,
66    payload: &[u8],
67    qos: QOS,
68    retain: bool,
69) -> Result<Success, Error> {
70    let cstr = &CString::new(client_id).expect("no cstring for u");
71    let bytes = cstr.as_bytes_with_nul();
72    let client_id = bytes.as_ptr();
73
74    let cstr = &CString::new(topic).expect("no cstring for u");
75    let bytes = cstr.as_bytes_with_nul();
76    let topic = bytes.as_ptr();
77
78    let payload_len = payload.len();
79    let payload: *const c_void = Box::new(payload).as_ptr() as *const c_void;
80
81    unsafe {
82        let c_payload: *mut c_void =
83            libc::malloc(std::mem::size_of::<u8>() * payload_len) as *mut c_void;
84        payload.copy_to(c_payload, payload_len);
85
86        let res = mosquitto_broker_publish(
87            client_id as *const c_char, // client id to send to, null = all clients
88            topic as *const c_char,     // topic to publish on
89            payload_len as i32,         // payload length in bytes, 0 for empty payload
90            c_payload, // payload bytes, non-null if payload length > 0, must be heap allocated
91            qos.to_i32(), // qos
92            retain,    // retain
93            std::ptr::null_mut(), //mqtt5 properties
94        );
95        match res {
96            0 => Ok(Success),
97            1 => Err(Error::NoMem),
98            3 => Err(Error::Inval),
99            _default => Err(Error::Unknown),
100        }
101    }
102}
103
104/// Forcefully disconnect all clients from the broker.
105///
106/// If `with_will` is true, then if the client has a Last Will and Testament
107/// defined then this will be sent. If false, the LWT will not be sent.
108pub fn kick_all_clients(with_will: bool) -> Result<Success, Error> {
109    match unsafe { mosquitto_kick_client_by_clientid(null(), with_will) } {
110        0 => Ok(Success),
111        error => Err(Error::from(error)),
112    }
113}
114
115/// Forcefully disconnect the client matching `client_id` from the broker.
116///
117/// If `with_will` is true, then if the client has a Last Will and Testament
118/// defined then this will be sent. If false, the LWT will not be sent.
119pub fn kick_client_by_clientid(client_id: &str, with_will: bool) -> Result<Success, Error> {
120    let client_id = CString::new(client_id).map_err(|_| Error::Inval)?;
121    match unsafe { mosquitto_kick_client_by_clientid(client_id.as_ptr(), with_will) } {
122        0 => Ok(Success),
123        error => Err(Error::from(error)),
124    }
125}
126
127/// Forcefully disconnect the client connected with username `username` from the broker.
128///
129/// If `with_will` is true, then if the client has a Last Will and Testament
130/// defined then this will be sent. If false, the LWT will not be sent.
131pub fn kick_client_by_username(username: &str, with_will: bool) -> Result<Success, Error> {
132    let username = CString::new(username).map_err(|_| Error::Inval)?;
133    match unsafe { mosquitto_kick_client_by_username(username.as_ptr(), with_will) } {
134        0 => Ok(Success),
135        error => Err(Error::from(error)),
136    }
137}
138
139/// Mosquitto log level.
140#[repr(C)]
141pub enum LogLevel {
142    /// The "info" level.
143    ///
144    /// Designates useful information.
145    Info = 1 << 0,
146    /// The "notice" level.
147    ///
148    /// Designates medium priority information.
149    Notice = 1 << 1,
150    /// The "warning" level.
151    ///
152    /// Designates hazardous situations.
153    Warning = 1 << 2,
154    /// The "error" level.
155    ///
156    /// Designates very serious errors.
157    Err = 1 << 3,
158    /// The "debug" level.
159    ///
160    /// Designates lower priority information.
161    Debug = 1 << 4,
162}
163
164/// Send a log message on `level` to the mosquitto logging subsystem.
165pub fn mosquitto_log(level: LogLevel, message: &str) {
166    let message = CString::new(message).expect("invalid log message: contains a nul byte");
167    unsafe {
168        mosquitto_log_printf(
169            level as i32,
170            "%s\0".as_ptr() as *const c_char,
171            message.as_ptr(),
172        )
173    }
174}
175
176/// Logs a message at the debug level into the mosquitto logging subsystem.
177///
178/// # Examples
179///
180/// ```no_run
181/// use mosquitto_plugin::mosquitto_debug;
182///
183/// # fn main() {
184/// let client_id = "unknown";
185/// mosquitto_debug!("Authenticating client id {}", client_id);
186/// # }
187/// ```
188#[macro_export]
189macro_rules! mosquitto_debug {
190    // mosquitto_debug!("a {} event", "log")
191    ($($arg:tt)+) => ($crate::mosquitto_calls::mosquitto_log($crate::mosquitto_calls::LogLevel::Debug, &format!($($arg)+)))
192}
193
194/// Logs a message at the info level into the mosquitto logging subsystem.
195///
196/// # Examples
197///
198/// ```no_run
199/// use mosquitto_plugin::mosquitto_info;
200///
201/// # fn main() {
202/// let client_id = "unknown";
203/// mosquitto_info!("Authentication of client id {} successful", client_id);
204/// # }
205/// ```
206#[macro_export]
207macro_rules! mosquitto_info {
208    ($($arg:tt)+) => ($crate::mosquitto_calls::mosquitto_log($crate::mosquitto_calls::LogLevel::Info, &format!($($arg)+)))
209}
210
211/// Logs a message at the notice level into the mosquitto logging subsystem.
212///
213/// # Examples
214///
215/// ```no_run
216/// use mosquitto_plugin::mosquitto_notice;
217///
218/// # fn main() {
219/// let client_id = "unknown";
220/// mosquitto_notice!("Authentication of client id {} is pending", client_id);
221/// # }
222/// ```
223#[macro_export]
224macro_rules! mosquitto_notice {
225    ($($arg:tt)+) => ($crate::mosquitto_calls::mosquitto_log($crate::mosquitto_calls::LogLevel::Notice, &format!($($arg)+)))
226}
227
228/// Logs a message at the warn level into the mosquitto logging subsystem.
229///
230/// # Examples
231///
232/// ```no_run
233/// use mosquitto_plugin::mosquitto_warn;
234///
235/// # fn main() {
236/// let auth_method = "SIP";
237/// let client_id = "unknown";
238/// mosquitto_warn!("Client id {} tries unspported authentification method {}", client_id, auth_method);
239/// # }
240/// ```
241#[macro_export]
242macro_rules! mosquitto_warn {
243    // mosquitto_warn!("a {} event", "log")
244    ($($arg:tt)+) => ($crate::mosquitto_calls::mosquitto_log($crate::mosquitto_calls::LogLevel::Warning, &format!($($arg)+)))
245}
246
247/// Logs a message at the error level into the mosquitto logging subsystem.
248///
249/// # Examples
250///
251/// ```no_run
252/// use mosquitto_plugin::mosquitto_error;
253///
254/// # fn main() {
255/// let client_id: Option<&str> = None;
256/// mosquitto_error!("Failed acl check for client id {:?}", client_id);
257/// # }
258/// ```
259#[macro_export]
260macro_rules! mosquitto_error {
261    // mosquitto_error!("a {} event", "log")
262    ($($arg:tt)+) => ($crate::mosquitto_calls::mosquitto_log($crate::mosquitto_calls::LogLevel::Err, &format!($($arg)+)))
263}