From 3007f1a3ff6de7dbc94c7807e5992d4695bb3444 Mon Sep 17 00:00:00 2001 From: Damian Poddebniak Date: Mon, 10 Aug 2020 15:55:09 +0200 Subject: [PATCH] Greeting Parser and Type --- src/parse/replies.rs | 108 ++++++++++++++++++++++++++++++++++--------- src/types.rs | 8 ++++ 2 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/parse/replies.rs b/src/parse/replies.rs index 7fffa64..6c3f78e 100644 --- a/src/parse/replies.rs +++ b/src/parse/replies.rs @@ -1,13 +1,16 @@ //! 4.2. SMTP Replies (RFC 5321) -use crate::parse::command::{address_literal, Domain}; +use crate::{ + parse::command::{address_literal, Domain}, + types::Greeting as GreetingType, +}; use abnf_core::streaming::{CRLF, SP}; use nom::{ branch::alt, bytes::streaming::{tag, take_while1, take_while_m_n}, - combinator::{opt, recognize}, + combinator::{map, map_res, opt, recognize}, multi::many0, - sequence::tuple, + sequence::{delimited, preceded, tuple}, IResult, }; @@ -15,27 +18,59 @@ use nom::{ /// ( "220-" (Domain / address-literal) [ SP textstring ] CRLF /// *( "220-" [ textstring ] CRLF ) /// "220" [ SP textstring ] CRLF ) -pub fn Greeting(input: &[u8]) -> IResult<&[u8], &[u8]> { +pub fn Greeting(input: &[u8]) -> IResult<&[u8], GreetingType> { let parser = alt(( - recognize(tuple(( - tag(b"220 "), - alt((Domain, address_literal)), - opt(tuple((SP, textstring))), - CRLF, - ))), - recognize(tuple(( - tag(b"220-"), - alt((Domain, address_literal)), - opt(tuple((SP, textstring))), - CRLF, - many0(tuple((tag(b"220-"), opt(textstring), CRLF))), - tag(b"220"), - opt(tuple((SP, textstring))), - CRLF, - ))), + map( + tuple(( + tag(b"220 "), + alt((Domain, address_literal)), + opt(preceded(SP, textstring)), + CRLF, + )), + |(_, domain, maybe_text, _)| GreetingType { + domain: domain.to_owned(), + text: maybe_text + .map(|str| str.to_string()) + .unwrap_or("".to_string()), + }, + ), + map( + tuple(( + tag(b"220-"), + alt((Domain, address_literal)), + opt(preceded(SP, textstring)), + CRLF, + many0(delimited(tag(b"220-"), opt(textstring), CRLF)), + tag(b"220"), + opt(preceded(SP, textstring)), + CRLF, + )), + |(_, domain, maybe_text, _, more_text, _, moar_text, _)| GreetingType { + domain: domain.to_owned(), + text: { + let mut res = maybe_text + .map(|str| format!("{}\n", str)) + .unwrap_or("\n".to_string()); + + for text in more_text { + let text = text + .map(|str| format!("{}\n", str)) + .unwrap_or("\n".to_string()); + res.push_str(&text); + } + + let text = moar_text + .map(|str| format!("{}", str)) + .unwrap_or("".to_string()); + res.push_str(&text); + + res + }, + }, + ), )); - let (remaining, parsed) = recognize(parser)(input)?; + let (remaining, parsed) = parser(input)?; Ok((remaining, parsed)) } @@ -43,7 +78,7 @@ pub fn Greeting(input: &[u8]) -> IResult<&[u8], &[u8]> { /// HT, SP, Printable US-ASCII /// /// textstring = 1*(%d09 / %d32-126) -pub fn textstring(input: &[u8]) -> IResult<&[u8], &[u8]> { +pub fn textstring(input: &[u8]) -> IResult<&[u8], &str> { fn is_value(byte: u8) -> bool { match byte { 9 | 32..=126 => true, @@ -51,7 +86,9 @@ pub fn textstring(input: &[u8]) -> IResult<&[u8], &[u8]> { } } - take_while1(is_value)(input) + let (remaining, parsed) = map_res(take_while1(is_value), std::str::from_utf8)(input)?; + + Ok((remaining, parsed)) } /// Reply-line = *( Reply-code "-" [ textstring ] CRLF ) @@ -78,3 +115,28 @@ pub fn Reply_code(input: &[u8]) -> IResult<&[u8], &[u8]> { // FIXME: do not accept all codes. take_while_m_n(3, 3, nom::character::is_digit)(input) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_Greeting() { + let greeting = b"220-example.org ESMTP Fake 4.93 #2 Thu, 16 Jul 2020 07:30:16 -0400\r\n\ +220-We do not authorize the use of this system to transport unsolicited,\r\n\ +220 and/or bulk e-mail.\r\n"; + + let (rem, out) = Greeting(greeting).unwrap(); + assert_eq!(rem, b""); + assert_eq!( + out, + GreetingType { + domain: "example.org".into(), + text: "ESMTP Fake 4.93 #2 Thu, 16 Jul 2020 07:30:16 -0400\n\ +We do not authorize the use of this system to transport unsolicited,\n\ +and/or bulk e-mail." + .into(), + } + ) + } +} diff --git a/src/types.rs b/src/types.rs index eaf26ab..4a7b71a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -90,6 +90,14 @@ impl std::fmt::Debug for Command { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Greeting { + pub domain: String, + // TODO: Vec> would be closer to the SMTP ABNF. + // What is wrong with you, SMTP? + pub text: String, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct EhloOkResp { pub domain: String,