diff --git a/src/error.rs b/src/error.rs index 7bf7e3a..e7f2fa6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,8 +28,12 @@ pub enum Error { /// /// [RFC 5321, section 4.2.1](https://tools.ietf.org/html/rfc5321#section-4.2.1) PermanentError(Response), + /// Error parsing a response + ResponseParsingError(&'static str), /// Internal client error - ClientError(String), + ClientError(&'static str), + /// DNS resolution error + ResolutionError, /// IO error IoError(io::Error), } @@ -45,6 +49,8 @@ impl StdError for Error { match *self { TransientError(_) => "a transient error occured during the SMTP transaction", PermanentError(_) => "a permanent error occured during the SMTP transaction", + ResponseParsingError(_) => "an error occured while parsing an SMTP response", + ResolutionError => "Could no resolve hostname", ClientError(_) => "an unknown error occured", IoError(_) => "an I/O error occured", } @@ -69,14 +75,14 @@ impl From for Error { match response.severity() { Severity::TransientNegativeCompletion => TransientError(response), Severity::PermanentNegativeCompletion => PermanentError(response), - _ => ClientError("Unknown error code".to_string()) + _ => ClientError("Unknown error code") } } } impl From<&'static str> for Error { fn from(string: &'static str) -> Error { - ClientError(string.to_string()) + ClientError(string) } } diff --git a/src/extension.rs b/src/extension.rs index 8037de0..beb7b31 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -42,7 +42,7 @@ pub enum Extension { impl Extension { fn from_str(s: &str) -> Result, &'static str> { - let splitted : Vec<&str> = s.split(' ').collect(); + let splitted : Vec<&str> = s.split_whitespace().collect(); match (splitted[0], splitted.len()) { ("8BITMIME", 1) => Ok(vec![EightBitMime]), ("SMTPUTF8", 1) => Ok(vec![SmtpUtfEight]), diff --git a/src/response.rs b/src/response.rs index dbc154a..465ddbb 100644 --- a/src/response.rs +++ b/src/response.rs @@ -31,14 +31,14 @@ pub enum Severity { } impl FromStr for Severity { - type Err = &'static str; - fn from_str(s: &str) -> RResult { + type Err = Error; + fn from_str(s: &str) -> RResult { match s { "2" => Ok(PositiveCompletion), "3" => Ok(PositiveIntermediate), "4" => Ok(TransientNegativeCompletion), "5" => Ok(PermanentNegativeCompletion), - _ => Err("First digit must be between 2 and 5"), + _ => Err(Error::ResponseParsingError("First digit must be between 2 and 5")), } } } @@ -74,8 +74,8 @@ pub enum Category { } impl FromStr for Category { - type Err = &'static str; - fn from_str(s: &str) -> RResult { + type Err = Error; + fn from_str(s: &str) -> RResult { match s { "0" => Ok(Syntax), "1" => Ok(Information), @@ -83,7 +83,7 @@ impl FromStr for Category { "3" => Ok(Unspecified3), "4" => Ok(Unspecified4), "5" => Ok(MailSystem), - _ => Err("Second digit must be between 0 and 5"), + _ => Err(Error::ResponseParsingError("Second digit must be between 0 and 5")), } } } @@ -122,10 +122,10 @@ impl FromStr for Code { if s.len() == 3 { match (s[0..1].parse::(), s[1..2].parse::(), s[2..3].parse::()) { (Ok(severity), Ok(category), Ok(detail)) => Ok(Code {severity: severity, category: category, detail: detail}), - _ => return Err(From::from("Could not parse reply code")), + _ => return Err(Error::ResponseParsingError("Could not parse response code")), } } else { - Err(From::from("Could not parse reply code")) + Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)")) } } } @@ -169,15 +169,16 @@ impl ResponseParser { pub fn read_line(&mut self, line: &str) -> RResult { if line.len() < 3 { - return Err(From::from("Could not parse reply code, line too short")); + return Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)")); } - if self.code.is_none() { - self.code = Some(try!(line[0..3].parse::())); - } else { - if self.code.as_ref().unwrap().code() != line[0..3] { - return Err(From::from("Could not parse reply code")); - } + match self.code { + Some(ref code) => { + if code.code() != line[0..3] { + return Err(Error::ResponseParsingError("Response code has changed during a reponse")); + } + }, + None => self.code = Some(try!(line[0..3].parse::())) } if line.len() > 4 { @@ -194,10 +195,9 @@ impl ResponseParser { /// Builds a response from a `ResponseParser` pub fn response(self) -> SmtpResult { - if self.code.is_some() { - Ok(Response::new(self.code.unwrap(), self.message)) - } else { - Err(From::from("Could not parse reply code")) + match self.code { + Some(code) => Ok(Response::new(code, self.message)), + None => Err(Error::ResponseParsingError("Incomplete response, could not read response code")) } } } @@ -280,8 +280,8 @@ mod test { #[test] fn test_severity_from_str() { - assert_eq!("2".parse::(), Ok(Severity::PositiveCompletion)); - assert_eq!("4".parse::(), Ok(Severity::TransientNegativeCompletion)); + assert_eq!("2".parse::().unwrap(), Severity::PositiveCompletion); + assert_eq!("4".parse::().unwrap(), Severity::TransientNegativeCompletion); assert!("1".parse::().is_err()); } @@ -292,8 +292,8 @@ mod test { #[test] fn test_category_from_str() { - assert_eq!("2".parse::(), Ok(Category::Connections)); - assert_eq!("4".parse::(), Ok(Category::Unspecified4)); + assert_eq!("2".parse::().unwrap(), Category::Connections); + assert_eq!("4".parse::().unwrap(), Category::Unspecified4); assert!("6".parse::().is_err()); } diff --git a/src/sender/mod.rs b/src/sender/mod.rs index c56dbda..ebbf8e3 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -171,6 +171,8 @@ impl Sender { if self.state.connection_reuse_count == 0 { try!(self.client.connect()); + let hello_error = Error::ResponseParsingError("No hostname announced by the server"); + // Log the connection info!("connection established to {}", self.client_info.server_addr); @@ -178,7 +180,7 @@ impl Sender { match self.client.ehlo(&self.client_info.hello_name) { Ok(response) => {self.server_info = Some( ServerInfo{ - name: response.first_word().expect("Server announced no hostname"), + name: try_smtp!(response.first_word().ok_or(hello_error), self), esmtp_features: Extension::parse_esmtp_response(&response), }); }, @@ -187,7 +189,7 @@ impl Sender { match self.client.helo(&self.client_info.hello_name) { Ok(response) => {self.server_info = Some( ServerInfo{ - name: response.first_word().expect("Server announced no hostname"), + name: try_smtp!(response.first_word().ok_or(hello_error), self), esmtp_features: vec!(), }); },