//! A milter that prints out all arguments and macros. use milter::{Actions, Context, MacroValue, Milter, ProtocolOpts, Stage, Status}; use std::{env, net::SocketAddr, process}; fn main() { let args = env::args().collect::>(); if args.len() != 2 { eprintln!("usage: {} ", args[0]); process::exit(1); } let mut milter = Milter::new(&args[1]); milter .name("Inspect") .on_negotiate(negotiate_callback) .on_connect(connect_callback) .on_helo(helo_callback) .on_mail(mail_callback) .on_rcpt(rcpt_callback) .on_data(data_callback) .on_header(header_callback) .on_eoh(eoh_callback) .on_body(body_callback) .on_eom(eom_callback) .on_abort(abort_callback) .on_close(close_callback) .on_unknown(unknown_callback); eprintln!("Inspect milter starting"); milter.run().expect("milter execution failed"); eprintln!("Inspect milter shut down"); } #[milter::on_negotiate(negotiate_callback)] fn handle_negotiate( ctx: Context<()>, actions: Actions, protocol_opts: ProtocolOpts, ) -> milter::Result<(Status, Actions, ProtocolOpts)> { println!("NEGOTIATE"); println!("actions: {:?}", actions); println!("protocol_opts: {:?}", protocol_opts); ctx.api.request_macros( Stage::Connect, "j \ _ \ {client_addr} \ {client_connections} \ {client_name} \ {client_port} \ {client_ptr} \ {daemon_addr} \ {daemon_name} \ {daemon_port} \ v", )?; ctx.api.request_macros( Stage::Helo, "{cert_issuer} \ {cert_subject} \ {cipher_bits} \ {cipher} \ {tls_version}", )?; ctx.api.request_macros( Stage::Mail, "{auth_authen} \ {auth_author} \ {auth_type} \ {mail_addr} \ {mail_host} \ {mail_mailer}", )?; ctx.api.request_macros( Stage::Rcpt, "{rcpt_addr} \ {rcpt_host} \ {rcpt_mailer}", )?; ctx.api.request_macros(Stage::Data, "i")?; Ok((Status::AllOpts, Default::default(), Default::default())) } #[milter::on_connect(connect_callback)] fn handle_connect( ctx: Context<()>, hostname: &str, socket_address: Option, ) -> milter::Result { println!("CONNECT"); println!("hostname: {}", hostname); if let Some(addr) = socket_address { println!("socket_address: {}", addr); } print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_helo(helo_callback)] fn handle_helo(ctx: Context<()>, helo_host: &str) -> milter::Result { println!("HELO"); println!("helo_host: {}", helo_host); print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_mail(mail_callback)] fn handle_mail(ctx: Context<()>, smtp_args: Vec<&str>) -> milter::Result { println!("MAIL"); println!("smtp_args: {:?}", smtp_args); print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_rcpt(rcpt_callback)] fn handle_rcpt(ctx: Context<()>, smtp_args: Vec<&str>) -> milter::Result { println!("RCPT"); println!("smtp_args: {:?}", smtp_args); print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_data(data_callback)] fn handle_data(ctx: Context<()>) -> milter::Result { println!("DATA"); print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_header(header_callback)] fn handle_header(_: Context<()>, name: &str, value: &str) -> milter::Result { println!("HEADER"); println!("name: {}", name); println!("value: {}", value); Ok(Status::Continue) } #[milter::on_eoh(eoh_callback)] fn handle_eoh(ctx: Context<()>) -> milter::Result { println!("EOH"); print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_body(body_callback)] fn handle_body(_: Context<()>, content: &[u8]) -> milter::Result { println!("BODY"); println!("content: {}", String::from_utf8_lossy(content)); Ok(Status::Continue) } #[milter::on_eom(eom_callback)] fn handle_eom(ctx: Context<()>) -> milter::Result { println!("EOM"); print_macros(&ctx.api)?; Ok(Status::Continue) } #[milter::on_abort(abort_callback)] fn handle_abort(_: Context<()>) -> milter::Result { println!("ABORT"); Ok(Status::Continue) } #[milter::on_close(close_callback)] fn handle_close(_: Context<()>) -> milter::Result { println!("CLOSE"); Ok(Status::Continue) } #[milter::on_unknown(unknown_callback)] fn handle_unknown(_: Context<()>, smtp_cmd: &str) -> milter::Result { println!("UNKNOWN"); println!("smtp_cmd: {}", smtp_cmd); Ok(Status::Continue) } fn print_macros(ctx: &impl MacroValue) -> milter::Result<()> { print_macro(ctx, "i")?; print_macro(ctx, "j")?; print_macro(ctx, "_")?; print_macro(ctx, "{auth_authen}")?; print_macro(ctx, "{auth_author}")?; print_macro(ctx, "{auth_type}")?; print_macro(ctx, "{client_addr}")?; print_macro(ctx, "{client_connections}")?; print_macro(ctx, "{client_name}")?; print_macro(ctx, "{client_port}")?; print_macro(ctx, "{client_ptr}")?; print_macro(ctx, "{cert_issuer}")?; print_macro(ctx, "{cert_subject}")?; print_macro(ctx, "{cipher_bits}")?; print_macro(ctx, "{cipher}")?; print_macro(ctx, "{daemon_addr}")?; print_macro(ctx, "{daemon_name}")?; print_macro(ctx, "{daemon_port}")?; print_macro(ctx, "{mail_addr}")?; print_macro(ctx, "{mail_host}")?; print_macro(ctx, "{mail_mailer}")?; print_macro(ctx, "{rcpt_addr}")?; print_macro(ctx, "{rcpt_host}")?; print_macro(ctx, "{rcpt_mailer}")?; print_macro(ctx, "{tls_version}")?; print_macro(ctx, "v")?; Ok(()) } fn print_macro(ctx: &impl MacroValue, name: &str) -> milter::Result<()> { ctx.macro_value(name).map(|value| { if let Some(value) = value { println!("{}: {}", name, value); } }) }