From 5d125bdbdba72cc2f5e1510c43964d4629fba4c9 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Thu, 8 Oct 2015 20:12:07 +0200 Subject: [PATCH] Format code with rustfmt --- Cargo.toml | 2 +- src/authentication.rs | 44 ++-- src/client/mod.rs | 43 ++-- src/client/net.rs | 2 - src/email.rs | 97 ++++----- src/error.rs | 2 +- src/extension.rs | 189 +++++++++-------- src/lib.rs | 7 +- src/response.rs | 476 ++++++++++++++++++++++-------------------- src/sender.rs | 84 ++++---- 10 files changed, 507 insertions(+), 439 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c20a9a..6ee41ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "smtp" -version = "0.2.0" +version = "0.2.1" description = "Simple SMTP client" readme = "README.md" documentation = "http://amousset.me/rust-smtp/smtp/" diff --git a/src/authentication.rs b/src/authentication.rs index b842854..fe3c0d4 100644 --- a/src/authentication.rs +++ b/src/authentication.rs @@ -25,12 +25,12 @@ pub enum Mecanism { impl Display for Mecanism { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", - match *self { - Mecanism::Plain => "PLAIN", - Mecanism::CramMd5 => "CRAM-MD5", - } - ) + write!(f, + "{}", + match *self { + Mecanism::Plain => "PLAIN", + Mecanism::CramMd5 => "CRAM-MD5", + }) } } @@ -43,15 +43,22 @@ impl Mecanism { } } - /// Returns the string to send to the server, using the provided username, password and challenge in some cases - pub fn response(&self, username: &str, password: &str, challenge: Option<&str>) -> Result { + /// Returns the string to send to the server, using the provided username, password and + /// challenge in some cases + pub fn response(&self, + username: &str, + password: &str, + challenge: Option<&str>) + -> Result { match *self { Mecanism::Plain => { match challenge { Some(_) => Err(Error::ClientError("This mecanism does not expect a challenge")), - None => Ok(format!("{}{}{}{}", NUL, username, NUL, password).as_bytes().to_base64(base64::STANDARD)), + None => Ok(format!("{}{}{}{}", NUL, username, NUL, password) + .as_bytes() + .to_base64(base64::STANDARD)), } - }, + } Mecanism::CramMd5 => { let encoded_challenge = match challenge { Some(challenge) => challenge, @@ -66,8 +73,10 @@ impl Mecanism { let mut hmac = Hmac::new(Md5::new(), password.as_bytes()); hmac.input(&decoded_challenge); - Ok(format!("{} {}", username, hmac.result().code().to_hex()).as_bytes().to_base64(base64::STANDARD)) - }, + Ok(format!("{} {}", username, hmac.result().code().to_hex()) + .as_bytes() + .to_base64(base64::STANDARD)) + } } } } @@ -80,7 +89,8 @@ mod test { fn test_plain() { let mecanism = Mecanism::Plain; - assert_eq!(mecanism.response("username", "password", None).unwrap(), "AHVzZXJuYW1lAHBhc3N3b3Jk"); + assert_eq!(mecanism.response("username", "password", None).unwrap(), + "AHVzZXJuYW1lAHBhc3N3b3Jk"); assert!(mecanism.response("username", "password", Some("test")).is_err()); } @@ -88,9 +98,11 @@ mod test { fn test_cram_md5() { let mecanism = Mecanism::CramMd5; - assert_eq!(mecanism.response("alice", "wonderland", - Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==")).unwrap(), - "YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA="); + assert_eq!(mecanism.response("alice", + "wonderland", + Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==")) + .unwrap(), + "YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA="); assert!(mecanism.response("alice", "wonderland", Some("tést")).is_err()); assert!(mecanism.response("alice", "wonderland", None).is_err()); } diff --git a/src/client/mod.rs b/src/client/mod.rs index 9f2d784..6910630 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -23,8 +23,9 @@ fn escape_dot(string: &str) -> String { format!(".{}", string) } else { string.to_string() - }.replace("\r.", "\r..") - .replace("\n.", "\n..") + } + .replace("\r.", "\r..") + .replace("\n.", "\n..") } /// Returns the string replacing all the CRLF with "\" @@ -59,15 +60,15 @@ impl Client { /// /// It does not connects to the server, but only creates the `Client` pub fn new(addr: A) -> Result, Error> { - let mut addresses = try!(addr.to_socket_addrs()); - - match addresses.next() { - Some(addr) => Ok(Client { - stream: None, - server_addr: addr, - }), - None => Err(From::from("Could nor resolve hostname")), - } + let mut addresses = try!(addr.to_socket_addrs()); + + match addresses.next() { + Some(addr) => Ok(Client { + stream: None, + server_addr: addr, + }), + None => Err(From::from("Could nor resolve hostname")), + } } } @@ -166,14 +167,18 @@ impl Client { pub fn auth(&mut self, mecanism: Mecanism, username: &str, password: &str) -> SmtpResult { if mecanism.supports_initial_response() { - self.command(&format!("AUTH {} {}", mecanism, try!(mecanism.response(username, password, None)))) + self.command(&format!("AUTH {} {}", + mecanism, + try!(mecanism.response(username, password, None)))) } else { let encoded_challenge = match try!(self.command("AUTH CRAM-MD5")).first_word() { Some(challenge) => challenge, None => return Err(Error::ResponseParsingError("Could not read CRAM challenge")), }; - let cram_response = try!(mecanism.response(username, password, Some(&encoded_challenge))); + let cram_response = try!(mecanism.response(username, + password, + Some(&encoded_challenge))); self.command(&format!("AUTH CRAM-MD5 {}", cram_response)) } @@ -236,19 +241,15 @@ mod test { fn test_remove_crlf() { assert_eq!(remove_crlf("\r\n"), ""); assert_eq!(remove_crlf("EHLO my_name\r\n"), "EHLO my_name"); - assert_eq!( - remove_crlf("EHLO my_name\r\nSIZE 42\r\n"), - "EHLO my_nameSIZE 42" - ); + assert_eq!(remove_crlf("EHLO my_name\r\nSIZE 42\r\n"), + "EHLO my_nameSIZE 42"); } #[test] fn test_escape_crlf() { assert_eq!(escape_crlf("\r\n"), ""); assert_eq!(escape_crlf("EHLO my_name\r\n"), "EHLO my_name"); - assert_eq!( - escape_crlf("EHLO my_name\r\nSIZE 42\r\n"), - "EHLO my_nameSIZE 42" - ); + assert_eq!(escape_crlf("EHLO my_name\r\nSIZE 42\r\n"), + "EHLO my_nameSIZE 42"); } } diff --git a/src/client/net.rs b/src/client/net.rs index 6b67a24..dbd7677 100644 --- a/src/client/net.rs +++ b/src/client/net.rs @@ -19,5 +19,3 @@ impl Connector for SmtpStream { /// Represents an atual SMTP network stream //Used later for ssl pub type SmtpStream = TcpStream; - - diff --git a/src/email.rs b/src/email.rs index 6df81dc..cd21165 100644 --- a/src/email.rs +++ b/src/email.rs @@ -74,9 +74,7 @@ pub struct Email { impl Display for Email { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", - self.message.as_string() - ) + write!(f, "{}", self.message.as_string()) } } @@ -92,7 +90,8 @@ impl EmailBuilder { message_id: current_message, }; - match Header::new_with_value("Message-ID".to_string(), format!("<{}@rust-smtp>", current_message)) { + match Header::new_with_value("Message-ID".to_string(), + format!("<{}@rust-smtp>", current_message)) { Ok(header) => email.message.headers.insert(header), Err(_) => (), } @@ -239,7 +238,7 @@ impl SendableEmail for Email { if self.to.is_empty() { None } else { - Some(self.to.clone()) + Some(self.to.clone()) } } @@ -280,22 +279,21 @@ mod test { message_id: current_message, }; - email.message.headers.insert( - Header::new_with_value("Message-ID".to_string(), - format!("<{}@rust-smtp>", current_message) - ).unwrap() - ); + email.message.headers.insert(Header::new_with_value("Message-ID".to_string(), + format!("<{}@rust-smtp>", + current_message)) + .unwrap()); - email.message.headers.insert( - Header::new_with_value("To".to_string(), "to@example.com".to_string()).unwrap() - ); + email.message + .headers + .insert(Header::new_with_value("To".to_string(), "to@example.com".to_string()) + .unwrap()); email.message.body = "body".to_string(); - assert_eq!( - format!("{}", email), - format!("Message-ID: <{}@rust-smtp>\r\nTo: to@example.com\r\n\r\nbody\r\n", current_message) - ); + assert_eq!(format!("{}", email), + format!("Message-ID: <{}@rust-smtp>\r\nTo: to@example.com\r\n\r\nbody\r\n", + current_message)); assert_eq!(current_message.to_string(), email.message_id().unwrap()); } @@ -305,21 +303,23 @@ mod test { let date_now = now(); let email = email_builder.to("user@localhost") - .from("user@localhost") - .cc(("cc@localhost", "Alias")) - .reply_to("reply@localhost") - .sender("sender@localhost") - .body("Hello World!") - .date(&date_now) - .subject("Hello") - .add_header(("X-test", "value")) - .build(); + .from("user@localhost") + .cc(("cc@localhost", "Alias")) + .reply_to("reply@localhost") + .sender("sender@localhost") + .body("Hello World!") + .date(&date_now) + .subject("Hello") + .add_header(("X-test", "value")) + .build(); - assert_eq!( - format!("{}", email), - format!("Message-ID: <{}@rust-smtp>\r\nTo: \r\nFrom: \r\nCc: \"Alias\" \r\nReply-To: \r\nSender: \r\nDate: {}\r\nSubject: Hello\r\nX-test: value\r\n\r\nHello World!\r\n", - email.message_id().unwrap(), date_now.rfc822z()) - ); + assert_eq!(format!("{}", email), + format!("Message-ID: <{}@rust-smtp>\r\nTo: \r\nFrom: \ + \r\nCc: \"Alias\" \r\nReply-To: \ + \r\nSender: \r\nDate: \ + {}\r\nSubject: Hello\r\nX-test: value\r\n\r\nHello World!\r\n", + email.message_id().unwrap(), + date_now.rfc822z())); } #[test] @@ -328,28 +328,21 @@ mod test { let date_now = now(); let email = email_builder.to("user@localhost") - .from("user@localhost") - .cc(("cc@localhost", "Alias")) - .reply_to("reply@localhost") - .sender("sender@localhost") - .body("Hello World!") - .date(&date_now) - .subject("Hello") - .add_header(("X-test", "value")) - .build(); + .from("user@localhost") + .cc(("cc@localhost", "Alias")) + .reply_to("reply@localhost") + .sender("sender@localhost") + .body("Hello World!") + .date(&date_now) + .subject("Hello") + .add_header(("X-test", "value")) + .build(); - assert_eq!( - email.from_address().unwrap(), - "sender@localhost".to_string() - ); - assert_eq!( - email.to_addresses().unwrap(), - vec!["user@localhost".to_string(), "cc@localhost".to_string()] - ); - assert_eq!( - email.message().unwrap(), - format!("{}", email) - ); + assert_eq!(email.from_address().unwrap(), + "sender@localhost".to_string()); + assert_eq!(email.to_addresses().unwrap(), + vec!["user@localhost".to_string(), "cc@localhost".to_string()]); + assert_eq!(email.message().unwrap(), format!("{}", email)); } } diff --git a/src/error.rs b/src/error.rs index 4295a4c..cd3638e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -70,7 +70,7 @@ impl From for Error { match response.severity() { Severity::TransientNegativeCompletion => TransientError(response), Severity::PermanentNegativeCompletion => PermanentError(response), - _ => ClientError("Unknown error code") + _ => ClientError("Unknown error code"), } } } diff --git a/src/extension.rs b/src/extension.rs index 3c67644..021c209 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -54,13 +54,13 @@ pub struct ServerInfo { impl Display for ServerInfo { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} with {}", - self.name, - match self.features.is_empty() { - true => "no supported features".to_string(), - false => format! ("{:?}", self.features), - } - ) + write!(f, + "{} with {}", + self.name, + match self.features.is_empty() { + true => "no supported features".to_string(), + false => format!("{:?}", self.features), + }) } } @@ -69,32 +69,42 @@ impl ServerInfo { pub fn from_response(response: &Response) -> Result { let name = match response.first_word() { Some(name) => name, - None => return Err(Error::ResponseParsingError("Could not read server name")) + None => return Err(Error::ResponseParsingError("Could not read server name")), }; let mut features: HashSet = HashSet::new(); for line in response.message() { - let splitted : Vec<&str> = line.split_whitespace().collect(); + let splitted: Vec<&str> = line.split_whitespace().collect(); let _ = match splitted[0] { - "8BITMIME" => {features.insert(Extension::EightBitMime);}, - "SMTPUTF8" => {features.insert(Extension::SmtpUtfEight);}, - "STARTTLS" => {features.insert(Extension::StartTls);}, + "8BITMIME" => { + features.insert(Extension::EightBitMime); + } + "SMTPUTF8" => { + features.insert(Extension::SmtpUtfEight); + } + "STARTTLS" => { + features.insert(Extension::StartTls); + } "AUTH" => { for &mecanism in &splitted[1..] { match mecanism { - "PLAIN" => {features.insert(Extension::Authentication(Mecanism::Plain));}, - "CRAM-MD5" => {features.insert(Extension::Authentication(Mecanism::CramMd5));}, + "PLAIN" => { + features.insert(Extension::Authentication(Mecanism::Plain)); + } + "CRAM-MD5" => { + features.insert(Extension::Authentication(Mecanism::CramMd5)); + } _ => (), } } - }, + } _ => (), }; } - Ok(ServerInfo{ + Ok(ServerInfo { name: name, features: features, }) @@ -113,85 +123,98 @@ impl ServerInfo { #[cfg(test)] mod test { - use std::collections::HashSet; - + use std::collections::HashSet; + use super::{ServerInfo, Extension}; use authentication::Mecanism; use response::{Code, Response, Severity, Category}; - + #[test] fn test_extension_fmt() { - assert_eq!(format!("{}", Extension::EightBitMime), "8BITMIME".to_string()); - assert_eq!(format!("{}", Extension::Authentication(Mecanism::Plain)), "AUTH PLAIN".to_string()); + assert_eq!(format!("{}", Extension::EightBitMime), + "8BITMIME".to_string()); + assert_eq!(format!("{}", Extension::Authentication(Mecanism::Plain)), + "AUTH PLAIN".to_string()); } - + #[test] fn test_serverinfo_fmt() { - let mut eightbitmime = HashSet::new(); - assert!(eightbitmime.insert(Extension::EightBitMime)); - - assert_eq!(format!("{}", ServerInfo{ - name: "name".to_string(), - features: eightbitmime.clone() - }), "name with {EightBitMime}".to_string()); - + let mut eightbitmime = HashSet::new(); + assert!(eightbitmime.insert(Extension::EightBitMime)); + + assert_eq!(format!("{}", + ServerInfo { + name: "name".to_string(), + features: eightbitmime.clone(), + }), + "name with {EightBitMime}".to_string()); + let empty = HashSet::new(); - - assert_eq!(format!("{}", ServerInfo{ - name: "name".to_string(), - features: empty, - }), "name with no supported features".to_string()); - + + assert_eq!(format!("{}", + ServerInfo { + name: "name".to_string(), + features: empty, + }), + "name with no supported features".to_string()); + let mut plain = HashSet::new(); - assert!(plain.insert(Extension::Authentication(Mecanism::Plain))); - - assert_eq!(format!("{}", ServerInfo{ - name: "name".to_string(), - features: plain.clone() - }), "name with {Authentication(Plain)}".to_string()); + assert!(plain.insert(Extension::Authentication(Mecanism::Plain))); + + assert_eq!(format!("{}", + ServerInfo { + name: "name".to_string(), + features: plain.clone(), + }), + "name with {Authentication(Plain)}".to_string()); } - + #[test] fn test_serverinfo() { - let response = Response::new( - Code::new(Severity::PositiveCompletion, Category::Unspecified4, 1), - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ); - - let mut features = HashSet::new(); - assert!(features.insert(Extension::EightBitMime)); - - let server_info = ServerInfo { - name: "me".to_string(), - features: features, - }; - - assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info); - - assert!(server_info.supports_feature(&Extension::EightBitMime)); - assert!(!server_info.supports_feature(&Extension::StartTls)); - assert!(!server_info.supports_auth_mecanism(Mecanism::CramMd5)); - - let response2 = Response::new( - Code::new(Severity::PositiveCompletion, Category::Unspecified4, 1), - vec!["me".to_string(), "AUTH PLAIN CRAM-MD5 OTHER".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ); - - let mut features2 = HashSet::new(); - assert!(features2.insert(Extension::EightBitMime)); - assert!(features2.insert(Extension::Authentication(Mecanism::Plain))); - assert!(features2.insert(Extension::Authentication(Mecanism::CramMd5))); - - let server_info2 = ServerInfo { - name: "me".to_string(), - features: features2, - }; - - assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2); - - assert!(server_info2.supports_feature(&Extension::EightBitMime)); - assert!(server_info2.supports_auth_mecanism(Mecanism::Plain)); - assert!(server_info2.supports_auth_mecanism(Mecanism::CramMd5)); - assert!(!server_info2.supports_feature(&Extension::StartTls)); + let response = Response::new(Code::new(Severity::PositiveCompletion, + Category::Unspecified4, + 1), + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]); + + let mut features = HashSet::new(); + assert!(features.insert(Extension::EightBitMime)); + + let server_info = ServerInfo { + name: "me".to_string(), + features: features, + }; + + assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info); + + assert!(server_info.supports_feature(&Extension::EightBitMime)); + assert!(!server_info.supports_feature(&Extension::StartTls)); + assert!(!server_info.supports_auth_mecanism(Mecanism::CramMd5)); + + let response2 = Response::new(Code::new(Severity::PositiveCompletion, + Category::Unspecified4, + 1), + vec!["me".to_string(), + "AUTH PLAIN CRAM-MD5 OTHER".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]); + + let mut features2 = HashSet::new(); + assert!(features2.insert(Extension::EightBitMime)); + assert!(features2.insert(Extension::Authentication(Mecanism::Plain))); + assert!(features2.insert(Extension::Authentication(Mecanism::CramMd5))); + + let server_info2 = ServerInfo { + name: "me".to_string(), + features: features2, + }; + + assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2); + + assert!(server_info2.supports_feature(&Extension::EightBitMime)); + assert!(server_info2.supports_auth_mecanism(Mecanism::Plain)); + assert!(server_info2.supports_auth_mecanism(Mecanism::CramMd5)); + assert!(!server_info2.supports_feature(&Extension::StartTls)); } } diff --git a/src/lib.rs b/src/lib.rs index df30bee..33496a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,8 @@ //! //! This client should tend to follow [RFC 5321](https://tools.ietf.org/html/rfc5321), but is still //! a work in progress. It is designed to efficiently send emails from an application to a -//! relay email server, as it relies as much as possible on the relay server for sanity and RFC compliance -//! checks. +//! relay email server, as it relies as much as possible on the relay server for sanity and RFC +//! compliance checks. //! //! It implements the following extensions: //! @@ -135,7 +135,8 @@ #![deny(missing_docs)] -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate rustc_serialize as serialize; extern crate crypto; extern crate time; diff --git a/src/response.rs b/src/response.rs index 416fc6d..7e24c9c 100644 --- a/src/response.rs +++ b/src/response.rs @@ -36,14 +36,14 @@ impl FromStr for Severity { impl Display for Severity { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", - match *self { - PositiveCompletion => 2, - PositiveIntermediate => 3, - TransientNegativeCompletion => 4, - PermanentNegativeCompletion => 5, - } - ) + write!(f, + "{}", + match *self { + PositiveCompletion => 2, + PositiveIntermediate => 3, + TransientNegativeCompletion => 4, + PermanentNegativeCompletion => 5, + }) } } @@ -81,16 +81,16 @@ impl FromStr for Category { impl Display for Category { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", - match *self { - Syntax => 0, - Information => 1, - Connections => 2, - Unspecified3 => 3, - Unspecified4 => 4, - MailSystem => 5, - } - ) + write!(f, + "{}", + match *self { + Syntax => 0, + Information => 1, + Connections => 2, + Unspecified3 => 3, + Unspecified4 => 4, + MailSystem => 5, + }) } } @@ -111,8 +111,14 @@ impl FromStr for Code { #[inline] fn from_str(s: &str) -> result::Result { 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}), + 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(Error::ResponseParsingError("Could not parse response code")), } } else { @@ -144,7 +150,7 @@ pub struct ResponseParser { code: Option, /// Server response string (optional) /// Handle multiline responses - message: Vec + message: Vec, } impl ResponseParser { @@ -166,10 +172,11 @@ impl ResponseParser { match self.code { Some(ref code) => { if code.code() != line[0..3] { - return Err(Error::ResponseParsingError("Response code has changed during a reponse")); + return Err(Error::ResponseParsingError("Response code has changed during a \ + reponse")); } - }, - None => self.code = Some(try!(line[0..3].parse::())) + } + None => self.code = Some(try!(line[0..3].parse::())), } if line.len() > 4 { @@ -188,7 +195,8 @@ impl ResponseParser { pub fn response(self) -> SmtpResult { match self.code { Some(code) => Ok(Response::new(code, self.message)), - None => Err(Error::ResponseParsingError("Incomplete response, could not read response code")) + None => Err(Error::ResponseParsingError("Incomplete response, could not read \ + response code")), } } } @@ -202,7 +210,7 @@ pub struct Response { code: Code, /// Server response string (optional) /// Handle multiline responses - message: Vec + message: Vec, } impl Response { @@ -260,7 +268,7 @@ impl Response { false => match self.message[0].split_whitespace().next() { Some(word) => Some(word.to_string()), None => None, - } + }, } } } @@ -271,8 +279,10 @@ mod test { #[test] fn test_severity_from_str() { - assert_eq!("2".parse::().unwrap(), Severity::PositiveCompletion); - assert_eq!("4".parse::().unwrap(), Severity::TransientNegativeCompletion); + assert_eq!("2".parse::().unwrap(), + Severity::PositiveCompletion); + assert_eq!("4".parse::().unwrap(), + Severity::TransientNegativeCompletion); assert!("1".parse::().is_err()); } @@ -295,26 +305,24 @@ mod test { #[test] fn test_code_new() { - assert_eq!( - Code::new(Severity::TransientNegativeCompletion, Category::Connections, 0), - Code { - severity: Severity::TransientNegativeCompletion, - category: Category::Connections, - detail: 0, - } - ); + assert_eq!(Code::new(Severity::TransientNegativeCompletion, + Category::Connections, + 0), + Code { + severity: Severity::TransientNegativeCompletion, + category: Category::Connections, + detail: 0, + }); } #[test] fn test_code_from_str() { - assert_eq!( - "421".parse::().unwrap(), - Code { - severity: Severity::TransientNegativeCompletion, - category: Category::Connections, - detail: 1, - } - ); + assert_eq!("421".parse::().unwrap(), + Code { + severity: Severity::TransientNegativeCompletion, + category: Category::Connections, + detail: 1, + }); } #[test] @@ -330,36 +338,38 @@ mod test { #[test] fn test_response_new() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail: 1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ), Response { - code: Code { - severity: Severity::PositiveCompletion, - category: Category::Unspecified4, - detail: 1, - }, - message: vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()], - }); - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec![] - ), Response { - code: Code { - severity: Severity::PositiveCompletion, - category: Category::Unspecified4, - detail: 1, - }, - message: vec![], - }); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]), + Response { + code: Code { + severity: Severity::PositiveCompletion, + category: Category::Unspecified4, + detail: 1, + }, + message: vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()], + }); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec![]), + Response { + code: Code { + severity: Severity::PositiveCompletion, + category: Category::Unspecified4, + detail: 1, + }, + message: vec![], + }); } #[test] @@ -373,186 +383,206 @@ mod test { let response = parser.response().unwrap(); - assert_eq!( - response, - Response { - code: Code { - severity: Severity::PositiveCompletion, - category: Category::MailSystem, - detail: 0, - }, - message: vec!["me".to_string(), "8BITMIME".to_string(), - "SIZE 42".to_string(), "AUTH PLAIN CRAM-MD5".to_string()], - } - ); + assert_eq!(response, + Response { + code: Code { + severity: Severity::PositiveCompletion, + category: Category::MailSystem, + detail: 0, + }, + message: vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string(), + "AUTH PLAIN CRAM-MD5".to_string()], + }); } #[test] fn test_response_is_positive() { - assert!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).is_positive()); - assert!(! Response::new( - Code { - severity: "5".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).is_positive()); + assert!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .is_positive()); + assert!(!Response::new(Code { + severity: "5".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .is_positive()); } #[test] fn test_response_message() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).message(), vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()]); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .message(), + vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()]); let empty_message: Vec = vec![]; - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec![] - ).message(), empty_message); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec![]) + .message(), + empty_message); } #[test] fn test_response_severity() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).severity(), Severity::PositiveCompletion); - assert_eq!(Response::new( - Code { - severity: "5".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail: 1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).severity(), Severity::PermanentNegativeCompletion); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .severity(), + Severity::PositiveCompletion); + assert_eq!(Response::new(Code { + severity: "5".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .severity(), + Severity::PermanentNegativeCompletion); } #[test] fn test_response_category() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).category(), Category::Unspecified4); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .category(), + Category::Unspecified4); } #[test] fn test_response_detail() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).detail(), 1); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .detail(), + 1); } #[test] fn test_response_code() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).code(), "241"); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .code(), + "241"); } #[test] fn test_response_has_code() { - assert!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).has_code(241)); - assert!(! Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).has_code(251)); + assert!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .has_code(241)); + assert!(!Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .has_code(251)); } #[test] fn test_response_first_word() { - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).first_word(), Some("me".to_string())); - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["me mo".to_string(), "8BITMIME".to_string(), "SIZE 42".to_string()] - ).first_word(), Some("me".to_string())); - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec![] - ).first_word(), None); - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec![" ".to_string()] - ).first_word(), None); - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec![" ".to_string()] - ).first_word(), None); - assert_eq!(Response::new( - Code { - severity: "2".parse::().unwrap(), - category: "4".parse::().unwrap(), - detail:1, - }, - vec!["".to_string()] - ).first_word(), None); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .first_word(), + Some("me".to_string())); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["me mo".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string()]) + .first_word(), + Some("me".to_string())); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec![]) + .first_word(), + None); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec![" ".to_string()]) + .first_word(), + None); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec![" ".to_string()]) + .first_word(), + None); + assert_eq!(Response::new(Code { + severity: "2".parse::().unwrap(), + category: "4".parse::().unwrap(), + detail: 1, + }, + vec!["".to_string()]) + .first_word(), + None); } } diff --git a/src/sender.rs b/src/sender.rs index f56219d..c99b7be 100644 --- a/src/sender.rs +++ b/src/sender.rs @@ -33,20 +33,20 @@ pub struct SenderBuilder { impl SenderBuilder { /// Creates a new local SMTP client pub fn new(addr: A) -> Result { - let mut addresses = try!(addr.to_socket_addrs()); - - - match addresses.next() { - Some(addr) => Ok(SenderBuilder { - server_addr: addr, - credentials: None, - connection_reuse_count_limit: 100, - enable_connection_reuse: false, - hello_name: "localhost".to_string(), - authentication_mecanisms: vec![Mecanism::CramMd5, Mecanism::Plain], - }), - None => Err(From::from("Could nor resolve hostname")), - } + let mut addresses = try!(addr.to_socket_addrs()); + + + match addresses.next() { + Some(addr) => Ok(SenderBuilder { + server_addr: addr, + credentials: None, + connection_reuse_count_limit: 100, + enable_connection_reuse: false, + hello_name: "localhost".to_string(), + authentication_mecanisms: vec![Mecanism::CramMd5, Mecanism::Plain], + }), + None => Err(From::from("Could nor resolve hostname")), + } } /// Creates a new local SMTP client to port 25 @@ -77,7 +77,7 @@ impl SenderBuilder { self.credentials = Some((username.to_string(), password.to_string())); self } - + /// Set the authentication mecanisms pub fn authentication_mecanisms(mut self, mecanisms: Vec) -> SenderBuilder { self.authentication_mecanisms = mecanisms; @@ -135,7 +135,7 @@ impl Sender { /// It does not connects to the server, but only creates the `Sender` pub fn new(builder: SenderBuilder) -> Sender { let client: Client = Client::new(builder.server_addr).unwrap(); - Sender{ + Sender { client: client, server_info: None, client_info: builder, @@ -185,12 +185,12 @@ impl Sender { Error::PermanentError(ref response) if response.has_code(550) => { match self.client.helo(&self.client_info.hello_name) { Ok(response) => response, - Err(error) => try_smtp!(Err(error), self) + Err(error) => try_smtp!(Err(error), self), } - }, + } _ => { try_smtp!(Err(error), self) - }, + } }, }; @@ -203,19 +203,19 @@ impl Sender { if self.client_info.credentials.is_some() && self.state.connection_reuse_count == 0 { let (username, password) = self.client_info.credentials.clone().unwrap(); - let mut found = false; + let mut found = false; - for mecanism in self.client_info.authentication_mecanisms.clone() { - if self.server_info.as_ref().unwrap().supports_auth_mecanism(mecanism) { - found = true; - let result = self.client.auth(mecanism, &username, &password); - try_smtp!(result, self); - } - } - - if !found { - debug!("No supported authentication mecanisms available"); - } + for mecanism in self.client_info.authentication_mecanisms.clone() { + if self.server_info.as_ref().unwrap().supports_auth_mecanism(mecanism) { + found = true; + let result = self.client.auth(mecanism, &username, &password); + try_smtp!(result, self); + } + } + + if !found { + debug!("No supported authentication mecanisms available"); + } } let current_message = try!(email.message_id().ok_or("Missing Message-ID")); @@ -224,7 +224,10 @@ impl Sender { let message = try!(email.message().ok_or("Missing message")); // Mail - let mail_options = match self.server_info.as_ref().unwrap().supports_feature(&Extension::EightBitMime) { + let mail_options = match self.server_info + .as_ref() + .unwrap() + .supports_feature(&Extension::EightBitMime) { true => Some("BODY=8BITMIME"), false => None, }; @@ -252,15 +255,22 @@ impl Sender { self.state.connection_reuse_count = self.state.connection_reuse_count + 1; // Log the message - info!("{}: conn_use={}, size={}, status=sent ({})", current_message, - self.state.connection_reuse_count, message.len(), - result.as_ref().ok().unwrap().message().iter().next().unwrap_or(&"no response".to_string()) - ); + info!("{}: conn_use={}, size={}, status=sent ({})", + current_message, + self.state.connection_reuse_count, + message.len(), + result.as_ref() + .ok() + .unwrap() + .message() + .iter() + .next() + .unwrap_or(&"no response".to_string())); } // Test if we can reuse the existing connection if (!self.client_info.enable_connection_reuse) || - (self.state.connection_reuse_count >= self.client_info.connection_reuse_count_limit) { + (self.state.connection_reuse_count >= self.client_info.connection_reuse_count_limit) { self.reset(); }