diff options
| author | Mroik <mroik@delayed.space> | 2026-04-07 16:38:43 +0200 |
|---|---|---|
| committer | Mroik <mroik@delayed.space> | 2026-04-13 06:56:10 +0200 |
| commit | 6d5043fc90f327addd587548a061d2d52ce1766e (patch) | |
| tree | d3ba42ded565a79037a687c37c6040bdd4b9dfb7 | |
| parent | 1600c4d157e64d7022f9401a53649c30d193cd45 (diff) | |
Add SMTP start mail transaction
Validation for the sender's email address should be added later on. This
is not strictly necessary as the request at this point will already have
gone through another MTA, but it should be done just for good measure in
case someone decides to expose this software directly to the internet.
Signed-off-by: Mroik <mroik@delayed.space>
| -rw-r--r-- | src/smtp.rs | 34 |
1 files changed, 31 insertions, 3 deletions
diff --git a/src/smtp.rs b/src/smtp.rs index 6614897..2ab79f8 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -30,6 +30,7 @@ impl SmtpServer { addr, state: SessionState::default(), client_host: String::new(), + from: None, }; spawn(session.run(stream)); } @@ -41,6 +42,7 @@ struct SessionHandler { addr: SocketAddr, state: SessionState, client_host: String, + from: Option<String>, } impl SessionHandler { @@ -81,21 +83,36 @@ impl SessionHandler { async fn apply(&mut self, command: Command) -> Result<Reply> { match &command { Command::HELO(hostname) => self.helo(hostname).await, + Command::MAIL(from) => self.mail(from).await, } } /// HELO resets buffers async fn helo(&mut self, hostname: &str) -> Result<Reply> { self.client_host = String::from(hostname); - self.state = SessionState::NextState; + self.state = SessionState::Normal; + self.from = None; Ok(Reply::Completed(String::from(SERVER_NAME))) } + + /// TODO: Validate email address + /// MAIL resets buffers + async fn mail(&mut self, from: &str) -> Result<Reply> { + if from.is_empty() { + return Ok(Reply::InvalidParameter); + } + + self.from = Some(String::from(from)); + self.state = SessionState::MailTransaction; + + Ok(Reply::Completed(String::from("Ok"))) + } } enum SessionState { WaitingHelo, - /// This is a dev state - NextState, + MailTransaction, + Normal, } impl Default for SessionState { @@ -108,6 +125,7 @@ enum Reply { Ready(String), Completed(String), InvalidCommand, + InvalidParameter, } impl ToString for Reply { @@ -116,12 +134,14 @@ impl ToString for Reply { Reply::Ready(hostname) => format!("220 {}", hostname), Reply::Completed(hostname) => format!("250 {}", hostname), Reply::InvalidCommand => format!("500 Command not recognized"), + Reply::InvalidParameter => format!("501 Parameter error"), } } } enum Command { HELO(String), + MAIL(String), } impl TryFrom<&str> for Command { @@ -132,6 +152,14 @@ impl TryFrom<&str> for Command { return Ok(Command::HELO(String::from(&value[5..]))); } + if value.len() >= 11 + && value.to_lowercase().starts_with("mail from:<") + && value.contains('>') + { + let from = &value[11..value.find('>').unwrap()]; + return Ok(Command::MAIL(String::from(from))); + } + Err(anyhow::format_err!("Invalid command")) } } |
