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}