Author: Kevin Schoon [me@kevinschoon.com]
Hash: 259ac2f17ae27a90f5eb705dccfed45f863ad17d
Timestamp: Tue, 13 Aug 2024 21:37:21 +0000 (2 months ago)

+74 -69 +/-4 browse
clean up api and export various components
1diff --git a/maitred/src/error.rs b/maitred/src/error.rs
2index 4ef87fa..1694437 100644
3--- a/maitred/src/error.rs
4+++ b/maitred/src/error.rs
5 @@ -8,16 +8,22 @@ use url::ParseError;
6 /// to shutdown and stop processing connections.
7 #[derive(Debug, thiserror::Error)]
8 pub enum Error {
9+ /// An unspecified internal error
10 #[error("Unspecified internal error: {0}")]
11 Internal(String),
12+ /// An IO related error such as not being able to bind to a TCP socket
13 #[error("Io: {0}")]
14 Io(#[from] std::io::Error),
15+ /// An error generated from the underlying SMTP protocol
16 #[error("Smtp failure: {0}")]
17 Smtp(#[from] SmtpError),
18+ /// Inability to parse a URL
19 #[error("Failed to parse Url: {0}")]
20 UrlParsing(#[from] ParseError),
21+ /// UTF8 related Error
22 #[error("Failed to read UTF8: {0}")]
23 Utf8(#[from] FromUtf8Error),
24+ /// Session timeout
25 #[error("Client took too long to respond: {0}s")]
26 Timeout(u64),
27 }
28 diff --git a/maitred/src/lib.rs b/maitred/src/lib.rs
29index e9a537b..c13f738 100644
30--- a/maitred/src/lib.rs
31+++ b/maitred/src/lib.rs
32 @@ -34,10 +34,11 @@ use transport::Response;
33
34 pub use error::Error;
35 pub use expand::{Error as ExpansionError, Expansion, Func as ExpansionFunc};
36+ pub use pipeline::{Pipeline, Transaction};
37 pub use server::Server;
38 pub use session::{
39- Options as SessionOptions, DEFAULT_CAPABILITIES, DEFAULT_GREETING, DEFAULT_HELP_BANNER,
40- DEFAULT_MAXIMUM_MESSAGE_SIZE,
41+ Options as SessionOptions, Session, DEFAULT_CAPABILITIES, DEFAULT_GREETING,
42+ DEFAULT_HELP_BANNER, DEFAULT_MAXIMUM_MESSAGE_SIZE,
43 };
44 pub use verify::{Error as VerifyError, Func as VerifyFunc, Verify};
45
46 @@ -47,64 +48,8 @@ pub use smtp_proto;
47 /// Chunk is a logical set of SMTP resposnes that might be generated from one
48 /// command or "pipelined" as the result of several commands sent by the client
49 /// that do not require immediate responses.
50- #[derive(Clone, Debug)]
51- pub(crate) struct Chunk(pub Vec<Response<String>>);
52-
53- /// Generate a single smtp_response
54- macro_rules! smtp_response {
55- ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
56- Response::General(SmtpResponse::new($code, $e1, $e2, $e3, $name.to_string()))
57- };
58- }
59- pub(crate) use smtp_response;
60-
61- /// Generate an SMTP response
62- macro_rules! smtp_chunk {
63- ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
64- Chunk(vec![Response::General(SmtpResponse::new(
65- $code,
66- $e1,
67- $e2,
68- $e3,
69- $name.to_string(),
70- ))])
71- };
72- }
73- pub(crate) use smtp_chunk;
74-
75- /// Generate an SMTP response error
76- macro_rules! smtp_chunk_ok {
77- ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
78- Ok::<Chunk, Chunk>(Chunk(vec![Response::General(SmtpResponse::new(
79- $code,
80- $e1,
81- $e2,
82- $e3,
83- $name.to_string(),
84- ))]))
85- };
86- }
87- pub(crate) use smtp_chunk_ok;
88-
89- /// Generate an SMTP response error
90- macro_rules! smtp_chunk_err {
91- ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
92- Err::<Chunk, Chunk>(Chunk(vec![Response::General(SmtpResponse::new(
93- $code,
94- $e1,
95- $e2,
96- $e3,
97- $name.to_string(),
98- ))]))
99- };
100- }
101- pub(crate) use smtp_chunk_err;
102-
103- impl Chunk {
104- pub fn new() -> Self {
105- Chunk(vec![])
106- }
107- }
108+ #[derive(Clone, Debug, Default)]
109+ pub struct Chunk(pub Vec<Response<String>>);
110
111 impl PartialEq for Chunk {
112 fn eq(&self, other: &Self) -> bool {
113 @@ -153,3 +98,53 @@ impl From<crate::expand::Error> for Chunk {
114 }
115 }
116 }
117+
118+ /// Generate a single smtp_response
119+ macro_rules! smtp_response {
120+ ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
121+ Response::General(SmtpResponse::new($code, $e1, $e2, $e3, $name.to_string()))
122+ };
123+ }
124+ pub(crate) use smtp_response;
125+
126+ /// Generate a Chunk that contains a single SMTP response.
127+ #[macro_export]
128+ macro_rules! smtp_chunk {
129+ ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
130+ Chunk(vec![Response::General(SmtpResponse::new(
131+ $code,
132+ $e1,
133+ $e2,
134+ $e3,
135+ $name.to_string(),
136+ ))])
137+ };
138+ }
139+
140+ /// Generate a Chunk that contains a single successful response.
141+ #[macro_export]
142+ macro_rules! smtp_chunk_ok {
143+ ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
144+ Ok::<Chunk, Chunk>(Chunk(vec![Response::General(SmtpResponse::new(
145+ $code,
146+ $e1,
147+ $e2,
148+ $e3,
149+ $name.to_string(),
150+ ))]))
151+ };
152+ }
153+
154+ /// Generate a Chunk that that contains a single error.
155+ #[macro_export]
156+ macro_rules! smtp_chunk_err {
157+ ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => {
158+ Err::<Chunk, Chunk>(Chunk(vec![Response::General(SmtpResponse::new(
159+ $code,
160+ $e1,
161+ $e2,
162+ $e3,
163+ $name.to_string(),
164+ ))]))
165+ };
166+ }
167 diff --git a/maitred/src/pipeline.rs b/maitred/src/pipeline.rs
168index 7ad02b3..353ed27 100644
169--- a/maitred/src/pipeline.rs
170+++ b/maitred/src/pipeline.rs
171 @@ -108,8 +108,8 @@ impl Pipeline {
172 self.history.clear();
173 res.clone().unwrap_or_else(|e| e)
174 }
175- Request::Mail { from: _ } => Chunk::new(),
176- Request::Rcpt { to: _ } => Chunk::new(),
177+ Request::Mail { from: _ } => Chunk::default(),
178+ Request::Rcpt { to: _ } => Chunk::default(),
179 Request::Bdat {
180 chunk_size: _,
181 is_last: _,
182 @@ -119,7 +119,7 @@ impl Pipeline {
183 self.history.clear();
184 chunk
185 } else {
186- Chunk::new()
187+ Chunk::default()
188 }
189 }
190 Request::Auth {
191 @@ -140,7 +140,7 @@ impl Pipeline {
192 self.history.clear();
193 chunk
194 } else {
195- Chunk::new()
196+ Chunk::default()
197 }
198 }
199 Request::Rset => {
200 diff --git a/maitred/src/session.rs b/maitred/src/session.rs
201index 47a143e..03d6fce 100644
202--- a/maitred/src/session.rs
203+++ b/maitred/src/session.rs
204 @@ -126,9 +126,9 @@ impl Options {
205 }
206 }
207
208- /// Stateful connection that coresponds to a single SMTP session
209+ /// Stateful connection that coresponds to a single SMTP session.
210 #[derive(Default)]
211- pub(crate) struct Session {
212+ pub struct Session {
213 /// message body
214 pub body: Option<Vec<u8>>,
215 /// mailto address
216 @@ -145,11 +145,14 @@ pub(crate) struct Session {
217 }
218
219 impl Session {
220+ /// Configure a session with various options that effect it's behavior.
221 pub fn with_options(mut self, opts: Rc<Options>) -> Self {
222 self.opts = opts;
223 self
224 }
225
226+ /// Reset the connection to it's default state but after a HELO/ELHO has
227+ /// been issued successfully.
228 pub fn reset(&mut self) {
229 self.body = None;
230 self.mail_from = None;
231 @@ -170,14 +173,15 @@ impl Session {
232 )
233 }
234
235- /// If the session is in extended mode i.e. EHLO was sent
236+ /// If the session is in extended mode i.e. EHLO was sent in the beginning
237+ /// of the connection.
238 pub fn is_extended(&self) -> bool {
239 self.initialized
240 .as_ref()
241 .is_some_and(|mode| matches!(mode, Mode::Extended))
242 }
243
244- /// check if the capability is supported by the session
245+ /// Check if the capability is supported by the session
246 pub fn has_capability(&self, capability: u32) -> bool {
247 self.initialized
248 .as_ref()
249 @@ -185,7 +189,7 @@ impl Session {
250 && self.opts.capabilities & capability != 0
251 }
252
253- /// ensure that the session has been initialized otherwise return an error
254+ /// Ensure that the session has been initialized otherwise return an error
255 fn check_initialized(&self) -> StdResult<(), Chunk> {
256 if self.initialized.is_none() {
257 return Err(smtp_chunk!(500, 0, 0, 0, "It's polite to say EHLO first"));