use milter::{Actions, Context, MacroValue, Milter, ProtocolOpts, Stage, Status};
use std::{env, net::SocketAddr, process};
fn main() {
let args = env::args().collect::<Vec<_>>();
if args.len() != 2 {
eprintln!("usage: {} <socket>", 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<SocketAddr>,
) -> milter::Result<Status> {
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<Status> {
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<Status> {
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<Status> {
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<Status> {
println!("DATA");
print_macros(&ctx.api)?;
Ok(Status::Continue)
}
#[milter::on_header(header_callback)]
fn handle_header(_: Context<()>, name: &str, value: &str) -> milter::Result<Status> {
println!("HEADER");
println!("name: {}", name);
println!("value: {}", value);
Ok(Status::Continue)
}
#[milter::on_eoh(eoh_callback)]
fn handle_eoh(ctx: Context<()>) -> milter::Result<Status> {
println!("EOH");
print_macros(&ctx.api)?;
Ok(Status::Continue)
}
#[milter::on_body(body_callback)]
fn handle_body(_: Context<()>, content: &[u8]) -> milter::Result<Status> {
println!("BODY");
println!("content: {}", String::from_utf8_lossy(content));
Ok(Status::Continue)
}
#[milter::on_eom(eom_callback)]
fn handle_eom(ctx: Context<()>) -> milter::Result<Status> {
println!("EOM");
print_macros(&ctx.api)?;
Ok(Status::Continue)
}
#[milter::on_abort(abort_callback)]
fn handle_abort(_: Context<()>) -> milter::Result<Status> {
println!("ABORT");
Ok(Status::Continue)
}
#[milter::on_close(close_callback)]
fn handle_close(_: Context<()>) -> milter::Result<Status> {
println!("CLOSE");
Ok(Status::Continue)
}
#[milter::on_unknown(unknown_callback)]
fn handle_unknown(_: Context<()>, smtp_cmd: &str) -> milter::Result<Status> {
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);
}
})
}