use byte_strings::c_str;
use indymilter::{Callbacks, ContextActions, SetErrorReply};
use std::{net::Ipv4Addr, time::Duration};
use tokio::{net::TcpListener, sync::oneshot};
use indymilter_test::*;
const LOCALHOST: (Ipv4Addr, u16) = (Ipv4Addr::LOCALHOST, 0);
#[tokio::test]
async fn basic() {
let _ = tracing_subscriber::fmt::try_init();
let listener = TcpListener::bind(LOCALHOST).await.unwrap();
let callbacks = make_milter_callbacks();
let config = Default::default();
let (shutdown_milter, shutdown) = oneshot::channel();
let milter_addr = listener.local_addr().unwrap();
let milter = tokio::spawn(indymilter::run(listener, callbacks, config, shutdown));
let mut conn = TestConnection::configure()
.read_timeout(Duration::from_secs(10))
.write_timeout(Duration::from_secs(10))
.available_actions(Actions::ADD_HEADER)
.open_tcp(milter_addr)
.await
.unwrap();
assert_eq!(conn.negotiated_actions(), Actions::ADD_HEADER);
let status = conn.connect("client.example.org", [123, 123, 123, 123])
.await
.unwrap();
assert_eq!(status, Status::Continue);
let status = conn.helo("mail.example.org").await.unwrap();
assert_eq!(status, Status::Continue);
conn.macros(MacroStage::Mail, [("{auth_authen}", "from@example.org")])
.await
.unwrap();
let status = conn.mail(["<from@example.org>"]).await.unwrap();
assert_eq!(status, Status::Continue);
let (actions, status) = conn.eom().await.unwrap();
assert_eq!(
status,
Status::Reject {
message: Some(c_str!("550 No.").into()),
}
);
assert!(actions.has_add_header("X-Name", "X-Value"));
conn.close().await.unwrap();
shutdown_milter.send(()).unwrap();
milter.await.unwrap().unwrap();
}
fn make_milter_callbacks() -> Callbacks<()> {
use indymilter::Status;
Callbacks::new()
.on_negotiate(|_cx, _, _| Box::pin(async { Status::AllOpts }))
.on_connect(|_cx, _, _| Box::pin(async { Status::Continue }))
.on_helo(|_cx, _| Box::pin(async { Status::Continue }))
.on_mail(|_cx, _| Box::pin(async { Status::Continue }))
.on_eom(|cx| {
Box::pin(async move {
cx.actions.add_header("X-Name", "X-Value").await.unwrap();
cx.reply.set_error_reply("550", None, ["No."]).unwrap();
Status::Reject
})
})
}