milter-sys 0.2.3

Low-level FFI bindings to libmilter
Documentation
//! Low-level FFI bindings to libmilter.

#![allow(non_camel_case_types)]

use libc::{c_char, c_int, c_uchar, c_uint, c_ulong, c_void, size_t};

pub const SMFI_VERSION: c_int = 0x1000001;
pub const SMFI_PROT_VERSION: c_int = 6;

pub const MILTER_LEN_BYTES: size_t = 4;
pub const MILTER_OPTLEN: size_t = MILTER_LEN_BYTES * 3;
pub const MILTER_CHUNK_SIZE: size_t = 65535;
pub const MILTER_MAX_DATA_SIZE: size_t = 65535;

pub const SMFI_V1_ACTS: c_ulong = 0xf;
pub const SMFI_V2_ACTS: c_ulong = 0x3f;
pub const SMFI_CURR_ACTS: c_ulong = 0x1ff;

pub const SMFIA_UNKNOWN: c_uchar = b'U';
pub const SMFIA_UNIX: c_uchar = b'L';
pub const SMFIA_INET: c_uchar = b'4';
pub const SMFIA_INET6: c_uchar = b'6';

pub const SMFIC_ABORT: c_uchar = b'A';
pub const SMFIC_BODY: c_uchar = b'B';
pub const SMFIC_CONNECT: c_uchar = b'C';
pub const SMFIC_MACRO: c_uchar = b'D';
pub const SMFIC_BODYEOB: c_uchar = b'E';
pub const SMFIC_HELO: c_uchar = b'H';
pub const SMFIC_QUIT_NC: c_uchar = b'K';
pub const SMFIC_HEADER: c_uchar = b'L';
pub const SMFIC_MAIL: c_uchar = b'M';
pub const SMFIC_EOH: c_uchar = b'N';
pub const SMFIC_OPTNEG: c_uchar = b'O';
pub const SMFIC_QUIT: c_uchar = b'Q';
pub const SMFIC_RCPT: c_uchar = b'R';
pub const SMFIC_DATA: c_uchar = b'T';
pub const SMFIC_UNKNOWN: c_uchar = b'U';

pub const SMFIR_ADDRCPT: c_uchar = b'+';
pub const SMFIR_DELRCPT: c_uchar = b'-';
pub const SMFIR_ADDRCPT_PAR: c_uchar = b'2';
pub const SMFIR_SHUTDOWN: c_uchar = b'4';
pub const SMFIR_ACCEPT: c_uchar = b'a';
pub const SMFIR_REPLBODY: c_uchar = b'b';
pub const SMFIR_CONTINUE: c_uchar = b'c';
pub const SMFIR_DISCARD: c_uchar = b'd';
pub const SMFIR_CHGFROM: c_uchar = b'e';
pub const SMFIR_CONN_FAIL: c_uchar = b'f';
pub const SMFIR_ADDHEADER: c_uchar = b'h';
pub const SMFIR_INSHEADER: c_uchar = b'i';
pub const SMFIR_SETSYMLIST: c_uchar = b'l';
pub const SMFIR_CHGHEADER: c_uchar = b'm';
pub const SMFIR_PROGRESS: c_uchar = b'p';
pub const SMFIR_QUARANTINE: c_uchar = b'q';
pub const SMFIR_REJECT: c_uchar = b'r';
pub const SMFIR_SKIP: c_uchar = b's';
pub const SMFIR_TEMPFAIL: c_uchar = b't';
pub const SMFIR_REPLYCODE: c_uchar = b'y';

pub const SMFIP_NOCONNECT: c_ulong = 0x1;
pub const SMFIP_NOHELO: c_ulong = 0x2;
pub const SMFIP_NOMAIL: c_ulong = 0x4;
pub const SMFIP_NORCPT: c_ulong = 0x8;
pub const SMFIP_NOBODY: c_ulong = 0x10;
pub const SMFIP_NOHDRS: c_ulong = 0x20;
pub const SMFIP_NOEOH: c_ulong = 0x40;
pub const SMFIP_NR_HDR: c_ulong = 0x80;
pub const SMFIP_NOHREPL: c_ulong = SMFIP_NR_HDR;
pub const SMFIP_NOUNKNOWN: c_ulong = 0x100;
pub const SMFIP_NODATA: c_ulong = 0x200;
pub const SMFIP_SKIP: c_ulong = 0x400;
pub const SMFIP_RCPT_REJ: c_ulong = 0x800;
pub const SMFIP_NR_CONN: c_ulong = 0x1000;
pub const SMFIP_NR_HELO: c_ulong = 0x2000;
pub const SMFIP_NR_MAIL: c_ulong = 0x4000;
pub const SMFIP_NR_RCPT: c_ulong = 0x8000;
pub const SMFIP_NR_DATA: c_ulong = 0x10000;
pub const SMFIP_NR_UNKN: c_ulong = 0x20000;
pub const SMFIP_NR_EOH: c_ulong = 0x40000;
pub const SMFIP_NR_BODY: c_ulong = 0x80000;
pub const SMFIP_HDR_LEADSPC: c_ulong = 0x100000;
pub const SMFIP_MDS_256K: c_ulong = 0x10000000;
pub const SMFIP_MDS_1M: c_ulong = 0x20000000;

pub const SMFI_V1_PROT: c_ulong = 0x3f;
pub const SMFI_V2_PROT: c_ulong = 0x7f;
pub const SMFI_CURR_PROT: c_ulong = 0x1fffff;
pub const SMFI_INTERNAL: c_ulong = 0x70000000;

pub const MI_SUCCESS: c_int = 0;
pub const MI_FAILURE: c_int = -1;
pub const MI_CONTINUE: c_int = 1;

pub const SMFIF_NONE: c_ulong = 0x0;
pub const SMFIF_ADDHDRS: c_ulong = 0x1;
pub const SMFIF_CHGBODY: c_ulong = 0x2;
pub const SMFIF_MODBODY: c_ulong = SMFIF_CHGBODY;
pub const SMFIF_ADDRCPT: c_ulong = 0x4;
pub const SMFIF_DELRCPT: c_ulong = 0x8;
pub const SMFIF_CHGHDRS: c_ulong = 0x10;
pub const SMFIF_QUARANTINE: c_ulong = 0x20;
pub const SMFIF_CHGFROM: c_ulong = 0x40;
pub const SMFIF_ADDRCPT_PAR: c_ulong = 0x80;
pub const SMFIF_SETSYMLIST: c_ulong = 0x100;

pub const SMFIM_NOMACROS: c_int = -1;
pub const SMFIM_FIRST: c_int = 0;
pub const SMFIM_CONNECT: c_int = 0;
pub const SMFIM_HELO: c_int = 1;
pub const SMFIM_ENVFROM: c_int = 2;
pub const SMFIM_ENVRCPT: c_int = 3;
pub const SMFIM_DATA: c_int = 4;
pub const SMFIM_EOM: c_int = 5;
pub const SMFIM_EOH: c_int = 6;
pub const SMFIM_LAST: c_int = 6;

pub const SMFIS_CONTINUE: c_int = 0;
pub const SMFIS_REJECT: c_int = 1;
pub const SMFIS_DISCARD: c_int = 2;
pub const SMFIS_ACCEPT: c_int = 3;
pub const SMFIS_TEMPFAIL: c_int = 4;
pub const SMFIS_NOREPLY: c_int = 7;
pub const SMFIS_SKIP: c_int = 8;
pub const SMFIS_ALL_OPTS: c_int = 10;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct smfi_str {
    _private: [u8; 0],
}

pub type SMFICTX = smfi_str;
pub type SMFICTX_PTR = *mut smfi_str;
pub type smfiDesc_str = smfiDesc;
pub type smfiDesc_ptr = *mut smfiDesc;
pub type sfsistat = c_int;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct smfiDesc {
    pub xxfi_name: *mut c_char,
    pub xxfi_version: c_int,
    pub xxfi_flags: c_ulong,
    pub xxfi_connect: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, hostname: *mut c_char, hostaddr: *mut libc::sockaddr) -> sfsistat>,
    pub xxfi_helo: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, helohost: *mut c_char) -> sfsistat>,
    pub xxfi_envfrom: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, argv: *mut *mut c_char) -> sfsistat>,
    pub xxfi_envrcpt: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, argv: *mut *mut c_char) -> sfsistat>,
    pub xxfi_header: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, headerf: *mut c_char, headerv: *mut c_char) -> sfsistat>,
    pub xxfi_eoh: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
    pub xxfi_body: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, bodyp: *mut c_uchar, bodylen: size_t) -> sfsistat>,
    pub xxfi_eom: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
    pub xxfi_abort: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
    pub xxfi_close: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
    pub xxfi_unknown: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, arg: *const c_char) -> sfsistat>,
    pub xxfi_data: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
    pub xxfi_negotiate: Option<
        unsafe extern "C" fn(
            ctx: *mut SMFICTX,
            f0: c_ulong,
            f1: c_ulong,
            f2: c_ulong,
            f3: c_ulong,
            pf0: *mut c_ulong,
            pf1: *mut c_ulong,
            pf2: *mut c_ulong,
            pf3: *mut c_ulong,
        ) -> sfsistat,
    >,
}

extern "C" {
    pub fn smfi_opensocket(rmsocket: c_int) -> c_int;
    pub fn smfi_register(descr: smfiDesc) -> c_int;
    pub fn smfi_main() -> c_int;
    pub fn smfi_setbacklog(obacklog: c_int) -> c_int;
    pub fn smfi_setdbg(level: c_int) -> c_int;
    pub fn smfi_settimeout(otimeout: c_int) -> c_int;
    pub fn smfi_setconn(oconn: *mut c_char) -> c_int;
    pub fn smfi_stop() -> c_int;
    pub fn smfi_setmaxdatasize(sz: size_t) -> size_t;
    pub fn smfi_version(pmajor: *mut c_uint, pminor: *mut c_uint, ppl: *mut c_uint) -> c_int;
    pub fn smfi_getsymval(ctx: *mut SMFICTX, symname: *mut c_char) -> *mut c_char;
    pub fn smfi_setreply(ctx: *mut SMFICTX, rcode: *mut c_char, xcode: *mut c_char, message: *mut c_char) -> c_int;
    pub fn smfi_setmlreply(ctx: *mut SMFICTX, rcode: *const c_char, xcode: *const c_char, ...) -> c_int;
    pub fn smfi_addheader(ctx: *mut SMFICTX, headerf: *mut c_char, headerv: *mut c_char) -> c_int;
    pub fn smfi_chgheader(ctx: *mut SMFICTX, headerf: *mut c_char, index: c_int, headerv: *mut c_char) -> c_int;
    pub fn smfi_insheader(ctx: *mut SMFICTX, index: c_int, headerf: *mut c_char, headerv: *mut c_char) -> c_int;
    pub fn smfi_chgfrom(ctx: *mut SMFICTX, mail: *mut c_char, args: *mut c_char) -> c_int;
    pub fn smfi_addrcpt(ctx: *mut SMFICTX, rcpt: *mut c_char) -> c_int;
    pub fn smfi_addrcpt_par(ctx: *mut SMFICTX, rcpt: *mut c_char, args: *mut c_char) -> c_int;
    pub fn smfi_delrcpt(ctx: *mut SMFICTX, rcpt: *mut c_char) -> c_int;
    pub fn smfi_progress(ctx: *mut SMFICTX) -> c_int;
    pub fn smfi_replacebody(ctx: *mut SMFICTX, bodyp: *mut c_uchar, bodylen: c_int) -> c_int;
    pub fn smfi_quarantine(ctx: *mut SMFICTX, reason: *mut c_char) -> c_int;
    pub fn smfi_setpriv(ctx: *mut SMFICTX, privatedata: *mut c_void) -> c_int;
    pub fn smfi_getpriv(ctx: *mut SMFICTX) -> *mut c_void;
    pub fn smfi_setsymlist(ctx: *mut SMFICTX, stage: c_int, macros: *mut c_char) -> c_int;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn all_current_protocol_opts() {
        let protocol_opts = SMFIP_NOCONNECT
            | SMFIP_NOHELO
            | SMFIP_NOMAIL
            | SMFIP_NORCPT
            | SMFIP_NOBODY
            | SMFIP_NOHDRS
            | SMFIP_NOEOH
            | SMFIP_NR_HDR
            | SMFIP_NOUNKNOWN
            | SMFIP_NODATA
            | SMFIP_SKIP
            | SMFIP_RCPT_REJ
            | SMFIP_NR_CONN
            | SMFIP_NR_HELO
            | SMFIP_NR_MAIL
            | SMFIP_NR_RCPT
            | SMFIP_NR_DATA
            | SMFIP_NR_UNKN
            | SMFIP_NR_EOH
            | SMFIP_NR_BODY
            | SMFIP_HDR_LEADSPC;

        assert_eq!(protocol_opts, SMFI_CURR_PROT);
    }

    #[test]
    fn smfi_version_returns_version() {
        let (mut a, mut b, mut c) = (0, 0, 0);

        let result = unsafe { smfi_version(&mut a, &mut b, &mut c) };

        assert_eq!(result, MI_SUCCESS);
        assert_eq!((a, b, c), (1, 0, 1));
    }

    #[test]
    fn register_milter() {
        let desc = smfiDesc {
            xxfi_name: "test\0".as_ptr() as _,
            xxfi_version: SMFI_VERSION,
            xxfi_flags: 0,
            xxfi_connect: None,
            xxfi_helo: None,
            xxfi_envfrom: None,
            xxfi_envrcpt: None,
            xxfi_header: None,
            xxfi_eoh: None,
            xxfi_body: None,
            xxfi_eom: None,
            xxfi_abort: None,
            xxfi_close: None,
            xxfi_unknown: None,
            xxfi_data: None,
            xxfi_negotiate: None,
        };

        let result = unsafe { smfi_register(desc) };

        assert_eq!(result, MI_SUCCESS);
    }
}