Author:
Hash:
Timestamp:
+59 -23 +/-6 browse
Kevin Schoon [me@kevinschoon.com]
c985e3c60393a664d75d26dff6af799a7f5d1d7d
Tue, 08 Oct 2024 15:09:04 +0000 (1.0 years ago)
| 1 | diff --git a/maitred/Cargo.toml b/maitred/Cargo.toml |
| 2 | index fb2c193..6db3845 100644 |
| 3 | --- a/maitred/Cargo.toml |
| 4 | +++ b/maitred/Cargo.toml |
| 5 | @@ -33,5 +33,6 @@ tracing-subscriber = "0.3.18" |
| 6 | |
| 7 | [features] |
| 8 | default = [] |
| 9 | - full = ["server"] |
| 10 | + full = ["server", "auth"] |
| 11 | server = [] |
| 12 | + auth = ["server"] |
| 13 | diff --git a/maitred/src/auth.rs b/maitred/src/auth.rs |
| 14 | index 133acff..0494e65 100644 |
| 15 | --- a/maitred/src/auth.rs |
| 16 | +++ b/maitred/src/auth.rs |
| 17 | @@ -8,6 +8,7 @@ use crate::smtp_response; |
| 18 | use crate::session::Response; |
| 19 | use smtp_proto::Response as SmtpResponse; |
| 20 | |
| 21 | + /// Any error that occurred during authentication. |
| 22 | #[derive(Debug, thiserror::Error)] |
| 23 | pub enum AuthError { |
| 24 | #[error("Unauthorized")] |
| 25 | @@ -51,6 +52,7 @@ impl Into<Response<String>> for AuthError { |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | + /// Authentication trait for handling PLAIN SASL auth as defined in RFC4616 |
| 30 | #[async_trait] |
| 31 | pub trait PlainAuth: Sync + Send { |
| 32 | /// authenticate is passed the plaintext authcid, authzid, and passwd |
| 33 | @@ -64,6 +66,7 @@ pub trait PlainAuth: Sync + Send { |
| 34 | ) -> Result<(), AuthError>; |
| 35 | } |
| 36 | |
| 37 | + /// Convenience function implementing PlainAuth |
| 38 | pub struct PlainAuthFunc<F, T>(pub F) |
| 39 | where |
| 40 | F: Fn(&str, &str, &str) -> T + Sync + Send, |
| 41 | diff --git a/maitred/src/delivery.rs b/maitred/src/delivery.rs |
| 42 | index 4e402f0..d7cd878 100644 |
| 43 | --- a/maitred/src/delivery.rs |
| 44 | +++ b/maitred/src/delivery.rs |
| 45 | @@ -4,6 +4,7 @@ use async_trait::async_trait; |
| 46 | |
| 47 | use crate::session::Envelope; |
| 48 | |
| 49 | + /// Error that occurred delivering mail |
| 50 | #[derive(Debug, thiserror::Error)] |
| 51 | pub enum DeliveryError { |
| 52 | /// Indicates an unspecified error that occurred during milting. |
| 53 | @@ -22,7 +23,7 @@ pub trait Delivery: Sync + Send { |
| 54 | } |
| 55 | |
| 56 | /// DeliveryFunc wraps an async closure implementing the Delivery trait. |
| 57 | - /// ```FIXME |
| 58 | + /// ```rust |
| 59 | /// use maitred::delivery::DeliveryFunc; |
| 60 | /// use maitred::Envelope; |
| 61 | /// |
| 62 | diff --git a/maitred/src/lib.rs b/maitred/src/lib.rs |
| 63 | index 07d67d0..ec4e5f2 100644 |
| 64 | --- a/maitred/src/lib.rs |
| 65 | +++ b/maitred/src/lib.rs |
| 66 | @@ -1,7 +1,4 @@ |
| 67 | - //! Maitred is a flexible and embedable SMTP server for handling e-mail from |
| 68 | - //! within a Rust program. Designed for use in [ayllu](https://ayllu-forge.org) |
| 69 | - //! but also for general use. |
| 70 | - //! |
| 71 | + #![doc = include_str!("../../README.md")] |
| 72 | //! # Example SMTP Server |
| 73 | //! ```rust,no_run |
| 74 | //! use maitred::auth::PlainAuthFunc; |
| 75 | @@ -13,7 +10,7 @@ |
| 76 | //! |
| 77 | //! use tracing::Level; |
| 78 | //! |
| 79 | - //! async fn print_message(envelope: &Envelope) -> Result<(), DeliveryError> { |
| 80 | + //! fn print_message(envelope: &Envelope) { |
| 81 | //! println!("New SMTP Message:"); |
| 82 | //! println!("{:?}", envelope.body.headers()); |
| 83 | //! println!("Subject: {:?}", envelope.body.subject()); |
| 84 | @@ -24,7 +21,6 @@ |
| 85 | //! .map(|text| String::from_utf8_lossy(text.as_bytes()).to_string()) |
| 86 | //! .unwrap_or_default() |
| 87 | //! ); |
| 88 | - //! Ok(()) |
| 89 | //! } |
| 90 | //! |
| 91 | //! #[tokio::main] |
| 92 | @@ -39,8 +35,8 @@ |
| 93 | //! let mut mail_server = Server::default() |
| 94 | //! .address("127.0.0.1:2525") |
| 95 | //! .with_delivery(DeliveryFunc(|envelope: &Envelope| { |
| 96 | - //! let envelope = envelope.clone(); |
| 97 | - //! async move { print_message(&envelope).await } |
| 98 | + //! print_message(envelope); |
| 99 | + //! async move { Ok(()) } |
| 100 | //! })); |
| 101 | //! mail_server.listen().await?; |
| 102 | //! Ok(()) |
| 103 | @@ -55,28 +51,58 @@ mod opportunistic; |
| 104 | mod rewrite; |
| 105 | |
| 106 | /// SMTP Authentication |
| 107 | + #[cfg(feature = "auth")] |
| 108 | pub mod auth; |
| 109 | - /// Message Delivery |
| 110 | - pub mod delivery; |
| 111 | - /// Callback for implementing SMTP command EXPN |
| 112 | - pub mod expand; |
| 113 | - /// Message and envelope manipulation |
| 114 | - pub mod milter; |
| 115 | - /// Callback for implementing SMPT command VRFY |
| 116 | - pub mod verify; |
| 117 | + #[cfg(feature = "auth")] |
| 118 | + #[doc(inline)] |
| 119 | + pub use auth::{AuthError, PlainAuth, PlainAuthFunc}; |
| 120 | + |
| 121 | /// Low level SMTP session without network transport |
| 122 | pub mod session; |
| 123 | + #[doc(inline)] |
| 124 | + pub use session::{ |
| 125 | + Action, Envelope, Response, Session, DEFAULT_CAPABILITIES, DEFAULT_GREETING, |
| 126 | + DEFAULT_HELP_BANNER, DEFAULT_MAXIMUM_MESSAGE_SIZE, |
| 127 | + }; |
| 128 | |
| 129 | /// Full featured tokio based TCP server for handling SMTP sessions |
| 130 | #[cfg(feature = "server")] |
| 131 | pub mod server; |
| 132 | #[cfg(feature = "server")] |
| 133 | - mod transport; |
| 134 | + /// Low level line oriented transport for SMTP messages |
| 135 | + pub mod transport; |
| 136 | #[cfg(feature = "server")] |
| 137 | mod validation; |
| 138 | #[cfg(feature = "server")] |
| 139 | mod worker; |
| 140 | |
| 141 | + #[cfg(feature = "server")] |
| 142 | + #[doc(inline)] |
| 143 | + pub use server::{Server, ServerError, DEFAULT_GLOBAL_TIMEOUT_SECS, DEFAULT_LISTEN_ADDR}; |
| 144 | + #[cfg(feature = "server")] |
| 145 | + #[doc(inline)] |
| 146 | + pub use transport::{Command, Transport, TransportError}; |
| 147 | + /// Message Delivery |
| 148 | + #[cfg(feature = "server")] |
| 149 | + pub mod delivery; |
| 150 | + #[doc(inline)] |
| 151 | + pub use delivery::{Delivery, DeliveryError, DeliveryFunc}; |
| 152 | + /// Callback for implementing SMTP command EXPN |
| 153 | + #[cfg(feature = "server")] |
| 154 | + pub mod expand; |
| 155 | + #[doc(inline)] |
| 156 | + pub use expand::{Expansion, ExpansionError, ExpansionFunc}; |
| 157 | + /// Message and envelope manipulation |
| 158 | + #[cfg(feature = "server")] |
| 159 | + pub mod milter; |
| 160 | + #[doc(inline)] |
| 161 | + pub use milter::{Milter, MilterError, MilterFunc}; |
| 162 | + /// Callback for implementing SMPT command VRFY |
| 163 | + #[cfg(feature = "server")] |
| 164 | + pub mod verify; |
| 165 | + #[doc(inline)] |
| 166 | + pub use verify::{Verify, VerifyError, VerifyFunc}; |
| 167 | + |
| 168 | /// Generate a single smtp_response |
| 169 | macro_rules! smtp_response { |
| 170 | ($code:expr, $e1:expr, $e2:expr, $e3:expr, $name:expr) => { |
| 171 | diff --git a/maitred/src/session.rs b/maitred/src/session.rs |
| 172 | index 51d1c2d..8f9558b 100644 |
| 173 | --- a/maitred/src/session.rs |
| 174 | +++ b/maitred/src/session.rs |
| 175 | @@ -31,12 +31,13 @@ pub const DEFAULT_GREETING: &str = "Maitred ESMTP Server"; |
| 176 | // 250-DSN |
| 177 | // 250-SMTPUTF8 |
| 178 | |
| 179 | - /// Default SMTP capabilities advertised by the server |
| 180 | + /// Default SMTP capabilities advertised by the server. |
| 181 | pub const DEFAULT_CAPABILITIES: u32 = smtp_proto::EXT_SIZE |
| 182 | | smtp_proto::EXT_ENHANCED_STATUS_CODES |
| 183 | | smtp_proto::EXT_PIPELINING |
| 184 | | smtp_proto::EXT_8BIT_MIME; |
| 185 | |
| 186 | + /// Wrapper around an SMTP message to send to the client. |
| 187 | #[derive(Debug, Clone)] |
| 188 | pub enum Response<T> |
| 189 | where |
| 190 | @@ -84,7 +85,7 @@ where |
| 191 | |
| 192 | impl<T> Eq for Response<T> where T: Display {} |
| 193 | |
| 194 | - /// An Envelope containing an e-mail message created from the session |
| 195 | + /// An Envelope containing an e-mail message created from the session. |
| 196 | #[derive(Clone, Debug)] |
| 197 | pub struct Envelope { |
| 198 | pub body: Message<'static>, |
| 199 | @@ -93,6 +94,7 @@ pub struct Envelope { |
| 200 | pub hostname: Host, |
| 201 | } |
| 202 | |
| 203 | + /// Action for the server implementor to take probably asynchronously. |
| 204 | pub enum Action<'a> { |
| 205 | Send(Response<String>), |
| 206 | SendMany(Vec<Response<String>>), |
| 207 | diff --git a/maitred/src/transport.rs b/maitred/src/transport.rs |
| 208 | index 9a62879..c5da76a 100644 |
| 209 | --- a/maitred/src/transport.rs |
| 210 | +++ b/maitred/src/transport.rs |
| 211 | @@ -8,6 +8,7 @@ use tokio_util::codec::{Decoder, Encoder}; |
| 212 | |
| 213 | use crate::session::Response; |
| 214 | |
| 215 | + /// Error that occurred at the transport layer |
| 216 | #[derive(Debug, thiserror::Error)] |
| 217 | pub enum TransportError { |
| 218 | /// Returned when a client attempts to send multiple commands sequentially |
| 219 | @@ -44,7 +45,9 @@ pub(crate) enum Receiver { |
| 220 | /// Command from the client with an optional attached payload. |
| 221 | #[derive(Debug)] |
| 222 | pub enum Command { |
| 223 | + /// One or more requests depending if PIPELINING is enabled. |
| 224 | Requests(Vec<Request<String>>), |
| 225 | + /// Message payload possibily sent over multiple frames. |
| 226 | Payload(Bytes), |
| 227 | } |
| 228 | |
| 229 | @@ -57,8 +60,8 @@ impl Display for Command { |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | - /// Line oriented transport |
| 234 | - /// TODO: BINARYMIME |
| 235 | + /// Low-level line oriented transport for handling SMTP connections. |
| 236 | + // TODO: BINARYMIME |
| 237 | #[derive(Default)] |
| 238 | pub struct Transport { |
| 239 | receiver: Option<Box<Receiver>>, |