1use std::io;
2
3use async_trait::async_trait;
4use thiserror::Error;
5
6use miltr_common::{
7 actions::{Action, Continue},
8 commands::{Body, Connect, Header, Helo, Macro, Mail, Recipient, Unknown},
9 modifications::ModificationResponse,
10 optneg::OptNeg,
11 ProtocolError,
12};
13
14#[async_trait]
18pub trait Milter: Send {
19 type Error: Send;
21
22 #[doc(alias = "SMFIC_OPTNEG")]
24 #[doc(alias = "xxfi_negotiate")]
25 async fn option_negotiation(&mut self, theirs: OptNeg) -> Result<OptNeg, Error<Self::Error>> {
26 let mut ours = OptNeg::default();
27 ours = ours
28 .merge_compatible(&theirs)
29 .map_err(ProtocolError::CompatibilityError)?;
30 Ok(ours)
31 }
32
33 #[doc(alias = "SMFIC_MACRO")]
35 async fn macro_(&mut self, _macro: Macro) -> Result<(), Self::Error> {
36 Ok(())
37 }
38
39 #[doc(alias = "SMFIC_CONNECT")]
41 #[doc(alias = "xxfi_connect")]
42 async fn connect(&mut self, _connect_info: Connect) -> Result<Action, Self::Error> {
43 Ok(Continue.into())
44 }
45
46 #[doc(alias = "SMFIC_HELO")]
48 #[doc(alias = "xxfi_helo")]
49 async fn helo(&mut self, _helo: Helo) -> Result<Action, Self::Error> {
50 Ok(Continue.into())
51 }
52
53 #[doc(alias = "SMFIC_MAIL")]
55 #[doc(alias = "from")]
56 #[doc(alias = "xxfi_envfrom")]
57 async fn mail(&mut self, _mail: Mail) -> Result<Action, Self::Error> {
58 Ok(Continue.into())
59 }
60
61 #[doc(alias = "SMFIC_RCPT")]
63 #[doc(alias = "to")]
64 #[doc(alias = "xxfi_envrcpt")]
65 async fn rcpt(&mut self, _recipient: Recipient) -> Result<Action, Self::Error> {
66 Ok(Continue.into())
67 }
68
69 #[doc(alias = "SMFIC_DATA")]
74 #[doc(alias = "xxfi_data")]
75 async fn data(&mut self) -> Result<Action, Self::Error> {
76 Ok(Continue.into())
77 }
78
79 #[doc(alias = "SMFIC_HEADER")]
83 #[doc(alias = "xxfi_header")]
84 async fn header(&mut self, _header: Header) -> Result<Action, Self::Error> {
85 Ok(Continue.into())
86 }
87
88 #[doc(alias = "SMFIC_EOH")]
90 #[doc(alias = "xxfi_eoh")]
91 async fn end_of_header(&mut self) -> Result<Action, Self::Error> {
92 Ok(Continue.into())
93 }
94
95 #[doc(alias = "SMFIC_BODY")]
99 #[doc(alias = "xxfi_body")]
100 async fn body(&mut self, _body: Body) -> Result<Action, Self::Error> {
101 Ok(Continue.into())
102 }
103
104 #[doc(alias = "SMFIC_BODYEOB")]
109 #[doc(alias = "xxfi_eom")]
110 async fn end_of_body(&mut self) -> Result<ModificationResponse, Self::Error> {
111 Ok(ModificationResponse::empty_continue())
112 }
113
114 #[doc(alias = "SMFIC_UNKNOWN")]
116 #[doc(alias = "xxfi_unknown")]
117 async fn unknown(&mut self, _cmd: Unknown) -> Result<Action, Self::Error> {
118 Ok(Continue.into())
119 }
120
121 #[doc(alias = "SMFIC_ABORT")]
133 #[doc(alias = "xxfi_abort")]
134 async fn abort(&mut self) -> Result<(), Self::Error>;
135
136 #[doc(alias = "SMFIC_QUIT")]
143 #[doc(alias = "xxfi_close")]
144 async fn quit(&mut self) -> Result<(), Self::Error> {
145 Ok(())
146 }
147
148 #[doc(alias = "SMFIC_QUIT_NC")]
150 async fn quit_nc(&mut self) -> Result<(), Self::Error> {
151 Ok(())
152 }
153}
154
155#[derive(Debug, Error)]
157pub enum Error<ImplError> {
158 #[error(transparent)]
161 Io(#[from] io::Error),
162
163 #[error(transparent)]
166 Codec(#[from] ProtocolError),
167
168 #[error(transparent)]
171 Impl {
172 source: ImplError,
174 },
175}
176
177impl<AppError> Error<AppError> {
178 pub(crate) fn from_app_error(source: AppError) -> Self {
179 Self::Impl { source }
180 }
181}