#[macro_use]
extern crate bitflags;
extern crate milter_sys as sys;
#[doc(hidden)]
pub use sys::{SMFICTX, sfsistat};
#[allow(unused_imports)]
#[macro_use]
extern crate milter_callback;
#[doc(hidden)]
pub use milter_callback::*;
use libc::{c_char, c_uchar, c_ulong, size_t};
use std::cell::{Ref, RefCell, RefMut};
use std::ffi::{CStr, CString};
use std::ptr::{self, NonNull};
use std::time::Duration;
use std::convert::TryInto;
pub mod error;
pub use error::*;
bitflags! {
#[derive(Default)]
pub struct Actions: u64 {
const SET_REQUESTED_MACROS = sys::SMFIF_SETSYMLIST;
const CHANGE_SENDER = sys::SMFIF_CHGFROM;
const ADD_RECIPIENT = sys::SMFIF_ADDRCPT;
const ADD_RECIPIENT_EXT = sys::SMFIF_ADDRCPT_PAR;
const DELETE_RECIPIENT = sys::SMFIF_DELRCPT;
const ADD_HEADER = sys::SMFIF_ADDHDRS;
const CHANGE_HEADER = sys::SMFIF_CHGHDRS;
const CHANGE_BODY = sys::SMFIF_CHGBODY;
const QUARANTINE = sys::SMFIF_QUARANTINE;
}
}
bitflags! {
#[derive(Default)]
pub struct ProtocolOpts: u64 {
const NO_CONNECT = sys::SMFIP_NOCONNECT;
const NO_HELO = sys::SMFIP_NOHELO;
const NO_MAIL = sys::SMFIP_NOMAIL;
const NO_RCPT = sys::SMFIP_NORCPT;
const NO_DATA = sys::SMFIP_NODATA;
const NO_HEADER = sys::SMFIP_NOHDRS;
const NO_EOH = sys::SMFIP_NOEOH;
const NO_BODY = sys::SMFIP_NOBODY;
const NO_UNKNOWN = sys::SMFIP_NOUNKNOWN;
const SKIP = sys::SMFIP_SKIP;
const REJECTED_RCPT = sys::SMFIP_RCPT_REJ;
const NOREPLY_CONNECT = sys::SMFIP_NR_CONN;
const NOREPLY_HELO = sys::SMFIP_NR_HELO;
const NOREPLY_MAIL = sys::SMFIP_NR_MAIL;
const NOREPLY_RCPT = sys::SMFIP_NR_RCPT;
const NOREPLY_DATA = sys::SMFIP_NR_DATA;
const NOREPLY_HEADER = sys::SMFIP_NR_HDR;
const NOREPLY_EOH = sys::SMFIP_NR_EOH;
const NOREPLY_BODY = sys::SMFIP_NR_BODY;
const NOREPLY_UNKNOWN = sys::SMFIP_NR_UNKN;
const HEADER_LEADING_SPACE = sys::SMFIP_HDR_LEADSPC;
const MAX_DATA_SIZE_256K = sys::SMFIP_MDS_256K;
const MAX_DATA_SIZE_1M = sys::SMFIP_MDS_1M;
}
}
pub enum Stage {
Connect = 0,
Helo = 1,
Mail = 2,
Rcpt = 3,
Data = 4,
Eoh = 6,
Eom = 5,
}
pub enum Status {
Continue = 0,
Reject = 1,
Discard = 2,
Accept = 3,
Tempfail = 4,
Noreply = 7,
Skip = 8,
AllOpts = 10,
}
pub type NegotiateCallback = unsafe extern "C" fn(
*mut sys::SMFICTX, c_ulong, c_ulong, c_ulong, c_ulong, *mut c_ulong, *mut c_ulong, *mut c_ulong, *mut c_ulong
) -> sys::sfsistat;
pub type ConnectCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *mut c_char, *mut libc::sockaddr) -> sys::sfsistat;
pub type HeloCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *mut c_char) -> sys::sfsistat;
pub type MailCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *mut *mut c_char) -> sys::sfsistat;
pub type RcptCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *mut *mut c_char) -> sys::sfsistat;
pub type DataCallback = unsafe extern "C" fn(*mut sys::SMFICTX) -> sys::sfsistat;
pub type HeaderCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *mut c_char, *mut c_char) -> sys::sfsistat;
pub type EohCallback = unsafe extern "C" fn(*mut sys::SMFICTX) -> sys::sfsistat;
pub type BodyCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *mut c_uchar, size_t) -> sys::sfsistat;
pub type EomCallback = unsafe extern "C" fn(*mut sys::SMFICTX) -> sys::sfsistat;
pub type AbortCallback = unsafe extern "C" fn(*mut sys::SMFICTX) -> sys::sfsistat;
pub type CloseCallback = unsafe extern "C" fn(*mut sys::SMFICTX) -> sys::sfsistat;
pub type UnknownCallback = unsafe extern "C" fn(*mut sys::SMFICTX, *const c_char) -> sys::sfsistat;
pub struct Milter {
name: String,
actions: Actions,
timeout: Option<u64>,
backlog: Option<u32>,
negotiate_callback: Option<NegotiateCallback>,
connect_callback: Option<ConnectCallback>,
helo_callback: Option<HeloCallback>,
mail_callback: Option<MailCallback>,
rcpt_callback: Option<RcptCallback>,
data_callback: Option<DataCallback>,
header_callback: Option<HeaderCallback>,
eoh_callback: Option<EohCallback>,
body_callback: Option<BodyCallback>,
eom_callback: Option<EomCallback>,
abort_callback: Option<AbortCallback>,
close_callback: Option<CloseCallback>,
unknown_callback: Option<UnknownCallback>,
}
impl Milter {
pub fn new(name: &str) -> Self {
Milter {
name: name.to_owned(),
actions: Default::default(),
timeout: None,
backlog: None,
negotiate_callback: None,
connect_callback: None,
helo_callback: None,
mail_callback: None,
rcpt_callback: None,
data_callback: None,
header_callback: None,
eoh_callback: None,
body_callback: None,
eom_callback: None,
abort_callback: None,
close_callback: None,
unknown_callback: None,
}
}
pub fn on_negotiate(&mut self, cb: NegotiateCallback) -> &mut Self {
self.negotiate_callback = Some(cb);
self
}
pub fn on_connect(&mut self, cb: ConnectCallback) -> &mut Self {
self.connect_callback = Some(cb);
self
}
pub fn on_helo(&mut self, cb: HeloCallback) -> &mut Self {
self.helo_callback = Some(cb);
self
}
pub fn on_mail(&mut self, cb: MailCallback) -> &mut Self {
self.mail_callback = Some(cb);
self
}
pub fn on_rcpt(&mut self, cb: RcptCallback) -> &mut Self {
self.rcpt_callback = Some(cb);
self
}
pub fn on_data(&mut self, cb: DataCallback) -> &mut Self {
self.data_callback = Some(cb);
self
}
pub fn on_header(&mut self, cb: HeaderCallback) -> &mut Self {
self.header_callback = Some(cb);
self
}
pub fn on_eoh(&mut self, cb: EohCallback) -> &mut Self {
self.eoh_callback = Some(cb);
self
}
pub fn on_body(&mut self, cb: BodyCallback) -> &mut Self {
self.body_callback = Some(cb);
self
}
pub fn on_eom(&mut self, cb: EomCallback) -> &mut Self {
self.eom_callback = Some(cb);
self
}
pub fn on_abort(&mut self, cb: AbortCallback) -> &mut Self {
self.abort_callback = Some(cb);
self
}
pub fn on_close(&mut self, cb: CloseCallback) -> &mut Self {
self.close_callback = Some(cb);
self
}
pub fn on_unknown(&mut self, cb: UnknownCallback) -> &mut Self {
self.unknown_callback = Some(cb);
self
}
pub fn actions(&mut self, actions: Actions) -> &mut Self {
self.actions = actions;
self
}
pub fn set_timeout(&mut self, duration: Duration) -> &mut Self {
let secs = duration.as_secs();
self.timeout = Some(secs);
self
}
pub fn set_socket_backlog(&mut self, conns: u32) -> &mut Self {
assert!(conns > 0);
self.backlog = Some(conns);
self
}
fn register(&self) -> std::result::Result<i32, &'static str> {
let name: &str = &self.name;
let name = CString::new(name).unwrap().into_raw(); let actions = &self.actions;
let smfilter = sys::smfiDesc {
xxfi_name: name,
xxfi_version: sys::SMFI_VERSION,
xxfi_flags: actions.bits,
xxfi_connect: self.connect_callback,
xxfi_helo: self.helo_callback,
xxfi_envfrom: self.mail_callback,
xxfi_envrcpt: self.rcpt_callback,
xxfi_header: self.header_callback,
xxfi_eoh: self.eoh_callback,
xxfi_body: self.body_callback,
xxfi_eom: self.eom_callback,
xxfi_abort: self.abort_callback,
xxfi_close: self.close_callback,
xxfi_unknown: self.unknown_callback,
xxfi_data: self.data_callback,
xxfi_negotiate: self.negotiate_callback,
};
let i = unsafe { sys::smfi_register(smfilter) };
if i == sys::MI_FAILURE {
Err("register failed")
} else {
Ok(i)
}
}
pub fn run(&self, socket: &str) -> std::result::Result<i32, &'static str> {
let cs = CString::new(socket).unwrap();
unsafe {
let _ = sys::smfi_setconn(cs.into_raw());
}
let _result = self.register();
if let Some(timeout) = self.timeout {
let _ = unsafe { sys::smfi_settimeout(timeout.try_into().unwrap()) };
}
if let Some(backlog) = self.backlog {
let _ = unsafe { sys::smfi_setbacklog(backlog.try_into().unwrap()) };
}
println!("Starting main");
let status = unsafe { sys::smfi_main() };
println!("Exiting main");
Ok(status)
}
}
pub struct Context<T = ()> {
base: ContextBase,
pub data: DataHandle<T>,
}
impl<T> Context<T> {
pub fn new(ptr: *mut sys::SMFICTX) -> Self {
assert!(!ptr.is_null());
Context {
base: ContextBase::new(ptr),
data: DataHandle::new(ptr),
}
}
pub fn set_requested_macros(&self, stage: Stage, macros: &str) -> Result<()> {
self.base.set_requested_macros(stage, macros)
}
pub fn set_error_reply(&self, code: &str, ext_code: Option<&str>, lines: Vec<&str>) -> Result<()> {
self.base.set_error_reply(code, ext_code, lines)
}
pub fn exit(&self) {
self.base.exit()
}
}
pub trait MacroValue {
fn macro_value(&self, name: &str) -> Result<Option<String>>;
}
impl<T> MacroValue for Context<T> {
fn macro_value(&self, name: &str) -> Result<Option<String>> {
self.base.macro_value(name)
}
}
impl<T> MacroValue for ActionContext<T> {
fn macro_value(&self, name: &str) -> Result<Option<String>> {
self.base.macro_value(name)
}
}
pub struct ActionContext<T = ()> {
base: ContextBase,
pub data: DataHandle<T>,
}
impl<T> ActionContext<T> {
pub fn new(ptr: *mut sys::SMFICTX) -> Self {
assert!(!ptr.is_null());
ActionContext {
base: ContextBase::new(ptr),
data: DataHandle::new(ptr),
}
}
pub fn macro_value(&self, name: &str) -> Result<Option<String>> {
self.base.macro_value(name)
}
pub fn set_error_reply(&self, code: &str, ext_code: Option<&str>, lines: Vec<&str>) -> Result<()> {
self.base.set_error_reply(code, ext_code, lines)
}
pub fn exit(&self) {
self.base.exit()
}
pub fn add_header(&self, name: &str, value: &str) -> Result<()> {
self.base.add_header(name, value)
}
pub fn insert_header(&self, index: usize, name: &str, value: &str) -> Result<()> {
self.base.insert_header(index, name, value)
}
pub fn change_header(&self, name: &str, index: usize, value: Option<&str>) -> Result<()> {
self.base.change_header(name, index, value)
}
pub fn change_sender(&self, mail: &str, esmtp: Option<&str>) -> Result<()> {
self.base.change_sender(mail, esmtp)
}
pub fn add_recipient(&self, rcpt: &str, esmtp: Option<&str>) -> Result<()> {
self.base.add_recipient(rcpt, esmtp)
}
pub fn delete_recipient(&self, rcpt: &str) -> Result<()> {
self.base.delete_recipient(rcpt)
}
pub fn append_to_new_body(&self, content: &[u8]) -> Result<()> {
self.base.append_to_new_body(content)
}
pub fn keepalive(&self) -> Result<()> {
self.base.keepalive()
}
pub fn quarantine(&self, reason: &str) -> Result<()> {
self.base.quarantine(reason)
}
}
struct ContextBase {
context_ptr: NonNull<sys::SMFICTX>,
}
impl ContextBase {
fn new(ptr: *mut sys::SMFICTX) -> Self {
ContextBase { context_ptr: NonNull::new(ptr).unwrap() }
}
fn macro_value(&self, name: &str) -> Result<Option<String>> {
let name = to_c_string(name)?;
unsafe {
let value = sys::smfi_getsymval(self.context_ptr.as_ptr(), name.as_ptr() as _);
Ok(if value.is_null() {
None
} else {
Some(String::from(CStr::from_ptr(value).to_str().map_err(|_| Error::new(ErrorKind::ConversionError))?))
})
}
}
fn set_requested_macros(&self, stage: Stage, macros: &str) -> Result<()> {
let macros = to_c_string(macros)?;
to_result(unsafe {
sys::smfi_setsymlist(self.context_ptr.as_ptr(), stage as _, macros.as_ptr() as _)
})
}
fn set_error_reply(&self, code: &str, ext_code: Option<&str>, lines: Vec<&str>) -> Result<()> {
let code = to_c_string(code)?;
let ext_code = ext_code.map(|s| to_c_string(s)).transpose()?;
let ls = lines.into_iter()
.map(|line| to_c_string(line))
.collect::<Result<Vec<CString>>>()?;
let p = self.context_ptr.as_ptr();
let c = code.as_ptr() as *mut _;
let x = ext_code.as_ref().map_or(ptr::null_mut(), |cs| cs.as_ptr() as *mut _);
macro_rules! reply {
($line:expr) => {
unsafe { sys::smfi_setreply(p, c, x, $line as _) }
};
($($line:expr),+) => {
unsafe {
sys::smfi_setmlreply(
p, c, x, $($line.as_ptr() as *mut c_char),+, ptr::null_mut() as *mut c_char
)
}
}
}
to_result(match ls.len() {
0 => reply!(ptr::null_mut()),
1 => reply!(ls[0].as_ptr()),
2 => reply!(ls[0], ls[1]),
3 => reply!(ls[0], ls[1], ls[2]),
4 => reply!(ls[0], ls[1], ls[2], ls[3]),
5 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4]),
6 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5]),
7 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6]),
8 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7]),
9 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8]),
10 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9]),
11 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10]),
12 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11]),
13 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12]),
14 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13]),
15 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14]),
16 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15]),
17 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16]),
18 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17]),
19 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18]),
20 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19]),
21 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20]),
22 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21]),
23 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22]),
24 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23]),
25 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24]),
26 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25]),
27 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25], ls[26]),
28 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25], ls[26], ls[27]),
29 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25], ls[26], ls[27], ls[28]),
30 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25], ls[26], ls[27], ls[28], ls[29]),
31 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25], ls[26], ls[27], ls[28], ls[29], ls[30]),
32 => reply!(ls[0], ls[1], ls[2], ls[3], ls[4], ls[5], ls[6], ls[7], ls[8], ls[9], ls[10], ls[11], ls[12], ls[13], ls[14], ls[15], ls[16], ls[17], ls[18], ls[19], ls[20], ls[21], ls[22], ls[23], ls[24], ls[25], ls[26], ls[27], ls[28], ls[29], ls[30], ls[31]),
_ => panic!("more than 32 lines"),
})
}
fn exit(&self) {
let _ = unsafe { sys::smfi_stop() };
}
fn change_sender(&self, mail: &str, esmtp: Option<&str>) -> Result<()> {
let mail = to_c_string(mail)?;
let esmtp = esmtp.map(|s| to_c_string(s)).transpose()?;
let e = esmtp.as_ref().map_or(ptr::null_mut(), |cs| cs.as_ptr() as _);
to_result(unsafe {
sys::smfi_chgfrom(self.context_ptr.as_ptr(), mail.as_ptr() as _, e)
})
}
fn add_recipient(&self, rcpt: &str, esmtp: Option<&str>) -> Result<()> {
let rcpt = to_c_string(rcpt)?;
to_result(match esmtp {
None => {
unsafe { sys::smfi_addrcpt(self.context_ptr.as_ptr(), rcpt.as_ptr() as _) }
}
Some(esmtp) => {
let ext = to_c_string(esmtp)?;
unsafe { sys::smfi_addrcpt_par(self.context_ptr.as_ptr(), rcpt.as_ptr() as _, ext.as_ptr() as _) }
}
})
}
fn delete_recipient(&self, rcpt: &str) -> Result<()> {
let rcpt = to_c_string(rcpt)?;
to_result(unsafe { sys::smfi_delrcpt(self.context_ptr.as_ptr(), rcpt.as_ptr() as _) })
}
fn add_header(&self, name: &str, value: &str) -> Result<()> {
let name = to_c_string(name)?;
let value = to_c_string(value)?;
unsafe {
to_result(sys::smfi_addheader(self.context_ptr.as_ptr(), name.as_ptr() as _, value.as_ptr() as _))
}
}
fn insert_header(&self, index: usize, name: &str, value: &str) -> Result<()> {
let name = to_c_string(name)?;
let value = to_c_string(value)?;
let index = index.try_into().unwrap(); unsafe {
to_result(sys::smfi_insheader(
self.context_ptr.as_ptr(),
index,
name.as_ptr() as _,
value.as_ptr() as _,
))
}
}
fn change_header(&self, name: &str, index: usize, value: Option<&str>) -> Result<()> {
let name = to_c_string(name)?;
let value = value.map(|s| to_c_string(s)).transpose()?;
let index = index.try_into().unwrap(); let v = value.as_ref().map_or(ptr::null_mut(), |cs| cs.as_ptr() as _);
to_result(unsafe {
sys::smfi_chgheader(self.context_ptr.as_ptr(), name.as_ptr() as _, index, v)
})
}
fn append_to_new_body(&self, content: &[u8]) -> Result<()> {
to_result(unsafe {
sys::smfi_replacebody(self.context_ptr.as_ptr(), content.as_ptr() as _, content.len() as _)
})
}
fn quarantine(&self, reason: &str) -> Result<()> {
let s = to_c_string(reason)?;
let r = unsafe { sys::smfi_quarantine(self.context_ptr.as_ptr(), s.as_ptr() as _) };
to_result(r)
}
fn keepalive(&self) -> Result<()> {
let status = unsafe { sys::smfi_progress(self.context_ptr.as_ptr()) };
match status {
sys::MI_SUCCESS => Ok(()),
_ => Err(Error::new(ErrorKind::MilterError)),
}
}
}
pub struct DataHandle<T> {
context_ptr: NonNull<sys::SMFICTX>,
data_ptr: RefCell<Option<NonNull<T>>>,
}
impl<T> DataHandle<T> {
fn new(ptr: *mut sys::SMFICTX) -> Self {
assert!(!ptr.is_null());
let data_ptr = unsafe { sys::smfi_getpriv(ptr) as _ };
DataHandle {
context_ptr: NonNull::new(ptr).unwrap(),
data_ptr: RefCell::new(NonNull::new(data_ptr)),
}
}
pub fn replace(&self, data: T) -> Option<T> {
self.replace_data(Box::into_raw(Box::new(data)))
}
pub fn take(&self) -> Option<T> {
self.replace_data(ptr::null_mut())
}
fn replace_data(&self, ptr: *mut T) -> Option<T> {
let _ = unsafe { sys::smfi_setpriv(self.context_ptr.as_ptr(), ptr as _) };
self.data_ptr
.replace(NonNull::new(ptr))
.map(|t| unsafe { *Box::from_raw(t.as_ptr()) })
}
pub fn borrow(&self) -> Result<Option<Ref<T>>> {
let data_ref = self.data_ptr.try_borrow()
.map_err(|_| Error::new(ErrorKind::DataAccessError))?;
Ok(if data_ref.is_none() {
None
} else {
Some(Ref::map(data_ref, |t| unsafe { t.as_ref().unwrap().as_ref() }))
})
}
pub fn borrow_mut(&self) -> Result<Option<RefMut<T>>> {
let data_ref = self.data_ptr.try_borrow_mut()
.map_err(|_| Error::new(ErrorKind::DataAccessError))?;
Ok(if data_ref.is_none() {
None
} else {
Some(RefMut::map(data_ref, |t| unsafe { t.as_mut().unwrap().as_mut() }))
})
}
}
pub fn version() -> (u32, u32, u32) {
let (mut major, mut minor, mut patch) = (0, 0, 0);
let _ = unsafe { sys::smfi_version(&mut major, &mut minor, &mut patch) };
(major, minor, patch)
}
pub fn set_internal_debug_level(level: i32) {
let _ = unsafe { sys::smfi_setdbg(level) };
}
fn to_c_string(s: &str) -> Result<CString> {
CString::new(s).map_err(|_| Error::new(ErrorKind::ConversionError))
}
fn to_result(status: sys::sfsistat) -> Result<()> {
match status {
sys::MI_SUCCESS => Ok(()),
_ => Err(Error::new(ErrorKind::MilterError)),
}
}