From facfa4d7eabc10e834f0fa721c67f21bc3ebe95f Mon Sep 17 00:00:00 2001 From: Damian Poddebniak Date: Fri, 4 Dec 2020 15:03:28 +0100 Subject: [PATCH] Improve fields of Command enum. --- src/parse/command.rs | 68 ++++++++++++++++----- src/types.rs | 137 +++++++++++++++++++++++++++++++++---------- 2 files changed, 160 insertions(+), 45 deletions(-) diff --git a/src/parse/command.rs b/src/parse/command.rs index 8dbb85f..38b60f8 100644 --- a/src/parse/command.rs +++ b/src/parse/command.rs @@ -40,7 +40,12 @@ pub fn helo(input: &[u8]) -> IResult<&[u8], Command> { let (remaining, (_, _, data, _)) = parser(input)?; - Ok((remaining, Command::Helo(data.into()))) + Ok(( + remaining, + Command::Helo { + fqdn_or_address_literal: data.into(), + }, + )) } /// ehlo = "EHLO" SP ( Domain / address-literal ) CRLF @@ -54,7 +59,12 @@ pub fn ehlo(input: &[u8]) -> IResult<&[u8], Command> { let (remaining, (_, _, data, _)) = parser(input)?; - Ok((remaining, Command::Ehlo(data.into()))) + Ok(( + remaining, + Command::Ehlo { + fqdn_or_address_literal: data.into(), + }, + )) } /// mail = "MAIL FROM:" Reverse-path [SP Mail-parameters] CRLF @@ -72,8 +82,8 @@ pub fn mail(input: &[u8]) -> IResult<&[u8], Command> { Ok(( remaining, Command::Mail { - data: data.into(), - params: maybe_params.map(|params| params.into()), + reverse_path: data.into(), + parameters: maybe_params.map(|params| params.into()), }, )) } @@ -101,8 +111,8 @@ pub fn rcpt(input: &[u8]) -> IResult<&[u8], Command> { Ok(( remaining, Command::Rcpt { - data: data.into(), - params: maybe_params.map(|params| params.into()), + forward_path: data.into(), + parameters: maybe_params.map(|params| params.into()), }, )) } @@ -131,7 +141,12 @@ pub fn vrfy(input: &[u8]) -> IResult<&[u8], Command> { let (remaining, (_, _, data, _)) = parser(input)?; - Ok((remaining, Command::Vrfy(data.into()))) + Ok(( + remaining, + Command::Vrfy { + user_or_mailbox: data.into(), + }, + )) } /// expn = "EXPN" SP String CRLF @@ -140,7 +155,12 @@ pub fn expn(input: &[u8]) -> IResult<&[u8], Command> { let (remaining, (_, _, data, _)) = parser(input)?; - Ok((remaining, Command::Expn(data.into()))) + Ok(( + remaining, + Command::Expn { + mailing_list: data.into(), + }, + )) } /// help = "HELP" [ SP String ] CRLF @@ -149,7 +169,12 @@ pub fn help(input: &[u8]) -> IResult<&[u8], Command> { let (remaining, (_, maybe_data, _)) = parser(input)?; - Ok((remaining, Command::Help(maybe_data.map(|data| data.into())))) + Ok(( + remaining, + Command::Help { + argument: maybe_data.map(|data| data.into()), + }, + )) } /// noop = "NOOP" [ SP String ] CRLF @@ -158,7 +183,12 @@ pub fn noop(input: &[u8]) -> IResult<&[u8], Command> { let (remaining, (_, maybe_data, _)) = parser(input)?; - Ok((remaining, Command::Noop(maybe_data.map(|data| data.into())))) + Ok(( + remaining, + Command::Noop { + argument: maybe_data.map(|data| data.into()), + }, + )) } /// quit = "QUIT" CRLF @@ -512,14 +542,24 @@ mod test { #[test] fn test_ehlo() { let (rem, parsed) = ehlo(b"EHLO [123.123.123.123]\r\n???").unwrap(); - assert_eq!(parsed, Command::Ehlo(b"123.123.123.123".to_vec())); + assert_eq!( + parsed, + Command::Ehlo { + fqdn_or_address_literal: b"123.123.123.123".to_vec() + } + ); assert_eq!(rem, b"???"); } #[test] fn test_helo() { let (rem, parsed) = helo(b"HELO example.com\r\n???").unwrap(); - assert_eq!(parsed, Command::Helo(b"example.com".to_vec())); + assert_eq!( + parsed, + Command::Helo { + fqdn_or_address_literal: b"example.com".to_vec() + } + ); assert_eq!(rem, b"???"); } @@ -529,8 +569,8 @@ mod test { assert_eq!( parsed, Command::Mail { - data: b"".to_vec(), - params: None + reverse_path: b"".to_vec(), + parameters: None } ); assert_eq!(rem, b"???"); diff --git a/src/types.rs b/src/types.rs index 4a7b71a..5fa5c73 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,23 +1,86 @@ use crate::escape; +use std::io::Write; #[derive(Clone, PartialEq, Eq)] pub enum Command { - Ehlo(Vec), - Helo(Vec), + Ehlo { + fqdn_or_address_literal: Vec, + }, + Helo { + fqdn_or_address_literal: Vec, + }, Mail { - data: Vec, - params: Option>, + reverse_path: Vec, + parameters: Option>, }, Rcpt { - data: Vec, - params: Option>, + forward_path: Vec, + parameters: Option>, }, Data, Rset, - Vrfy(Vec), - Expn(Vec), - Help(Option>), - Noop(Option>), + /// This command asks the receiver to confirm that the argument + /// identifies a user or mailbox. If it is a user name, information is + /// returned as specified in Section 3.5. + /// + /// This command has no effect on the reverse-path buffer, the forward- + /// path buffer, or the mail data buffer. + Vrfy { + user_or_mailbox: Vec, + }, + /// This command asks the receiver to confirm that the argument + /// identifies a mailing list, and if so, to return the membership of + /// that list. If the command is successful, a reply is returned + /// containing information as described in Section 3.5. This reply will + /// have multiple lines except in the trivial case of a one-member list. + /// + /// This command has no effect on the reverse-path buffer, the forward- + /// path buffer, or the mail data buffer, and it may be issued at any + /// time. + Expn { + mailing_list: Vec, + }, + /// This command causes the server to send helpful information to the + /// client. The command MAY take an argument (e.g., any command name) + /// and return more specific information as a response. + /// + /// SMTP servers SHOULD support HELP without arguments and MAY support it + /// with arguments. + /// + /// This command has no effect on the reverse-path buffer, the forward- + /// path buffer, or the mail data buffer, and it may be issued at any + /// time. + Help { + argument: Option>, + }, + /// This command does not affect any parameters or previously entered + /// commands. It specifies no action other than that the receiver send a + /// "250 OK" reply. + /// + /// If a parameter string is specified, servers SHOULD ignore it. + /// + /// This command has no effect on the reverse-path buffer, the forward- + /// path buffer, or the mail data buffer, and it may be issued at any + /// time. + Noop { + argument: Option>, + }, + /// This command specifies that the receiver MUST send a "221 OK" reply, + /// and then close the transmission channel. + /// + /// The receiver MUST NOT intentionally close the transmission channel + /// until it receives and replies to a QUIT command (even if there was an + /// error). The sender MUST NOT intentionally close the transmission + /// channel until it sends a QUIT command, and it SHOULD wait until it + /// receives the reply (even if there was an error response to a previous + /// command). If the connection is closed prematurely due to violations + /// of the above or system or network failure, the server MUST cancel any + /// pending transaction, but not undo any previously completed + /// transaction, and generally MUST act as if the command or transaction + /// in progress had received a temporary error (i.e., a 4yz response). + /// + /// The QUIT command may be issued at any time. Any current uncompleted + /// mail transaction will be aborted. Quit, // Extensions StartTLS, @@ -30,16 +93,16 @@ pub enum Command { impl Command { pub fn name(&self) -> &'static str { match self { - Command::Ehlo(_) => "EHLO", - Command::Helo(_) => "HELO", + Command::Ehlo { .. } => "EHLO", + Command::Helo { .. } => "HELO", Command::Mail { .. } => "MAIL", Command::Rcpt { .. } => "RCPT", Command::Data => "DATA", Command::Rset => "RSET", - Command::Vrfy(_) => "VRFY", - Command::Expn(_) => "EXPN", - Command::Help(_) => "HELP", - Command::Noop(_) => "NOOP", + Command::Vrfy { .. } => "VRFY", + Command::Expn { .. } => "EXPN", + Command::Help { .. } => "HELP", + Command::Noop { .. } => "NOOP", Command::Quit => "QUIT", // Extensions Command::StartTLS => "STARTTLS", @@ -51,34 +114,46 @@ impl Command { } } +// FIXME: try to derive(Debug) instead impl std::fmt::Debug for Command { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { use Command::*; match self { - Ehlo(data) => write!(f, "Ehlo({})", escape(data)), - Helo(data) => write!(f, "Helo({})", escape(data)), + Ehlo { + fqdn_or_address_literal, + } => write!(f, "Ehlo({})", escape(fqdn_or_address_literal)), + Helo { + fqdn_or_address_literal, + } => write!(f, "Helo({})", escape(fqdn_or_address_literal)), Mail { - data: path, - params: None, + reverse_path: path, + parameters: None, } => write!(f, "Mail({})", escape(path)), Mail { - data: path, - params: Some(params), + reverse_path: path, + parameters: Some(params), } => write!(f, "Mail({}, {})", escape(path), escape(params)), - Rcpt { data, params: None } => write!(f, "Rcpt({})", escape(data)), Rcpt { - data, - params: Some(params), + forward_path: data, + parameters: None, + } => write!(f, "Rcpt({})", escape(data)), + Rcpt { + forward_path: data, + parameters: Some(params), } => write!(f, "Rcpt({}, {})", escape(data), escape(params)), Data => write!(f, "Data"), Rset => write!(f, "Rset"), - Vrfy(data) => write!(f, "Vrfy({})", escape(data)), - Expn(data) => write!(f, "Expn({})", escape(data)), - Help(None) => write!(f, "Help"), - Help(Some(data)) => write!(f, "Help({})", escape(data)), - Noop(None) => write!(f, "Noop"), - Noop(Some(data)) => write!(f, "Noop({})", escape(data)), + Vrfy { user_or_mailbox } => write!(f, "Vrfy({})", escape(user_or_mailbox)), + Expn { mailing_list } => write!(f, "Expn({})", escape(mailing_list)), + Help { argument: None } => write!(f, "Help"), + Help { + argument: Some(data), + } => write!(f, "Help({})", escape(data)), + Noop { argument: None } => write!(f, "Noop"), + Noop { + argument: Some(data), + } => write!(f, "Noop({})", escape(data)), Quit => write!(f, "Quit"), // Extensions StartTLS => write!(f, "StartTLS"),