diff --git a/lettre/Cargo.toml b/lettre/Cargo.toml index fea5be4..a242229 100644 --- a/lettre/Cargo.toml +++ b/lettre/Cargo.toml @@ -21,7 +21,7 @@ is-it-maintained-open-issues = { repository = "lettre/lettre" } [dependencies] log = "^0.4" -nom = { version = "^4.0", optional = true } +nom = { version = "^5.0", optional = true } bufstream = { version = "^0.1", optional = true } native-tls = { version = "^0.2", optional = true } base64 = { version = "^0.10", optional = true } diff --git a/lettre/src/smtp/client/mod.rs b/lettre/src/smtp/client/mod.rs index b75e73b..e3cbfc9 100644 --- a/lettre/src/smtp/client/mod.rs +++ b/lettre/src/smtp/client/mod.rs @@ -7,7 +7,6 @@ use crate::smtp::error::{Error, SmtpResult}; use crate::smtp::response::Response; use bufstream::BufStream; use log::debug; -use nom::ErrorKind as NomErrorKind; use std::fmt::{Debug, Display}; use std::io::{self, BufRead, BufReader, Read, Write}; use std::net::ToSocketAddrs; @@ -250,7 +249,9 @@ impl InnerClient { let mut response = raw_response.parse::(); while response.is_err() { - if response.as_ref().err().unwrap() != &NomErrorKind::Complete { + if let Error::Parsing(nom::error::ErrorKind::Complete) = + response.as_ref().err().unwrap() + { break; } // TODO read more than one line diff --git a/lettre/src/smtp/error.rs b/lettre/src/smtp/error.rs index c459177..ace85a9 100644 --- a/lettre/src/smtp/error.rs +++ b/lettre/src/smtp/error.rs @@ -37,7 +37,7 @@ pub enum Error { /// TLS error Tls(native_tls::Error), /// Parsing error - Parsing(nom::ErrorKind), + Parsing(nom::error::ErrorKind), } impl Display for Error { @@ -94,9 +94,13 @@ impl From for Error { } } -impl From for Error { - fn from(err: nom::ErrorKind) -> Error { - Parsing(err) +impl From> for Error { + fn from(err: nom::Err<(&str, nom::error::ErrorKind)>) -> Error { + Parsing(match err { + nom::Err::Incomplete(_) => nom::error::ErrorKind::Complete, + nom::Err::Failure((_, k)) => k, + nom::Err::Error((_, k)) => k, + }) } } diff --git a/lettre/src/smtp/response.rs b/lettre/src/smtp/response.rs index 71eea73..5432075 100644 --- a/lettre/src/smtp/response.rs +++ b/lettre/src/smtp/response.rs @@ -1,11 +1,18 @@ //! SMTP response, containing a mandatory return code and an optional text //! message -use nom::*; -use nom::{crlf, ErrorKind as NomErrorKind}; +use crate::smtp::Error; +use nom::{ + branch::alt, + bytes::complete::{tag, take_until}, + combinator::{complete, map}, + multi::many0, + sequence::{preceded, tuple}, + IResult, +}; use std::fmt::{Display, Formatter, Result}; use std::result; -use std::str::{from_utf8, FromStr}; +use std::str::FromStr; use std::string::ToString; /// First digit indicates severity @@ -142,13 +149,10 @@ pub struct Response { } impl FromStr for Response { - type Err = NomErrorKind; + type Err = Error; - fn from_str(s: &str) -> result::Result { - match parse_response(s.as_bytes()) { - Ok((_, res)) => Ok(res), - Err(e) => Err(e.into_error_kind()), - } + fn from_str(s: &str) -> result::Result { + parse_response(s).map(|(_, r)| r).map_err(|e| e.into()) } } @@ -186,97 +190,88 @@ impl Response { // Parsers (originally from tokio-smtp) -named!( - parse_code, - map!( - tuple!(parse_severity, parse_category, parse_detail), - |(severity, category, detail)| Code { +fn parse_code(i: &str) -> IResult<&str, Code> { + let (i, severity) = parse_severity(i)?; + let (i, category) = parse_category(i)?; + let (i, detail) = parse_detail(i)?; + Ok(( + i, + Code { severity, category, detail, - } - ) -); + }, + )) +} -named!( - parse_severity, - alt!( - tag!("2") => { |_| Severity::PositiveCompletion } | - tag!("3") => { |_| Severity::PositiveIntermediate } | - tag!("4") => { |_| Severity::TransientNegativeCompletion } | - tag!("5") => { |_| Severity::PermanentNegativeCompletion } - ) -); +fn parse_severity(i: &str) -> IResult<&str, Severity> { + alt(( + map(tag("2"), |_| Severity::PositiveCompletion), + map(tag("3"), |_| Severity::PositiveIntermediate), + map(tag("4"), |_| Severity::TransientNegativeCompletion), + map(tag("5"), |_| Severity::PermanentNegativeCompletion), + ))(i) +} -named!( - parse_category, - alt!( - tag!("0") => { |_| Category::Syntax } | - tag!("1") => { |_| Category::Information } | - tag!("2") => { |_| Category::Connections } | - tag!("3") => { |_| Category::Unspecified3 } | - tag!("4") => { |_| Category::Unspecified4 } | - tag!("5") => { |_| Category::MailSystem } - ) -); +fn parse_category(i: &str) -> IResult<&str, Category> { + alt(( + map(tag("0"), |_| Category::Syntax), + map(tag("1"), |_| Category::Information), + map(tag("2"), |_| Category::Connections), + map(tag("3"), |_| Category::Unspecified3), + map(tag("4"), |_| Category::Unspecified4), + map(tag("5"), |_| Category::MailSystem), + ))(i) +} -named!( - parse_detail, - alt!( - tag!("0") => { |_| Detail::Zero } | - tag!("1") => { |_| Detail::One } | - tag!("2") => { |_| Detail::Two } | - tag!("3") => { |_| Detail::Three } | - tag!("4") => { |_| Detail::Four} | - tag!("5") => { |_| Detail::Five } | - tag!("6") => { |_| Detail::Six} | - tag!("7") => { |_| Detail::Seven } | - tag!("8") => { |_| Detail::Eight } | - tag!("9") => { |_| Detail::Nine } - ) -); +fn parse_detail(i: &str) -> IResult<&str, Detail> { + alt(( + map(tag("0"), |_| Detail::Zero), + map(tag("1"), |_| Detail::One), + map(tag("2"), |_| Detail::Two), + map(tag("3"), |_| Detail::Three), + map(tag("4"), |_| Detail::Four), + map(tag("5"), |_| Detail::Five), + map(tag("6"), |_| Detail::Six), + map(tag("7"), |_| Detail::Seven), + map(tag("8"), |_| Detail::Eight), + map(tag("9"), |_| Detail::Nine), + ))(i) +} -named!( - parse_response, - map_res!( - tuple!( - // Parse any number of continuation lines. - many0!(tuple!( - parse_code, - preceded!(char!('-'), take_until_and_consume!(b"\r\n".as_ref())) - )), - // Parse the final line. - tuple!( - parse_code, - terminated!( - opt!(preceded!(char!(' '), take_until!(b"\r\n".as_ref()))), - crlf - ) - ) - ), - |(lines, (last_code, last_line)): (Vec<_>, _)| { - // Check that all codes are equal. - if !lines.iter().all(|&(ref code, _)| *code == last_code) { - return Err(()); - } +fn parse_response(i: &str) -> IResult<&str, Response> { + let (i, lines) = many0(tuple(( + parse_code, + preceded(tag("-"), take_until("\r\n")), + tag("\r\n"), + )))(i)?; + let (i, (last_code, last_line)) = + tuple((parse_code, preceded(tag(" "), take_until("\r\n"))))(i)?; + let (i, _) = complete(tag("\r\n"))(i)?; - // Extract text from lines, and append last line. - let mut lines = lines.into_iter().map(|(_, text)| text).collect::>(); - if let Some(text) = last_line { - lines.push(text); - } + // Check that all codes are equal. + if !lines.iter().all(|&(ref code, _, _)| *code == last_code) { + return Err(nom::Err::Failure(("", nom::error::ErrorKind::Not))); + } - Ok(Response { - code: last_code, - message: lines - .into_iter() - .map(|line| from_utf8(line).map(ToString::to_string)) - .collect::, _>>() - .map_err(|_| ())?, - }) - } - ) -); + // Extract text from lines, and append last line. + let mut lines: Vec<&str> = lines + .into_iter() + .map(|(_, text, _)| text) + .collect::>(); + lines.push(last_line); + + Ok(( + i, + Response { + code: last_code, + message: lines + .iter() + .map(ToString::to_string) + .collect::>(), + }, + )) +} #[cfg(test)] mod test { diff --git a/lettre_email/src/lib.rs b/lettre_email/src/lib.rs index 35cb7ef..f6e19e1 100644 --- a/lettre_email/src/lib.rs +++ b/lettre_email/src/lib.rs @@ -610,7 +610,6 @@ mod test { assert!(string_res.unwrap().starts_with("Subject: A Subject")); } - #[test] fn test_email_sendable() { let email_builder = EmailBuilder::new();