Improve fields of Command enum.

This commit is contained in:
Damian Poddebniak 2020-12-04 15:03:28 +01:00
parent b8448a8889
commit facfa4d7ea
2 changed files with 160 additions and 45 deletions

View File

@ -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"<userx@y.foo.org>".to_vec(),
params: None
reverse_path: b"<userx@y.foo.org>".to_vec(),
parameters: None
}
);
assert_eq!(rem, b"???");

View File

@ -1,23 +1,86 @@
use crate::escape;
use std::io::Write;
#[derive(Clone, PartialEq, Eq)]
pub enum Command {
Ehlo(Vec<u8>),
Helo(Vec<u8>),
Ehlo {
fqdn_or_address_literal: Vec<u8>,
},
Helo {
fqdn_or_address_literal: Vec<u8>,
},
Mail {
data: Vec<u8>,
params: Option<Vec<u8>>,
reverse_path: Vec<u8>,
parameters: Option<Vec<u8>>,
},
Rcpt {
data: Vec<u8>,
params: Option<Vec<u8>>,
forward_path: Vec<u8>,
parameters: Option<Vec<u8>>,
},
Data,
Rset,
Vrfy(Vec<u8>),
Expn(Vec<u8>),
Help(Option<Vec<u8>>),
Noop(Option<Vec<u8>>),
/// 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<u8>,
},
/// 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<u8>,
},
/// 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<Vec<u8>>,
},
/// 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<Vec<u8>>,
},
/// 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"),