diff --git a/src/email/mod.rs b/src/email/mod.rs index 8876ea2..efd8983 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -382,10 +382,9 @@ impl EmailBuilder { let message_id = Uuid::new_v4(); - match Header::new_with_value("Message-ID".to_string(), - format!("<{}.lettre@localhost>", message_id)) { - Ok(header) => self.message.add_header(header), - Err(_) => (), + if let Ok(header) = Header::new_with_value("Message-ID".to_string(), + format!("<{}.lettre@localhost>", message_id)) { + self.message.add_header(header) } Ok(Email { diff --git a/src/transport/error.rs b/src/transport/error.rs index 2771673..6d08c93 100644 --- a/src/transport/error.rs +++ b/src/transport/error.rs @@ -15,21 +15,21 @@ pub enum Error { /// Transient SMTP error, 4xx reply code /// /// [RFC 5321, section 4.2.1](https://tools.ietf.org/html/rfc5321#section-4.2.1) - TransientError(Response), + Transient(Response), /// Permanent SMTP error, 5xx reply code /// /// [RFC 5321, section 4.2.1](https://tools.ietf.org/html/rfc5321#section-4.2.1) - PermanentError(Response), + Permanent(Response), /// Error parsing a response - ResponseParsingError(&'static str), + ResponseParsing(&'static str), /// Error parsing a base64 string in response - ChallengeParsingError(FromBase64Error), + ChallengeParsing(FromBase64Error), /// Internal client error - ClientError(&'static str), + Client(&'static str), /// DNS resolution error - ResolutionError, + Resolution, /// IO error - IoError(io::Error), + Io(io::Error), } impl Display for Error { @@ -41,19 +41,19 @@ impl Display for Error { impl StdError for Error { fn description(&self) -> &str { 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", - ChallengeParsingError(_) => "an error occured while parsing a CRAM-MD5 challenge", - ResolutionError => "could not resolve hostname", - ClientError(_) => "an unknown error occured", - IoError(_) => "an I/O error occured", + Transient(_) => "a transient error occured during the SMTP transaction", + Permanent(_) => "a permanent error occured during the SMTP transaction", + ResponseParsing(_) => "an error occured while parsing an SMTP response", + ChallengeParsing(_) => "an error occured while parsing a CRAM-MD5 challenge", + Resolution => "could not resolve hostname", + Client(_) => "an unknown error occured", + Io(_) => "an I/O error occured", } } fn cause(&self) -> Option<&StdError> { match *self { - IoError(ref err) => Some(&*err as &StdError), + Io(ref err) => Some(&*err as &StdError), _ => None, } } @@ -61,23 +61,23 @@ impl StdError for Error { impl From for Error { fn from(err: io::Error) -> Error { - IoError(err) + Io(err) } } impl From for Error { fn from(response: Response) -> Error { match response.severity() { - Severity::TransientNegativeCompletion => TransientError(response), - Severity::PermanentNegativeCompletion => PermanentError(response), - _ => ClientError("Unknown error code"), + Severity::TransientNegativeCompletion => Transient(response), + Severity::PermanentNegativeCompletion => Permanent(response), + _ => Client("Unknown error code"), } } } impl From<&'static str> for Error { fn from(string: &'static str) -> Error { - ClientError(string) + Client(string) } } diff --git a/src/transport/file/mod.rs b/src/transport/file/mod.rs index 3ac6dfb..2a179fa 100644 --- a/src/transport/file/mod.rs +++ b/src/transport/file/mod.rs @@ -38,7 +38,7 @@ impl EmailTransport for FileEmailTransport { email.to_addresses().join("> to=<")); try!(f.write_all(log_line.as_bytes())); - try!(f.write_all(format!("{}", email.message()).as_bytes())); + try!(f.write_all(email.message().clone().as_bytes())); info!("{} status=", log_line); diff --git a/src/transport/sendmail/mod.rs b/src/transport/sendmail/mod.rs deleted file mode 100644 index 8198b20..0000000 --- a/src/transport/sendmail/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! This transport creates a file for each email, containing the enveloppe information and the email -//! itself. - -use transport::EmailTransport; -use transport::error::EmailResult; -use transport::smtp::response::Response; -use transport::smtp::response::{Category, Code, Severity}; -use email::SendableEmail; - -/// Writes the content and the enveloppe information to a file -pub struct SendmailEmailTransport { - command: String, -} - -impl SendmailEmailTransport { - /// Creates a new transport to the default "sendmail" command - pub fn new() -> SendmailEmailTransport { - SendmailEmailTransport { command: "sendmail".to_string() } - } - - /// Creates a new transport with a custom sendmail command - pub fn new_with_command(command: &str) -> SendmailEmailTransport { - SendmailEmailTransport { command: command.to_string() } - } -} - -impl EmailTransport for SendmailEmailTransport { - fn send(&mut self, email: T) -> EmailResult { - - - // Build TO list - // Set FROM - // Send content - - let sendmail_sh_frist_half = "sendmail ".to_string() + &to_address; - let sendmail_sh_second_half = " < email.txt".to_string(); - let sendmail_sh = sendmail_sh_frist_half + &sendmail_sh_second_half; - - let output = Command::new(self.command) - .arg("-c") - .arg(sendmail_sh) - .output() - .unwrap_or_else(|e| panic!("failed to execute process: {}", e)); - - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - - - - let mut file = self.path.clone(); - file.push(format!("{}.txt", email.message_id())); - - let mut f = try!(File::create(file.as_path())); - - let log_line = format!("{}: from=<{}> to=<{}>\n", - email.message_id(), - email.from_address(), - email.to_addresses().join("> to=<")); - - try!(f.write_all(log_line.as_bytes())); - try!(f.write_all(format!("{}", email.message()).as_bytes())); - - info!("{} status=", log_line); - - Ok(Response::new(Code::new(Severity::PositiveCompletion, Category::MailSystem, 0), - vec![format!("Ok: email written to {}", - file.to_str().unwrap_or("non-UTF-8 path"))])) - } - - fn close(&mut self) { - () - } -} diff --git a/src/transport/smtp/authentication.rs b/src/transport/smtp/authentication.rs index 5f69e58..8d30373 100644 --- a/src/transport/smtp/authentication.rs +++ b/src/transport/smtp/authentication.rs @@ -53,9 +53,7 @@ impl Mechanism { match *self { Mechanism::Plain => { match challenge { - Some(_) => { - Err(Error::ClientError("This mechanism does not expect a challenge")) - } + Some(_) => Err(Error::Client("This mechanism does not expect a challenge")), None => { Ok(format!("{}{}{}{}", NUL, username, NUL, password) .as_bytes() @@ -66,14 +64,12 @@ impl Mechanism { Mechanism::CramMd5 => { let encoded_challenge = match challenge { Some(challenge) => challenge, - None => { - return Err(Error::ClientError("This mechanism does expect a challenge")) - } + None => return Err(Error::Client("This mechanism does expect a challenge")), }; let decoded_challenge = match encoded_challenge.from_base64() { Ok(challenge) => challenge, - Err(error) => return Err(Error::ChallengeParsingError(error)), + Err(error) => return Err(Error::ChallengeParsing(error)), }; let mut hmac = Hmac::new(Md5::new(), password.as_bytes()); diff --git a/src/transport/smtp/client/mod.rs b/src/transport/smtp/client/mod.rs index 1217bf7..77bc329 100644 --- a/src/transport/smtp/client/mod.rs +++ b/src/transport/smtp/client/mod.rs @@ -22,7 +22,7 @@ pub mod net; /// Reference : https://tools.ietf.org/html/rfc5321#page-62 (4.5.2. Transparency) #[inline] fn escape_dot(string: &str) -> String { - if string.starts_with(".") { + if string.starts_with('.') { format!(".{}", string) } else { string.to_string() @@ -193,7 +193,7 @@ impl Client { } 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")), + None => return Err(Error::ResponseParsing("Could not read CRAM challenge")), }; debug!("CRAM challenge: {}", encoded_challenge); @@ -202,7 +202,7 @@ impl Client { password, Some(&encoded_challenge))); - self.command(&format!("{}", cram_response)) + self.command(&cram_response.clone()) } } @@ -233,7 +233,7 @@ impl Client { /// Gets the SMTP response fn get_reply(&mut self) -> EmailResult { - let mut parser = ResponseParser::new(); + let mut parser = ResponseParser::default(); let mut line = String::new(); try!(self.stream.as_mut().unwrap().read_line(&mut line)); @@ -247,10 +247,12 @@ impl Client { let response = try!(parser.response()); - match response.is_positive() { - true => Ok(response), - false => Err(From::from(response)), + if response.is_positive() { + Ok(response) + } else { + Err(From::from(response)) } + } } diff --git a/src/transport/smtp/client/net.rs b/src/transport/smtp/client/net.rs index d771f26..c4a0e6f 100644 --- a/src/transport/smtp/client/net.rs +++ b/src/transport/smtp/client/net.rs @@ -66,9 +66,9 @@ pub enum NetworkStream { impl Clone for NetworkStream { #[inline] fn clone(&self) -> NetworkStream { - match self { - &NetworkStream::Plain(ref stream) => NetworkStream::Plain(stream.try_clone().unwrap()), - &NetworkStream::Ssl(ref stream) => NetworkStream::Ssl(stream.try_clone().unwrap()), + match *self { + NetworkStream::Plain(ref stream) => NetworkStream::Plain(stream.try_clone().unwrap()), + NetworkStream::Ssl(ref stream) => NetworkStream::Ssl(stream.try_clone().unwrap()), } } } diff --git a/src/transport/smtp/extension.rs b/src/transport/smtp/extension.rs index a53e8ce..c62de01 100644 --- a/src/transport/smtp/extension.rs +++ b/src/transport/smtp/extension.rs @@ -69,7 +69,7 @@ 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::ResponseParsing("Could not read server name")), }; let mut features: HashSet = HashSet::new(); @@ -77,7 +77,7 @@ impl ServerInfo { for line in response.message() { let splitted: Vec<&str> = line.split_whitespace().collect(); - let _ = match splitted[0] { + match splitted[0] { "8BITMIME" => { features.insert(Extension::EightBitMime); } diff --git a/src/transport/smtp/mod.rs b/src/transport/smtp/mod.rs index c0e0932..d67eab7 100644 --- a/src/transport/smtp/mod.rs +++ b/src/transport/smtp/mod.rs @@ -87,7 +87,7 @@ pub struct SmtpTransportBuilder { authentication_mechanism: Option, } -/// Builder for the SMTP SmtpTransport +/// Builder for the SMTP `SmtpTransport` impl SmtpTransportBuilder { /// Creates a new local SMTP client pub fn new(addr: A) -> Result { @@ -280,10 +280,8 @@ impl EmailTransport for SmtpTransport { let message = email.message(); // Check if the connection is still available - if self.state.connection_reuse_count > 0 { - if !self.client.is_connected() { - self.reset(); - } + if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) { + self.reset(); } if self.state.connection_reuse_count == 0 { @@ -329,12 +327,13 @@ impl EmailTransport for SmtpTransport { let accepted_mechanisms = match self.client_info.authentication_mechanism { Some(mechanism) => vec![mechanism], None => { - match self.client.is_encrypted() { + if self.client.is_encrypted() { // If encrypted, allow all mechanisms, with a preference for the // simplest - true => vec![Mechanism::Plain, Mechanism::CramMd5], + vec![Mechanism::Plain, Mechanism::CramMd5] + } else { // If not encrypted, do not all clear-text passwords - false => vec![Mechanism::CramMd5], + vec![Mechanism::CramMd5] } } }; @@ -373,7 +372,7 @@ impl EmailTransport for SmtpTransport { info!("{}: from=<{}>", message_id, from_address); // Recipient - for to_address in to_addresses.iter() { + for to_address in &to_addresses { try_smtp!(self.client.rcpt(&to_address), self); // Log the rcpt command info!("{}: to=<{}>", message_id, to_address); @@ -387,7 +386,7 @@ impl EmailTransport for SmtpTransport { if result.is_ok() { // Increment the connection reuse counter - self.state.connection_reuse_count = self.state.connection_reuse_count + 1; + self.state.connection_reuse_count += 1; // Log the message info!("{}: conn_use={}, size={}, status=sent ({})", diff --git a/src/transport/smtp/response.rs b/src/transport/smtp/response.rs index 0988ba0..50687c3 100644 --- a/src/transport/smtp/response.rs +++ b/src/transport/smtp/response.rs @@ -30,7 +30,7 @@ impl FromStr for Severity { "3" => Ok(PositiveIntermediate), "4" => Ok(TransientNegativeCompletion), "5" => Ok(PermanentNegativeCompletion), - _ => Err(Error::ResponseParsingError("First digit must be between 2 and 5")), + _ => Err(Error::ResponseParsing("First digit must be between 2 and 5")), } } } @@ -75,7 +75,7 @@ impl FromStr for Category { "3" => Ok(Unspecified3), "4" => Ok(Unspecified4), "5" => Ok(MailSystem), - _ => Err(Error::ResponseParsingError("Second digit must be between 0 and 5")), + _ => Err(Error::ResponseParsing("Second digit must be between 0 and 5")), } } } @@ -122,10 +122,10 @@ impl FromStr for Code { detail: detail, }) } - _ => return Err(Error::ResponseParsingError("Could not parse response code")), + _ => Err(Error::ResponseParsing("Could not parse response code")), } } else { - Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)")) + Err(Error::ResponseParsing("Wrong code length (should be 3 digit)")) } } } @@ -147,7 +147,7 @@ impl Code { } /// Parses an SMTP response -#[derive(PartialEq,Eq,Clone,Debug)] +#[derive(PartialEq,Eq,Clone,Debug,Default)] pub struct ResponseParser { /// Response code code: Option, @@ -157,25 +157,17 @@ pub struct ResponseParser { } impl ResponseParser { - /// Creates a new parser - pub fn new() -> ResponseParser { - ResponseParser { - code: None, - message: vec![], - } - } - /// Parses a line and return a `bool` indicating if there are more lines to come pub fn read_line(&mut self, line: &str) -> result::Result { if line.len() < 3 { - return Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)")); + return Err(Error::ResponseParsing("Wrong code length (should be 3 digit)")); } match self.code { Some(ref code) => { if code.code() != line[0..3] { - return Err(Error::ResponseParsingError("Response code has changed during a \ + return Err(Error::ResponseParsing("Response code has changed during a \ reponse")); } } @@ -184,7 +176,7 @@ impl ResponseParser { if line.len() > 4 { self.message.push(line[4..].to_string()); - Ok(line.as_bytes()[3] == '-' as u8) + Ok(line.as_bytes()[3] == b'-') } else { Ok(false) } @@ -195,7 +187,7 @@ impl ResponseParser { match self.code { Some(code) => Ok(Response::new(code, self.message)), None => { - Err(Error::ResponseParsingError("Incomplete response, could not read response \ + Err(Error::ResponseParsing("Incomplete response, could not read response \ code")) } } @@ -264,15 +256,15 @@ impl Response { /// Returns only the first word of the message if possible pub fn first_word(&self) -> Option { - match self.message.is_empty() { - true => None, - false => { - match self.message[0].split_whitespace().next() { - Some(word) => Some(word.to_string()), - None => None, - } + if self.message.is_empty() { + None + } else { + match self.message[0].split_whitespace().next() { + Some(word) => Some(word.to_string()), + None => None, } } + } } @@ -377,7 +369,7 @@ mod test { #[test] fn test_response_parser() { - let mut parser = ResponseParser::new(); + let mut parser = ResponseParser::default(); assert!(parser.read_line("250-me").unwrap()); assert!(parser.read_line("250-8BITMIME").unwrap());