From eb4e7f98294d4b757e639412a01ade986f8d8ff4 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Sat, 25 Nov 2017 11:55:05 +0100 Subject: [PATCH] Change rustfmt style --- lettre/benches/transport_smtp.rs | 39 ++- lettre/examples/smtp.rs | 17 +- lettre/src/file/mod.rs | 14 +- lettre/src/lib.rs | 43 ++- lettre/src/sendmail/mod.rs | 31 +- lettre/src/smtp/authentication.rs | 82 +++--- lettre/src/smtp/client/mock.rs | 12 +- lettre/src/smtp/client/mod.rs | 65 ++--- lettre/src/smtp/client/net.rs | 38 ++- lettre/src/smtp/commands.rs | 182 +++++------- lettre/src/smtp/extension.rs | 181 +++++------- lettre/src/smtp/mod.rs | 181 +++++------- lettre/src/smtp/response.rs | 203 +++++-------- lettre/src/smtp/util.rs | 11 +- lettre/src/stub/mod.rs | 11 +- lettre/tests/transport_file.rs | 18 +- lettre/tests/transport_sendmail.rs | 10 +- lettre/tests/transport_smtp.rs | 15 +- lettre/tests/transport_stub.rs | 10 +- lettre_email/examples/smtp.rs | 5 +- lettre_email/src/lib.rs | 351 +++++++++-------------- rustfmt.toml | 4 + website/content/sending-messages/smtp.md | 10 +- 23 files changed, 611 insertions(+), 922 deletions(-) diff --git a/lettre/benches/transport_smtp.rs b/lettre/benches/transport_smtp.rs index 560acbe..4d873c6 100644 --- a/lettre/benches/transport_smtp.rs +++ b/lettre/benches/transport_smtp.rs @@ -9,19 +9,17 @@ use lettre::smtp::ConnectionReuseParameters; #[bench] fn bench_simple_send(b: &mut test::Bencher) { - let mut sender = SmtpTransport::builder("127.0.0.1:2525", ClientSecurity::None) - .unwrap() - .build(); + let mut sender = SmtpTransport::builder("127.0.0.1:2525", ClientSecurity::None).unwrap() + .build(); b.iter(|| { - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "id".to_string(), - "Hello world".to_string(), - ); - let result = sender.send(&email); - assert!(result.is_ok()); - }); + let email = + SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "id".to_string(), + "Hello world".to_string()); + let result = sender.send(&email); + assert!(result.is_ok()); + }); } #[bench] @@ -31,14 +29,13 @@ fn bench_reuse_send(b: &mut test::Bencher) { .connection_reuse(ConnectionReuseParameters::ReuseUnlimited) .build(); b.iter(|| { - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "id".to_string(), - "Hello world".to_string(), - ); - let result = sender.send(&email); - assert!(result.is_ok()); - }); + let email = + SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "id".to_string(), + "Hello world".to_string()); + let result = sender.send(&email); + assert!(result.is_ok()); + }); sender.close() } diff --git a/lettre/examples/smtp.rs b/lettre/examples/smtp.rs index bf1c93f..3b8ba1d 100644 --- a/lettre/examples/smtp.rs +++ b/lettre/examples/smtp.rs @@ -1,22 +1,19 @@ -extern crate lettre; extern crate env_logger; +extern crate lettre; use lettre::{EmailAddress, EmailTransport, SimpleSendableEmail, SmtpTransport}; fn main() { env_logger::init().unwrap(); - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "file_id".to_string(), - "Hello ß☺ example".to_string(), - ); + let email = SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "file_id".to_string(), + "Hello ß☺ example".to_string()); // Open a local connection on port 25 - let mut mailer = SmtpTransport::builder_unencrypted_localhost() - .unwrap() - .build(); + let mut mailer = SmtpTransport::builder_unencrypted_localhost().unwrap() + .build(); // Send the email let result = mailer.send(&email); diff --git a/lettre/src/file/mod.rs b/lettre/src/file/mod.rs index 11ae012..6ef89b6 100644 --- a/lettre/src/file/mod.rs +++ b/lettre/src/file/mod.rs @@ -70,16 +70,12 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, FileResult> for FileEmailTransport let mut message_content = String::new(); let _ = email.message().read_to_string(&mut message_content); - let simple_email = SimpleSendableEmail::new( - email.from().clone(), - email.to().clone(), - email.message_id().clone(), - message_content, - ); + let simple_email = SimpleSendableEmail::new(email.from().clone(), + email.to().clone(), + email.message_id().clone(), + message_content); - f.write_all( - serde_json::to_string(&simple_email)?.as_bytes(), - )?; + f.write_all(serde_json::to_string(&simple_email)?.as_bytes())?; Ok(()) } diff --git a/lettre/src/lib.rs b/lettre/src/lib.rs index 2d31d7a..e462dfa 100644 --- a/lettre/src/lib.rs +++ b/lettre/src/lib.rs @@ -7,28 +7,28 @@ #![doc(html_root_url = "https://docs.rs/lettre/0.7.0")] #![deny(missing_docs, unsafe_code, unstable_features, warnings)] -#[macro_use] -extern crate log; -#[cfg(feature = "crammd5-auth")] -extern crate hex; -#[cfg(feature = "crammd5-auth")] -extern crate crypto; #[cfg(feature = "smtp-transport")] extern crate base64; #[cfg(feature = "smtp-transport")] extern crate bufstream; +#[cfg(feature = "crammd5-auth")] +extern crate crypto; +#[cfg(feature = "crammd5-auth")] +extern crate hex; +#[cfg(feature = "smtp-transport")] +extern crate hostname; +#[macro_use] +extern crate log; #[cfg(feature = "smtp-transport")] extern crate native_tls; #[cfg(feature = "smtp-transport")] -extern crate hostname; -#[cfg(feature = "file-transport")] -extern crate serde_json; +#[macro_use] +extern crate nom; #[cfg(feature = "serde-impls")] #[macro_use] extern crate serde_derive; -#[cfg(feature = "smtp-transport")] -#[macro_use] -extern crate nom; +#[cfg(feature = "file-transport")] +extern crate serde_json; #[cfg(feature = "smtp-transport")] pub mod smtp; @@ -101,18 +101,15 @@ pub struct SimpleSendableEmail { impl SimpleSendableEmail { /// Returns a new email - pub fn new( - from_address: EmailAddress, - to_addresses: Vec, - message_id: String, - message: String, - ) -> SimpleSendableEmail { - SimpleSendableEmail { - from: from_address, - to: to_addresses, + pub fn new(from_address: EmailAddress, + to_addresses: Vec, + message_id: String, + message: String) + -> SimpleSendableEmail { + SimpleSendableEmail { from: from_address, + to: to_addresses, message_id: message_id, - message: message.into_bytes(), - } + message: message.into_bytes(), } } } diff --git a/lettre/src/sendmail/mod.rs b/lettre/src/sendmail/mod.rs index 6437f42..ff32105 100644 --- a/lettre/src/sendmail/mod.rs +++ b/lettre/src/sendmail/mod.rs @@ -33,12 +33,12 @@ pub struct SendmailTransport { impl SendmailTransport { /// Creates a new transport with the default `/usr/sbin/sendmail` command pub fn new() -> SendmailTransport { - SendmailTransport { command: "/usr/sbin/sendmail".to_string() } + SendmailTransport { command: "/usr/sbin/sendmail".to_string(), } } /// Creates a new transport to the given sendmail command pub fn new_with_command>(command: S) -> SendmailTransport { - SendmailTransport { command: command.into() } + SendmailTransport { command: command.into(), } } } @@ -46,25 +46,22 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SendmailResult> for SendmailTranspo fn send + 'a>(&mut self, email: &'a U) -> SendmailResult { // Spawn the sendmail command let to_addresses: Vec = email.to().iter().map(|x| x.to_string()).collect(); - let mut process = Command::new(&self.command) - .args( - &[ - "-i", - "-f", - &email.from().to_string(), - &to_addresses.join(" "), - ], - ) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; + let mut process = Command::new(&self.command).args(&["-i", + "-f", + &email.from().to_string(), + &to_addresses.join(" ")]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn()?; let mut message_content = String::new(); let _ = email.message().read_to_string(&mut message_content); - match process.stdin.as_mut().unwrap().write_all( - message_content.as_bytes(), - ) { + match process.stdin + .as_mut() + .unwrap() + .write_all(message_content.as_bytes()) + { Ok(_) => (), Err(error) => return Err(From::from(error)), } diff --git a/lettre/src/smtp/authentication.rs b/lettre/src/smtp/authentication.rs index 9db9fa5..c4763bc 100644 --- a/lettre/src/smtp/authentication.rs +++ b/lettre/src/smtp/authentication.rs @@ -15,21 +15,20 @@ use std::fmt::{self, Display, Formatter}; /// Accepted authentication mecanisms on an encrypted connection /// Trying LOGIN last as it is deprecated. #[cfg(feature = "crammd5-auth")] -pub const DEFAULT_ENCRYPTED_MECHANISMS: &'static [Mechanism] = +pub const DEFAULT_ENCRYPTED_MECHANISMS: &[Mechanism] = &[Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login]; /// Accepted authentication mecanisms on an encrypted connection /// Trying LOGIN last as it is deprecated. #[cfg(not(feature = "crammd5-auth"))] -pub const DEFAULT_ENCRYPTED_MECHANISMS: &'static [Mechanism] = - &[Mechanism::Plain, Mechanism::Login]; +pub const DEFAULT_ENCRYPTED_MECHANISMS: &[Mechanism] = &[Mechanism::Plain, Mechanism::Login]; /// Accepted authentication mecanisms on an unencrypted connection #[cfg(feature = "crammd5-auth")] -pub const DEFAULT_UNENCRYPTED_MECHANISMS: &'static [Mechanism] = &[Mechanism::CramMd5]; +pub const DEFAULT_UNENCRYPTED_MECHANISMS: &[Mechanism] = &[Mechanism::CramMd5]; /// Accepted authentication mecanisms on an unencrypted connection /// When CRAMMD5 support is not enabled, no mechanisms are allowed. #[cfg(not(feature = "crammd5-auth"))] -pub const DEFAULT_UNENCRYPTED_MECHANISMS: &'static [Mechanism] = &[]; +pub const DEFAULT_UNENCRYPTED_MECHANISMS: &[Mechanism] = &[]; /// Convertable to user credentials @@ -61,10 +60,8 @@ pub struct Credentials { impl Credentials { /// Create a `Credentials` struct from username and password pub fn new(username: String, password: String) -> Credentials { - Credentials { - username: username, - password: password, - } + Credentials { username: username, + password: password, } } } @@ -86,16 +83,12 @@ pub enum Mechanism { impl Display for Mechanism { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!( - f, - "{}", - match *self { - Mechanism::Plain => "PLAIN", - Mechanism::Login => "LOGIN", - #[cfg(feature = "crammd5-auth")] - Mechanism::CramMd5 => "CRAM-MD5", - } - ) + write!(f, "{}", match *self { + Mechanism::Plain => "PLAIN", + Mechanism::Login => "LOGIN", + #[cfg(feature = "crammd5-auth")] + Mechanism::CramMd5 => "CRAM-MD5", + }) } } @@ -113,22 +106,21 @@ impl Mechanism { /// Returns the string to send to the server, using the provided username, password and /// challenge in some cases - pub fn response( - &self, - credentials: &Credentials, - challenge: Option<&str>, - ) -> Result { + pub fn response(&self, + credentials: &Credentials, + challenge: Option<&str>) + -> Result { match *self { Mechanism::Plain => { match challenge { Some(_) => Err(Error::Client("This mechanism does not expect a challenge")), - None => Ok(format!( - "{}{}{}{}", - NUL, - credentials.username, - NUL, - credentials.password - )), + None => { + Ok(format!("{}{}{}{}", + NUL, + credentials.username, + NUL, + credentials.password)) + } } } Mechanism::Login => { @@ -157,11 +149,9 @@ impl Mechanism { let mut hmac = Hmac::new(Md5::new(), credentials.password.as_bytes()); hmac.input(decoded_challenge.as_bytes()); - Ok(format!( - "{} {}", - credentials.username, - hex::encode(hmac.result().code()) - )) + Ok(format!("{} {}", + credentials.username, + hex::encode(hmac.result().code()))) } } } @@ -177,10 +167,8 @@ mod test { let credentials = Credentials::new("username".to_string(), "password".to_string()); - assert_eq!( - mechanism.response(&credentials, None).unwrap(), - "\u{0}username\u{0}password" - ); + assert_eq!(mechanism.response(&credentials, None).unwrap(), + "\u{0}username\u{0}password"); assert!(mechanism.response(&credentials, Some("test")).is_err()); } @@ -190,14 +178,10 @@ mod test { let credentials = Credentials::new("alice".to_string(), "wonderland".to_string()); - assert_eq!( - mechanism.response(&credentials, Some("Username")).unwrap(), - "alice" - ); - assert_eq!( - mechanism.response(&credentials, Some("Password")).unwrap(), - "wonderland" - ); + assert_eq!(mechanism.response(&credentials, Some("Username")).unwrap(), + "alice"); + assert_eq!(mechanism.response(&credentials, Some("Password")).unwrap(), + "wonderland"); assert!(mechanism.response(&credentials, None).is_err()); } @@ -212,7 +196,7 @@ mod test { mechanism .response( &credentials, - Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg=="), + Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==") ) .unwrap(), "alice a540ebe4ef2304070bbc3c456c1f64c0" diff --git a/lettre/src/smtp/client/mock.rs b/lettre/src/smtp/client/mock.rs index e7989af..8104a4c 100644 --- a/lettre/src/smtp/client/mock.rs +++ b/lettre/src/smtp/client/mock.rs @@ -20,17 +20,13 @@ impl Default for MockStream { impl MockStream { pub fn new() -> MockStream { - MockStream { - reader: Arc::new(Mutex::new(MockCursor::new(Vec::new()))), - writer: Arc::new(Mutex::new(MockCursor::new(Vec::new()))), - } + MockStream { reader: Arc::new(Mutex::new(MockCursor::new(Vec::new()))), + writer: Arc::new(Mutex::new(MockCursor::new(Vec::new()))), } } pub fn with_vec(vec: Vec) -> MockStream { - MockStream { - reader: Arc::new(Mutex::new(MockCursor::new(vec))), - writer: Arc::new(Mutex::new(MockCursor::new(Vec::new()))), - } + MockStream { reader: Arc::new(Mutex::new(MockCursor::new(vec))), + writer: Arc::new(Mutex::new(MockCursor::new(Vec::new()))), } } pub fn take_vec(&mut self) -> Vec { diff --git a/lettre/src/smtp/client/mod.rs b/lettre/src/smtp/client/mod.rs index 2844e4b..7be3e8b 100644 --- a/lettre/src/smtp/client/mod.rs +++ b/lettre/src/smtp/client/mod.rs @@ -64,7 +64,6 @@ impl ClientCodec { } Ok(buf.write_all(&frame[start..])?) } - } } } @@ -102,7 +101,7 @@ impl Client { impl Client { /// Closes the SMTP transaction if possible pub fn close(&mut self) { - let _ = self.smtp_command(QuitCommand); + let _ = self.command(QuitCommand); self.stream = None; } @@ -140,11 +139,10 @@ impl Client { } /// Connects to the configured server - pub fn connect( - &mut self, - addr: &A, - tls_parameters: Option<&ClientTlsParameters>, - ) -> SmtpResult { + pub fn connect(&mut self, + addr: &A, + tls_parameters: Option<&ClientTlsParameters>) + -> SmtpResult { // Connect should not be called when the client is already connected if self.stream.is_some() { return_err!("The connection is already established", self); @@ -162,30 +160,26 @@ impl Client { // Try to connect self.set_stream(Connector::connect(&server_addr, tls_parameters)?); - self.get_reply() + self.read_response() } /// Checks if the server is connected using the NOOP SMTP command #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] pub fn is_connected(&mut self) -> bool { - self.smtp_command(NoopCommand).is_ok() + self.command(NoopCommand).is_ok() } /// Sends an AUTH command with the given mechanism, and handles challenge if needed pub fn auth(&mut self, mechanism: Mechanism, credentials: &Credentials) -> SmtpResult { // TODO let mut challenges = 10; - let mut response = self.smtp_command( - AuthCommand::new(mechanism, credentials.clone(), None)?, - )?; + let mut response = self.command(AuthCommand::new(mechanism, credentials.clone(), None)?)?; while challenges > 0 && response.has_code(334) { challenges -= 1; - response = self.smtp_command(AuthCommand::new_from_response( - mechanism, - credentials.clone(), - &response, - )?)?; + response = self.command(AuthCommand::new_from_response(mechanism, + credentials.clone(), + &response)?)?; } if challenges == 0 { @@ -217,21 +211,21 @@ impl Client { break; } - self.write_server(out_buf.as_slice())?; + self.write(out_buf.as_slice())?; } - self.write_server(MESSAGE_ENDING.as_bytes())?; - self.get_reply() + self.write(MESSAGE_ENDING.as_bytes())?; + self.read_response() } /// Sends an SMTP command - pub fn smtp_command(&mut self, command: C) -> SmtpResult { - self.write_server(command.to_string().as_bytes())?; - self.get_reply() + pub fn command(&mut self, command: C) -> SmtpResult { + self.write(command.to_string().as_bytes())?; + self.read_response() } /// Writes a string to the server - fn write_server(&mut self, string: &[u8]) -> Result<(), Error> { + fn write(&mut self, string: &[u8]) -> Result<(), Error> { if self.stream.is_none() { return Err(From::from("Connection closed")); } @@ -239,16 +233,13 @@ impl Client { self.stream.as_mut().unwrap().write_all(string)?; self.stream.as_mut().unwrap().flush()?; - debug!( - "Wrote: {}", - escape_crlf(String::from_utf8_lossy(string).as_ref()) - ); + debug!("Wrote: {}", + escape_crlf(String::from_utf8_lossy(string).as_ref())); Ok(()) } /// Gets the SMTP response - fn get_reply(&mut self) -> SmtpResult { - + fn read_response(&mut self) -> SmtpResult { let mut raw_response = String::new(); let mut response = raw_response.parse::(); @@ -275,7 +266,7 @@ impl Client { #[cfg(test)] mod test { - use super::{ClientCodec, escape_crlf}; + use super::{escape_crlf, ClientCodec}; #[test] fn test_codec() { @@ -291,19 +282,15 @@ mod test { assert!(codec.encode(b"test\n", &mut buf).is_ok()); assert!(codec.encode(b".test\n", &mut buf).is_ok()); assert!(codec.encode(b"test", &mut buf).is_ok()); - assert_eq!( - String::from_utf8(buf).unwrap(), - "test\r\n..\r\n\r\ntestte\r\n..\r\nsttesttest.test\n.test\ntest" - ); + assert_eq!(String::from_utf8(buf).unwrap(), + "test\r\n..\r\n\r\ntestte\r\n..\r\nsttesttest.test\n.test\ntest"); } #[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/lettre/src/smtp/client/net.rs b/lettre/src/smtp/client/net.rs index 45cde83..ab016cb 100644 --- a/lettre/src/smtp/client/net.rs +++ b/lettre/src/smtp/client/net.rs @@ -18,16 +18,14 @@ pub struct ClientTlsParameters { impl ClientTlsParameters { /// Creates a `ClientTlsParameters` pub fn new(domain: String, connector: TlsConnector) -> ClientTlsParameters { - ClientTlsParameters { - connector: connector, - domain: domain, - } + ClientTlsParameters { connector: connector, + domain: domain, } } } /// Accepted protocols by default. /// This removes TLS 1.0 compared to tls-native defaults. -pub const DEFAULT_TLS_PROTOCOLS: &'static [Protocol] = &[Protocol::Tlsv11, Protocol::Tlsv12]; +pub const DEFAULT_TLS_PROTOCOLS: &[Protocol] = &[Protocol::Tlsv11, Protocol::Tlsv12]; #[derive(Debug)] /// Represents the different types of underlying network streams @@ -47,9 +45,7 @@ impl NetworkStream { NetworkStream::Tcp(ref s) => s.peer_addr(), NetworkStream::Tls(ref s) => s.get_ref().peer_addr(), NetworkStream::Mock(_) => { - Ok(SocketAddr::V4( - SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80), - )) + Ok(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80))) } } } @@ -96,7 +92,7 @@ impl Write for NetworkStream { pub trait Connector: Sized { /// Opens a connection to the given IP socket fn connect(addr: &SocketAddr, tls_parameters: Option<&ClientTlsParameters>) - -> io::Result; + -> io::Result; /// Upgrades to TLS connection fn upgrade_tls(&mut self, tls_parameters: &ClientTlsParameters) -> io::Result<()>; /// Is the NetworkStream encrypted @@ -104,19 +100,17 @@ pub trait Connector: Sized { } impl Connector for NetworkStream { - fn connect( - addr: &SocketAddr, - tls_parameters: Option<&ClientTlsParameters>, - ) -> io::Result { + fn connect(addr: &SocketAddr, + tls_parameters: Option<&ClientTlsParameters>) + -> io::Result { let tcp_stream = TcpStream::connect(addr)?; match tls_parameters { Some(context) => { - context - .connector - .connect(context.domain.as_ref(), tcp_stream) - .map(NetworkStream::Tls) - .map_err(|e| io::Error::new(ErrorKind::Other, e)) + context.connector + .connect(context.domain.as_ref(), tcp_stream) + .map(NetworkStream::Tls) + .map_err(|e| io::Error::new(ErrorKind::Other, e)) } None => Ok(NetworkStream::Tcp(tcp_stream)), } @@ -126,10 +120,10 @@ impl Connector for NetworkStream { fn upgrade_tls(&mut self, tls_parameters: &ClientTlsParameters) -> io::Result<()> { *self = match *self { NetworkStream::Tcp(ref mut stream) => { - match tls_parameters.connector.connect( - tls_parameters.domain.as_ref(), - stream.try_clone().unwrap(), - ) { + match tls_parameters.connector + .connect(tls_parameters.domain.as_ref(), + stream.try_clone().unwrap()) + { Ok(tls_stream) => NetworkStream::Tls(tls_stream), Err(err) => return Err(io::Error::new(ErrorKind::Other, err)), } diff --git a/lettre/src/smtp/commands.rs b/lettre/src/smtp/commands.rs index 958357e..ea69a7e 100644 --- a/lettre/src/smtp/commands.rs +++ b/lettre/src/smtp/commands.rs @@ -26,7 +26,7 @@ impl Display for EhloCommand { impl EhloCommand { /// Creates a EHLO command pub fn new(client_id: ClientId) -> EhloCommand { - EhloCommand { client_id: client_id } + EhloCommand { client_id: client_id, } } } @@ -44,20 +44,16 @@ impl Display for StarttlsCommand { /// MAIL command #[derive(PartialEq, Clone, Debug)] pub struct MailCommand { - sender: Option, + sender: Option, parameters: Vec, } impl Display for MailCommand { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!( - f, - "MAIL FROM:<{}>", - match self.sender { - Some(ref address) => address.to_string(), - None => "".to_string(), - } - )?; + write!(f, "MAIL FROM:<{}>", match self.sender { + Some(ref address) => address.to_string(), + None => "".to_string(), + })?; for parameter in &self.parameters { write!(f, " {}", parameter)?; } @@ -68,17 +64,15 @@ impl Display for MailCommand { impl MailCommand { /// Creates a MAIL command pub fn new(sender: Option, parameters: Vec) -> MailCommand { - MailCommand { - sender: sender, - parameters: parameters, - } + MailCommand { sender: sender, + parameters: parameters, } } } /// RCPT command #[derive(PartialEq, Clone, Debug)] pub struct RcptCommand { - recipient: EmailAddress, + recipient: EmailAddress, parameters: Vec, } @@ -95,10 +89,8 @@ impl Display for RcptCommand { impl RcptCommand { /// Creates an RCPT command pub fn new(recipient: EmailAddress, parameters: Vec) -> RcptCommand { - RcptCommand { - recipient: recipient, - parameters: parameters, - } + RcptCommand { recipient: recipient, + parameters: parameters, } } } @@ -212,19 +204,17 @@ impl Display for RsetCommand { /// AUTH command #[derive(PartialEq, Clone, Debug)] pub struct AuthCommand { - mechanism: Mechanism, + mechanism: Mechanism, credentials: Credentials, - challenge: Option, - response: Option, + challenge: Option, + response: Option, } impl Display for AuthCommand { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let encoded_response = if self.response.is_some() { - Some(base64::encode_config( - self.response.as_ref().unwrap().as_bytes(), - base64::STANDARD, - )) + Some(base64::encode_config(self.response.as_ref().unwrap().as_bytes(), + base64::STANDARD)) } else { None }; @@ -243,34 +233,27 @@ impl Display for AuthCommand { impl AuthCommand { /// Creates an AUTH command (from a challenge if provided) - pub fn new( - mechanism: Mechanism, - credentials: Credentials, - challenge: Option, - ) -> Result { + pub fn new(mechanism: Mechanism, + credentials: Credentials, + challenge: Option) + -> Result { let response = if mechanism.supports_initial_response() || challenge.is_some() { - Some(mechanism.response( - &credentials, - challenge.as_ref().map(String::as_str), - )?) + Some(mechanism.response(&credentials, challenge.as_ref().map(String::as_str))?) } else { None }; - Ok(AuthCommand { - mechanism: mechanism, - credentials: credentials, - challenge: challenge, - response: response, - }) + Ok(AuthCommand { mechanism: mechanism, + credentials: credentials, + challenge: challenge, + response: response, }) } /// Creates an AUTH command from a response that needs to be a /// valid challenge (with 334 response code) - pub fn new_from_response( - mechanism: Mechanism, - credentials: Credentials, - response: &Response, - ) -> Result { + pub fn new_from_response(mechanism: Mechanism, + credentials: Credentials, + response: &Response) + -> Result { if !response.has_code(334) { return Err(Error::ResponseParsing("Expecting a challenge")); } @@ -294,17 +277,12 @@ impl AuthCommand { debug!("auth decoded challenge: {}", decoded_challenge); - let response = Some(mechanism.response( - &credentials, - Some(decoded_challenge.as_ref()), - )?); + let response = Some(mechanism.response(&credentials, Some(decoded_challenge.as_ref()))?); - Ok(AuthCommand { - mechanism: mechanism, - credentials: credentials, - challenge: Some(decoded_challenge), - response: response, - }) + Ok(AuthCommand { mechanism: mechanism, + credentials: credentials, + challenge: Some(decoded_challenge), + response: response, }) } } @@ -319,77 +297,49 @@ mod test { fn test_display() { let id = ClientId::Domain("localhost".to_string()); let email = EmailAddress::new("test@example.com".to_string()); - let mail_parameter = MailParameter::Other { - keyword: "TEST".to_string(), - value: Some("value".to_string()), - }; - let rcpt_parameter = RcptParameter::Other { - keyword: "TEST".to_string(), - value: Some("value".to_string()), - }; + let mail_parameter = MailParameter::Other { keyword: "TEST".to_string(), + value: Some("value".to_string()), }; + let rcpt_parameter = RcptParameter::Other { keyword: "TEST".to_string(), + value: Some("value".to_string()), }; assert_eq!(format!("{}", EhloCommand::new(id)), "EHLO localhost\r\n"); - assert_eq!( - format!("{}", MailCommand::new(Some(email.clone()), vec![])), - "MAIL FROM:\r\n" - ); - assert_eq!( - format!("{}", MailCommand::new(None, vec![])), - "MAIL FROM:<>\r\n" - ); - assert_eq!( - format!( - "{}", - MailCommand::new(Some(email.clone()), vec![MailParameter::Size(42)]) - ), - "MAIL FROM: SIZE=42\r\n" - ); + assert_eq!(format!("{}", MailCommand::new(Some(email.clone()), vec![])), + "MAIL FROM:\r\n"); + assert_eq!(format!("{}", MailCommand::new(None, vec![])), + "MAIL FROM:<>\r\n"); + assert_eq!(format!("{}", + MailCommand::new(Some(email.clone()), vec![MailParameter::Size(42)])), + "MAIL FROM: SIZE=42\r\n"); assert_eq!( format!( "{}", MailCommand::new( Some(email.clone()), - vec![ - MailParameter::Size(42), - MailParameter::Body(MailBodyParameter::EightBitMime), - mail_parameter, - ], + vec![MailParameter::Size(42), + MailParameter::Body(MailBodyParameter::EightBitMime), + mail_parameter], ) ), "MAIL FROM: SIZE=42 BODY=8BITMIME TEST=value\r\n" ); - assert_eq!( - format!("{}", RcptCommand::new(email.clone(), vec![])), - "RCPT TO:\r\n" - ); - assert_eq!( - format!("{}", RcptCommand::new(email.clone(), vec![rcpt_parameter])), - "RCPT TO: TEST=value\r\n" - ); + assert_eq!(format!("{}", RcptCommand::new(email.clone(), vec![])), + "RCPT TO:\r\n"); + assert_eq!(format!("{}", RcptCommand::new(email.clone(), vec![rcpt_parameter])), + "RCPT TO: TEST=value\r\n"); assert_eq!(format!("{}", QuitCommand), "QUIT\r\n"); assert_eq!(format!("{}", DataCommand), "DATA\r\n"); assert_eq!(format!("{}", NoopCommand), "NOOP\r\n"); assert_eq!(format!("{}", HelpCommand::new(None)), "HELP\r\n"); - assert_eq!( - format!("{}", HelpCommand::new(Some("test".to_string()))), - "HELP test\r\n" - ); - assert_eq!( - format!("{}", VrfyCommand::new("test".to_string())), - "VRFY test\r\n" - ); - assert_eq!( - format!("{}", ExpnCommand::new("test".to_string())), - "EXPN test\r\n" - ); + assert_eq!(format!("{}", HelpCommand::new(Some("test".to_string()))), + "HELP test\r\n"); + assert_eq!(format!("{}", VrfyCommand::new("test".to_string())), + "VRFY test\r\n"); + assert_eq!(format!("{}", ExpnCommand::new("test".to_string())), + "EXPN test\r\n"); assert_eq!(format!("{}", RsetCommand), "RSET\r\n"); let credentials = Credentials::new("user".to_string(), "password".to_string()); - assert_eq!( - format!( - "{}", - AuthCommand::new(Mechanism::Plain, credentials.clone(), None).unwrap() - ), - "AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=\r\n" - ); + assert_eq!(format!("{}", + AuthCommand::new(Mechanism::Plain, credentials.clone(), None).unwrap()), + "AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=\r\n"); #[cfg(feature = "crammd5-auth")] assert_eq!( format!( @@ -402,13 +352,9 @@ mod test { ), "dXNlciAzMTYxY2NmZDdmMjNlMzJiYmMzZTQ4NjdmYzk0YjE4Nw==\r\n" ); - assert_eq!( - format!( - "{}", - AuthCommand::new(Mechanism::Login, credentials.clone(), None).unwrap() - ), - "AUTH LOGIN\r\n" - ); + assert_eq!(format!("{}", + AuthCommand::new(Mechanism::Login, credentials.clone(), None).unwrap()), + "AUTH LOGIN\r\n"); #[cfg(feature = "crammd5-auth")] assert_eq!( format!( diff --git a/lettre/src/smtp/extension.rs b/lettre/src/smtp/extension.rs index 48e2a27..c32d03c 100644 --- a/lettre/src/smtp/extension.rs +++ b/lettre/src/smtp/extension.rs @@ -1,5 +1,6 @@ //! ESMTP features +use hostname::get_hostname; use smtp::authentication::Mechanism; use smtp::error::Error; use smtp::response::Response; @@ -9,6 +10,9 @@ use std::fmt::{self, Display, Formatter}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::result::Result; +/// Default ehlo clinet id +pub const DEFAULT_EHLO_HOSTNAME: &str = "localhost"; + /// Client identifier, the parameter to `EHLO` #[derive(PartialEq, Eq, Clone, Debug)] pub enum ClientId { @@ -35,6 +39,15 @@ impl ClientId { pub fn new(domain: String) -> ClientId { ClientId::Domain(domain) } + + /// Defines a `ClientId` with the current hostname, of `localhost` if hostname could not be + /// found + pub fn hostname() -> ClientId { + ClientId::Domain(match get_hostname() { + Some(name) => name, + None => DEFAULT_EHLO_HOSTNAME.to_string(), + }) + } } /// Supported ESMTP keywords @@ -82,16 +95,14 @@ pub struct ServerInfo { impl Display for ServerInfo { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!( - f, - "{} with {}", - self.name, - if self.features.is_empty() { - "no supported features".to_string() - } else { - format!("{:?}", self.features) - } - ) + write!(f, + "{} with {}", + self.name, + if self.features.is_empty() { + "no supported features".to_string() + } else { + format!("{:?}", self.features) + }) } } @@ -142,10 +153,8 @@ impl ServerInfo { }; } - Ok(ServerInfo { - name: name.to_string(), - features: features, - }) + Ok(ServerInfo { name: name.to_string(), + features: features, }) } /// Checks if the server supports an ESMTP feature @@ -155,9 +164,7 @@ impl ServerInfo { /// Checks if the server supports an ESMTP feature pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool { - self.features.contains( - &Extension::Authentication(mechanism), - ) + self.features.contains(&Extension::Authentication(mechanism)) } } @@ -185,14 +192,12 @@ impl Display for MailParameter { MailParameter::Body(ref value) => write!(f, "BODY={}", value), MailParameter::Size(size) => write!(f, "SIZE={}", size), MailParameter::SmtpUtfEight => f.write_str("SMTPUTF8"), - MailParameter::Other { - ref keyword, - value: Some(ref value), - } => write!(f, "{}={}", keyword, XText(value)), - MailParameter::Other { - ref keyword, - value: None, - } => f.write_str(keyword), + MailParameter::Other { ref keyword, + value: Some(ref value), } => { + write!(f, "{}={}", keyword, XText(value)) + } + MailParameter::Other { ref keyword, + value: None, } => f.write_str(keyword), } } } @@ -230,14 +235,12 @@ pub enum RcptParameter { impl Display for RcptParameter { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { - RcptParameter::Other { - ref keyword, - value: Some(ref value), - } => write!(f, "{}={}", keyword, XText(value)), - RcptParameter::Other { - ref keyword, - value: None, - } => f.write_str(keyword), + RcptParameter::Other { ref keyword, + value: Some(ref value), } => { + write!(f, "{}={}", keyword, XText(value)) + } + RcptParameter::Other { ref keyword, + value: None, } => f.write_str(keyword), } } } @@ -252,14 +255,10 @@ mod test { #[test] fn test_extension_fmt() { - assert_eq!( - format!("{}", Extension::EightBitMime), - "8BITMIME".to_string() - ); - assert_eq!( - format!("{}", Extension::Authentication(Mechanism::Plain)), - "AUTH PLAIN".to_string() - ); + assert_eq!(format!("{}", Extension::EightBitMime), + "8BITMIME".to_string()); + assert_eq!(format!("{}", Extension::Authentication(Mechanism::Plain)), + "AUTH PLAIN".to_string()); } #[test] @@ -267,67 +266,41 @@ mod test { 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() - ); + 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(Mechanism::Plain))); - assert_eq!( - format!( - "{}", - ServerInfo { - name: "name".to_string(), - features: plain.clone(), - } - ), - "name with {Authentication(Plain)}".to_string() - ); + 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, - Detail(1), - ), - vec![ - "me".to_string(), - "8BITMIME".to_string(), - "SIZE 42".to_string(), - ], - ); + let response = Response::new(Code::new(Severity::PositiveCompletion, + Category::Unspecified4, + Detail(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, - }; + let server_info = ServerInfo { name: "me".to_string(), + features: features, }; assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info); @@ -336,34 +309,22 @@ mod test { #[cfg(feature = "crammd5-auth")] assert!(!server_info.supports_auth_mechanism(Mechanism::CramMd5)); - let response2 = Response::new( - Code::new( - Severity::PositiveCompletion, - Category::Unspecified4, - Detail(1), - ), - vec![ - "me".to_string(), - "AUTH PLAIN CRAM-MD5 OTHER".to_string(), - "8BITMIME".to_string(), - "SIZE 42".to_string(), - ], - ); + let response2 = Response::new(Code::new(Severity::PositiveCompletion, + Category::Unspecified4, + Detail(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(Mechanism::Plain), - )); + assert!(features2.insert(Extension::Authentication(Mechanism::Plain),)); #[cfg(feature = "crammd5-auth")] - assert!(features2.insert( - Extension::Authentication(Mechanism::CramMd5), - )); + assert!(features2.insert(Extension::Authentication(Mechanism::CramMd5),)); - let server_info2 = ServerInfo { - name: "me".to_string(), - features: features2, - }; + let server_info2 = ServerInfo { name: "me".to_string(), + features: features2, }; assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2); diff --git a/lettre/src/smtp/mod.rs b/lettre/src/smtp/mod.rs index 2cbf35e..e1d8716 100644 --- a/lettre/src/smtp/mod.rs +++ b/lettre/src/smtp/mod.rs @@ -91,24 +91,23 @@ //! //! let mut email_client: Client = Client::new(); //! let _ = email_client.connect(&("localhost", SMTP_PORT), None); -//! let _ = email_client.smtp_command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); -//! let _ = email_client.smtp_command( +//! let _ = email_client.command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); +//! let _ = email_client.command( //! MailCommand::new(Some(EmailAddress::new("user@example.com".to_string())), vec![]) //! ); -//! let _ = email_client.smtp_command( +//! let _ = email_client.command( //! RcptCommand::new(EmailAddress::new("user@example.org".to_string()), vec![]) //! ); -//! let _ = email_client.smtp_command(DataCommand); +//! let _ = email_client.command(DataCommand); //! let _ = email_client.message(Box::new("Test email".as_bytes())); -//! let _ = email_client.smtp_command(QuitCommand); +//! let _ = email_client.command(QuitCommand); //! ``` use EmailTransport; use SendableEmail; use native_tls::TlsConnector; -use hostname::get_hostname; -use smtp::authentication::{Credentials, DEFAULT_ENCRYPTED_MECHANISMS, - DEFAULT_UNENCRYPTED_MECHANISMS, Mechanism}; +use smtp::authentication::{Credentials, Mechanism, DEFAULT_ENCRYPTED_MECHANISMS, + DEFAULT_UNENCRYPTED_MECHANISMS}; use smtp::client::Client; use smtp::client::net::ClientTlsParameters; use smtp::client::net::DEFAULT_TLS_PROTOCOLS; @@ -207,28 +206,24 @@ impl SmtpTransportBuilder { /// Defaults are: /// /// * No connection reuse - /// * "localhost" as EHLO name /// * No authentication /// * No SMTPUTF8 support /// * A 60 seconds timeout for smtp commands - pub fn new( - addr: A, - security: ClientSecurity, - ) -> Result { + pub fn new(addr: A, + security: ClientSecurity) + -> Result { let mut addresses = addr.to_socket_addrs()?; match addresses.next() { Some(addr) => { - Ok(SmtpTransportBuilder { - server_addr: addr, - security: security, - smtp_utf8: false, - credentials: None, - connection_reuse: ConnectionReuseParameters::NoReuse, - hello_name: ClientId::Domain(get_hostname().unwrap_or("localhost".to_string())), - authentication_mechanism: None, - timeout: Some(Duration::new(60, 0)), - }) + Ok(SmtpTransportBuilder { server_addr: addr, + security: security, + smtp_utf8: false, + credentials: None, + connection_reuse: ConnectionReuseParameters::NoReuse, + hello_name: ClientId::hostname(), + authentication_mechanism: None, + timeout: Some(Duration::new(60, 0)), }) } None => Err(Error::Resolution), } @@ -247,10 +242,9 @@ impl SmtpTransportBuilder { } /// Enable connection reuse - pub fn connection_reuse( - mut self, - parameters: ConnectionReuseParameters, - ) -> SmtpTransportBuilder { + pub fn connection_reuse(mut self, + parameters: ConnectionReuseParameters) + -> SmtpTransportBuilder { self.connection_reuse = parameters; self } @@ -323,24 +317,20 @@ impl<'a> SmtpTransport { /// Creates an encrypted transport over submission port, using the provided domain /// to validate TLS certificates. pub fn simple_builder(domain: &str) -> Result { - let mut tls_builder = TlsConnector::builder()?; tls_builder.supported_protocols(DEFAULT_TLS_PROTOCOLS)?; let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls_builder.build().unwrap()); - SmtpTransportBuilder::new( - (domain, SUBMISSION_PORT), - ClientSecurity::Required(tls_parameters), - ) + SmtpTransportBuilder::new((domain, SUBMISSION_PORT), + ClientSecurity::Required(tls_parameters)) } /// Creates a new configurable builder - pub fn builder( - addr: A, - security: ClientSecurity, - ) -> Result { + pub fn builder(addr: A, + security: ClientSecurity) + -> Result { SmtpTransportBuilder::new(addr, security) } @@ -353,25 +343,20 @@ impl<'a> SmtpTransport { /// /// It does not connect to the server, but only creates the `SmtpTransport` pub fn new(builder: SmtpTransportBuilder) -> SmtpTransport { - let client = Client::new(); - SmtpTransport { - client: client, + SmtpTransport { client: client, server_info: None, client_info: builder, - state: State { - panic: false, - connection_reuse_count: 0, - }, - } + state: State { panic: false, + connection_reuse_count: 0, }, } } /// Gets the EHLO response and updates server information pub fn get_ehlo(&mut self) -> SmtpResult { // Extended Hello let ehlo_response = try_smtp!( - self.client.smtp_command(EhloCommand::new( + self.client.command(EhloCommand::new( ClientId::new(self.client_info.hello_name.to_string()), )), self @@ -406,7 +391,6 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { /// Sends an email #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms, cyclomatic_complexity))] fn send + 'a>(&mut self, email: &'a U) -> SmtpResult { - // Extract email information let message_id = email.message_id(); @@ -416,13 +400,13 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { } if self.state.connection_reuse_count == 0 { - self.client.connect( - &self.client_info.server_addr, - match self.client_info.security { - ClientSecurity::Wrapper(ref tls_parameters) => Some(tls_parameters), - _ => None, - }, - )?; + self.client.connect(&self.client_info.server_addr, + match self.client_info.security { + ClientSecurity::Wrapper(ref tls_parameters) => { + Some(tls_parameters) + } + _ => None, + })?; self.client.set_timeout(self.client_info.timeout)?; @@ -431,21 +415,20 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { self.get_ehlo()?; - match ( - &self.client_info.security.clone(), - self.server_info.as_ref().unwrap().supports_feature( - Extension::StartTls, - ), - ) { + match (&self.client_info.security.clone(), + self.server_info.as_ref() + .unwrap() + .supports_feature(Extension::StartTls)) + { (&ClientSecurity::Required(_), false) => { return Err(From::from("Could not encrypt connection, aborting")) } (&ClientSecurity::Opportunistic(_), false) => (), (&ClientSecurity::None, _) => (), (&ClientSecurity::Wrapper(_), _) => (), - (&ClientSecurity::Opportunistic(ref tls_parameters), true) | - (&ClientSecurity::Required(ref tls_parameters), true) => { - try_smtp!(self.client.smtp_command(StarttlsCommand), self); + (&ClientSecurity::Opportunistic(ref tls_parameters), true) + | (&ClientSecurity::Required(ref tls_parameters), true) => { + try_smtp!(self.client.command(StarttlsCommand), self); try_smtp!(self.client.upgrade_tls_stream(tls_parameters), self); debug!("connection encrypted"); @@ -471,16 +454,13 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { }; for mechanism in accepted_mechanisms { - if self.server_info.as_ref().unwrap().supports_auth_mechanism( - mechanism, - ) - { + if self.server_info.as_ref() + .unwrap() + .supports_auth_mechanism(mechanism) { found = true; try_smtp!( - self.client.auth( - mechanism, - self.client_info.credentials.as_ref().unwrap(), - ), + self.client + .auth(mechanism, self.client_info.credentials.as_ref().unwrap(),), self ); break; @@ -496,45 +476,34 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { // Mail let mut mail_options = vec![]; - if self.server_info.as_ref().unwrap().supports_feature( - Extension::EightBitMime, - ) - { + if self.server_info.as_ref() + .unwrap() + .supports_feature(Extension::EightBitMime) { mail_options.push(MailParameter::Body(MailBodyParameter::EightBitMime)); } - if self.server_info.as_ref().unwrap().supports_feature( - Extension::SmtpUtfEight, - ) && self.client_info.smtp_utf8 - { + if self.server_info.as_ref() + .unwrap() + .supports_feature(Extension::SmtpUtfEight) && self.client_info.smtp_utf8 { mail_options.push(MailParameter::SmtpUtfEight); } - try_smtp!( - self.client.smtp_command(MailCommand::new( - Some(email.from().clone()), - mail_options, - )), - self - ); + try_smtp!(self.client.command(MailCommand::new(Some(email.from().clone()), mail_options,)), + self); // Log the mail command info!("{}: from=<{}>", message_id, email.from()); // Recipient for to_address in &email.to() { - try_smtp!( - self.client.smtp_command( - RcptCommand::new(to_address.clone(), vec![]), - ), - self - ); + try_smtp!(self.client.command(RcptCommand::new(to_address.clone(), vec![]),), + self); // Log the rcpt command info!("{}: to=<{}>", message_id, to_address); } // Data - try_smtp!(self.client.smtp_command(DataCommand), self); + try_smtp!(self.client.command(DataCommand), self); // Message content let result = self.client.message(email.message()); @@ -544,25 +513,25 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport { self.state.connection_reuse_count += 1; // Log the message - info!( - "{}: conn_use={}, status=sent ({})", - message_id, - self.state.connection_reuse_count, - result - .as_ref() - .ok() - .unwrap() - .message - .iter() - .next() - .unwrap_or(&"no response".to_string()) - ); + info!("{}: conn_use={}, status=sent ({})", + message_id, + self.state.connection_reuse_count, + result.as_ref() + .ok() + .unwrap() + .message + .iter() + .next() + .unwrap_or(&"no response".to_string())); } // Test if we can reuse the existing connection match self.client_info.connection_reuse { ConnectionReuseParameters::ReuseLimited(limit) - if self.state.connection_reuse_count >= limit => self.reset(), + if self.state.connection_reuse_count >= limit => + { + self.reset() + } ConnectionReuseParameters::NoReuse => self.reset(), _ => (), } diff --git a/lettre/src/smtp/response.rs b/lettre/src/smtp/response.rs index 04ff1e1..0322dd1 100644 --- a/lettre/src/smtp/response.rs +++ b/lettre/src/smtp/response.rs @@ -2,8 +2,7 @@ //! message use self::Severity::*; -use nom::{ErrorKind as NomErrorKind, IResult as NomResult, crlf}; - +use nom::{crlf, ErrorKind as NomErrorKind, IResult as NomResult}; use nom::simple_errors::Err as NomError; use std::fmt::{Display, Formatter, Result}; use std::result; @@ -85,11 +84,9 @@ impl Code { panic!("The detail code must be between 0 and 9"); } - Code { - severity: severity, + Code { severity: severity, category: category, - detail: detail, - } + detail: detail, } } } @@ -120,10 +117,8 @@ impl FromStr for Response { impl Response { /// Creates a new `Response` pub fn new(code: Code, message: Vec) -> Response { - Response { - code: code, - message: message, - } + Response { code: code, + message: message, } } /// Tells if the response is positive @@ -141,9 +136,8 @@ impl Response { /// Returns only the first word of the message if possible pub fn first_word(&self) -> Option<&str> { - self.message.get(0).and_then( - |line| line.split_whitespace().next(), - ) + self.message.get(0) + .and_then(|line| line.split_whitespace().next()) } /// Returns only the line of the message if possible @@ -155,19 +149,15 @@ impl Response { // Parsers (originaly from tokio-smtp) named!(parse_code, - map!( - tuple!(parse_severity, parse_category, parse_detail), - |(severity, category, detail)| { - Code { - severity: severity, - category: category, - detail: detail, - } - } - ) -); + map!(tuple!(parse_severity, parse_category, parse_detail), + |(severity, category, detail)| { + Code { severity: severity, + category: category, + detail: detail, } + })); -named!(parse_severity, +named!( + parse_severity, alt!( tag!("2") => { |_| Severity::PositiveCompletion } | tag!("3") => { |_| Severity::PositiveIntermediate } | @@ -176,7 +166,8 @@ named!(parse_severity, ) ); -named!(parse_category, +named!( + parse_category, alt!( tag!("0") => { |_| Category::Syntax } | tag!("1") => { |_| Category::Information } | @@ -187,7 +178,8 @@ named!(parse_category, ) ); -named!(parse_detail, +named!( + parse_detail, alt!( tag!("0") => { |_| Detail(0) } | tag!("1") => { |_| Detail(1) } | @@ -203,56 +195,33 @@ named!(parse_detail, ); 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(()); - } + 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(()); + } - // 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); - } + // 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); + } - Ok(Response { - code: last_code, - message: lines.into_iter() - .map(|line| from_utf8(line).map(|s| s.to_string())) - .collect::, _>>() - .map_err(|_| ())?, - }) - } - ) -); + Ok(Response { code: last_code, + message: lines.into_iter() + .map(|line| from_utf8(line).map(|s| s.to_string())) + .collect::, _>>() + .map_err(|_| ())?, }) + })); #[cfg(test)] mod test { @@ -270,37 +239,27 @@ mod test { #[test] fn test_code_new() { - assert_eq!( - Code::new( - Severity::TransientNegativeCompletion, - Category::Connections, - Detail(0), - ), - Code { - severity: Severity::TransientNegativeCompletion, - category: Category::Connections, - detail: Detail(0), - } - ); + assert_eq!(Code::new(Severity::TransientNegativeCompletion, + Category::Connections, + Detail(0),), + Code { severity: Severity::TransientNegativeCompletion, + category: Category::Connections, + detail: Detail(0), }); } #[test] #[should_panic] fn test_code_new_panic() { - let _ = Code::new( - Severity::TransientNegativeCompletion, - Category::Connections, - Detail(11), - ); + let _ = Code::new(Severity::TransientNegativeCompletion, + Category::Connections, + Detail(11)); } #[test] fn test_code_display() { - let code = Code { - severity: Severity::TransientNegativeCompletion, + let code = Code { severity: Severity::TransientNegativeCompletion, category: Category::Connections, - detail: Detail(1), - }; + detail: Detail(1), }; assert_eq!(code.to_string(), "421"); } @@ -308,32 +267,20 @@ mod test { #[test] fn test_response_from_str() { let raw_response = "250-me\r\n250-8BITMIME\r\n250-SIZE 42\r\n250 AUTH PLAIN CRAM-MD5\r\n"; - assert_eq!( - raw_response.parse::().unwrap(), - Response { - code: Code { - severity: Severity::PositiveCompletion, - category: Category::MailSystem, - detail: Detail(0), - }, - message: vec![ - "me".to_string(), - "8BITMIME".to_string(), - "SIZE 42".to_string(), - "AUTH PLAIN CRAM-MD5".to_string(), - ], - } - ); + assert_eq!(raw_response.parse::().unwrap(), + Response { code: Code { severity: Severity::PositiveCompletion, + category: Category::MailSystem, + detail: Detail(0), }, + message: vec!["me".to_string(), + "8BITMIME".to_string(), + "SIZE 42".to_string(), + "AUTH PLAIN CRAM-MD5".to_string()], }); let wrong_code = "2506-me\r\n250-8BITMIME\r\n250-SIZE 42\r\n250 AUTH PLAIN CRAM-MD5\r\n"; - assert!( - wrong_code.parse::().is_err() - ); + assert!(wrong_code.parse::().is_err()); let wrong_end = "250-me\r\n250-8BITMIME\r\n250-SIZE 42\r\n250-AUTH PLAIN CRAM-MD5\r\n"; - assert!( - wrong_end.parse::().is_err() - ); + assert!(wrong_end.parse::().is_err()); } #[test] @@ -353,11 +300,11 @@ mod test { ).is_positive() ); assert!(!Response::new( - Code { - severity: Severity::TransientNegativeCompletion, - category: Category::MailSystem, - detail: Detail(0), - }, + Code { + severity: Severity::TransientNegativeCompletion, + category: Category::MailSystem, + detail: Detail(0), + }, vec![ "me".to_string(), "8BITMIME".to_string(), @@ -383,11 +330,11 @@ mod test { ).has_code(451) ); assert!(!Response::new( - Code { - severity: Severity::TransientNegativeCompletion, - category: Category::MailSystem, - detail: Detail(1), - }, + Code { + severity: Severity::TransientNegativeCompletion, + category: Category::MailSystem, + detail: Detail(1), + }, vec![ "me".to_string(), "8BITMIME".to_string(), diff --git a/lettre/src/smtp/util.rs b/lettre/src/smtp/util.rs index 0c08c0b..cd75d0e 100644 --- a/lettre/src/smtp/util.rs +++ b/lettre/src/smtp/util.rs @@ -34,13 +34,10 @@ mod tests { #[test] fn test() { - for (input, expect) in vec![ - ("bjorn", "bjorn"), - ("bjørn", "bjørn"), - ("Ø+= ❤️‰", "Ø+2B+3D+20❤️‰"), - ("+", "+2B"), - ] - { + for (input, expect) in vec![("bjorn", "bjorn"), + ("bjørn", "bjørn"), + ("Ø+= ❤️‰", "Ø+2B+3D+20❤️‰"), + ("+", "+2B")] { assert_eq!(format!("{}", XText(input)), expect); } } diff --git a/lettre/src/stub/mod.rs b/lettre/src/stub/mod.rs index 52a58db..9bfcfdf 100644 --- a/lettre/src/stub/mod.rs +++ b/lettre/src/stub/mod.rs @@ -50,13 +50,10 @@ pub type StubResult = Result<(), ()>; impl<'a, T: Read + 'a> EmailTransport<'a, T, StubResult> for StubEmailTransport { fn send>(&mut self, email: &'a U) -> StubResult { - - info!( - "{}: from=<{}> to=<{:?}>", - email.message_id(), - email.from(), - email.to() - ); + info!("{}: from=<{}> to=<{:?}>", + email.message_id(), + email.from(), + email.to()); self.response } } diff --git a/lettre/tests/transport_file.rs b/lettre/tests/transport_file.rs index ad8841e..259e1f4 100644 --- a/lettre/tests/transport_file.rs +++ b/lettre/tests/transport_file.rs @@ -14,12 +14,10 @@ mod test { #[test] fn file_transport() { let mut sender = FileEmailTransport::new(temp_dir()); - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "file_id".to_string(), - "Hello file".to_string(), - ); + let email = SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "file_id".to_string(), + "Hello file".to_string()); let result = sender.send(&email); assert!(result.is_ok()); @@ -29,11 +27,9 @@ mod test { let mut buffer = String::new(); let _ = f.read_to_string(&mut buffer); - assert_eq!( - buffer, - "{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\"message_id\":\ - \"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}" - ); + assert_eq!(buffer, + "{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\"message_id\":\ + \"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}"); remove_file(file).unwrap(); } diff --git a/lettre/tests/transport_sendmail.rs b/lettre/tests/transport_sendmail.rs index 8c7cc80..701d684 100644 --- a/lettre/tests/transport_sendmail.rs +++ b/lettre/tests/transport_sendmail.rs @@ -10,12 +10,10 @@ mod test { #[test] fn sendmail_transport_simple() { let mut sender = SendmailTransport::new(); - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "sendmail_id".to_string(), - "Hello sendmail".to_string(), - ); + let email = SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "sendmail_id".to_string(), + "Hello sendmail".to_string()); let result = sender.send(&email); println!("{:?}", result); diff --git a/lettre/tests/transport_smtp.rs b/lettre/tests/transport_smtp.rs index 236b382..ce29a63 100644 --- a/lettre/tests/transport_smtp.rs +++ b/lettre/tests/transport_smtp.rs @@ -8,15 +8,12 @@ mod test { #[test] fn smtp_transport_simple() { - let mut sender = SmtpTransport::builder("127.0.0.1:2525", ClientSecurity::None) - .unwrap() - .build(); - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "smtp_id".to_string(), - "Hello smtp".to_string(), - ); + let mut sender = SmtpTransport::builder("127.0.0.1:2525", ClientSecurity::None).unwrap() + .build(); + let email = SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "smtp_id".to_string(), + "Hello smtp".to_string()); sender.send(&email).unwrap(); } diff --git a/lettre/tests/transport_stub.rs b/lettre/tests/transport_stub.rs index 23976cf..27ca504 100644 --- a/lettre/tests/transport_stub.rs +++ b/lettre/tests/transport_stub.rs @@ -8,12 +8,10 @@ fn stub_transport() { let mut sender_ok = StubEmailTransport::new_positive(); let mut sender_ko = StubEmailTransport::new(Err(())); - let email = SimpleSendableEmail::new( - EmailAddress::new("user@localhost".to_string()), - vec![EmailAddress::new("root@localhost".to_string())], - "stub_id".to_string(), - "Hello stub".to_string(), - ); + let email = SimpleSendableEmail::new(EmailAddress::new("user@localhost".to_string()), + vec![EmailAddress::new("root@localhost".to_string())], + "stub_id".to_string(), + "Hello stub".to_string()); sender_ok.send(&email).unwrap(); sender_ko.send(&email).unwrap_err(); diff --git a/lettre_email/examples/smtp.rs b/lettre_email/examples/smtp.rs index 761fbe9..502c3b0 100644 --- a/lettre_email/examples/smtp.rs +++ b/lettre_email/examples/smtp.rs @@ -19,9 +19,8 @@ fn main() { .unwrap(); // Open a local connection on port 25 - let mut mailer = SmtpTransport::builder_unencrypted_localhost() - .unwrap() - .build(); + let mut mailer = SmtpTransport::builder_unencrypted_localhost().unwrap() + .build(); // Send the email let result = mailer.send(&email); diff --git a/lettre_email/src/lib.rs b/lettre_email/src/lib.rs index d20f99a..cc7d6f5 100644 --- a/lettre_email/src/lib.rs +++ b/lettre_email/src/lib.rs @@ -55,11 +55,11 @@ #![doc(html_root_url = "https://docs.rs/lettre_email/0.7.0")] #![deny(missing_docs, unsafe_code, unstable_features, warnings, missing_debug_implementations)] +extern crate email as email_format; +extern crate lettre; extern crate mime; extern crate time; extern crate uuid; -extern crate email as email_format; -extern crate lettre; pub mod error; @@ -70,7 +70,7 @@ use mime::Mime; use std::fs::File; use std::io::Read; use std::path::Path; -use time::{Tm, now}; +use time::{now, Tm}; use uuid::Uuid; /// Converts an address or an address with an alias to a `Header` @@ -174,17 +174,17 @@ impl IntoEmail for SimpleEmail { /// Simple representation of an email, useful for some transports #[derive(PartialEq, Eq, Clone, Debug, Default)] pub struct SimpleEmail { - from: Option, - to: Vec, - cc: Vec, - bcc: Vec, - reply_to: Option, - subject: Option, - date: Option, - html: Option, - text: Option, + from: Option, + to: Vec, + cc: Vec, + bcc: Vec, + reply_to: Option, + subject: Option, + date: Option, + html: Option, + text: Option, attachments: Vec, - headers: Vec
, + headers: Vec
, } impl SimpleEmail { @@ -359,10 +359,8 @@ pub struct Envelope { impl Envelope { /// Constructs an envelope with no receivers and an empty sender pub fn new() -> Self { - Envelope { - to: vec![], - from: String::new(), - } + Envelope { to: vec![], + from: String::new(), } } /// Adds a receiver pub fn to>(mut self, address: S) -> Self { @@ -398,7 +396,7 @@ pub struct Email { impl PartBuilder { /// Creates a new empty part pub fn new() -> PartBuilder { - PartBuilder { message: MimeMessage::new_blank_message() } + PartBuilder { message: MimeMessage::new_blank_message(), } } /// Adds a generic header @@ -466,17 +464,15 @@ impl PartBuilder { impl EmailBuilder { /// Creates a new empty email pub fn new() -> EmailBuilder { - EmailBuilder { - message: PartBuilder::new(), - to_header: vec![], - from_header: vec![], - cc_header: vec![], - bcc_header: vec![], + EmailBuilder { message: PartBuilder::new(), + to_header: vec![], + from_header: vec![], + cc_header: vec![], + bcc_header: vec![], reply_to_header: vec![], - sender_header: None, - envelope: None, - date_issued: false, - } + sender_header: None, + envelope: None, + date_issued: false, } } /// Sets the email body @@ -581,9 +577,7 @@ impl EmailBuilder { /// Adds a `Subject` header pub fn set_subject>(&mut self, subject: S) { - self.message.add_header( - ("Subject".to_string(), subject.into()), - ); + self.message.add_header(("Subject".to_string(), subject.into())); } /// Adds a `Date` header with the given date @@ -594,31 +588,27 @@ impl EmailBuilder { /// Adds a `Date` header with the given date pub fn set_date(&mut self, date: &Tm) { - self.message.add_header( - ("Date", Tm::rfc822z(date).to_string()), - ); + self.message.add_header(("Date", Tm::rfc822z(date).to_string())); self.date_issued = true; } /// Adds an attachment to the email - pub fn attachment( - mut self, - path: &Path, - filename: Option<&str>, - content_type: &Mime, - ) -> Result { + pub fn attachment(mut self, + path: &Path, + filename: Option<&str>, + content_type: &Mime) + -> Result { self.set_attachment(path, filename, content_type)?; Ok(self) } /// Adds an attachment to the email /// If filename is not provided, the name of the file will be used. - pub fn set_attachment( - &mut self, - path: &Path, - filename: Option<&str>, - content_type: &Mime, - ) -> Result<(), Error> { + pub fn set_attachment(&mut self, + path: &Path, + filename: Option<&str>, + content_type: &Mime) + -> Result<(), Error> { let file = File::open(path); let body = match file { Ok(mut f) => { @@ -651,14 +641,12 @@ impl EmailBuilder { } }; - let content = PartBuilder::new() - .body(body) - .header(( - "Content-Disposition", - format!("attachment; filename=\"{}\"", actual_filename), - )) - .header(("Content-Type", content_type.to_string())) - .build(); + let content = PartBuilder::new().body(body) + .header(("Content-Disposition", + format!("attachment; filename=\"{}\"", + actual_filename))) + .header(("Content-Type", content_type.to_string())) + .build(); self.set_message_type(MimeMultipartType::Mixed); self.add_child(content); @@ -697,10 +685,7 @@ impl EmailBuilder { /// Sets the email body to plain text content pub fn set_text>(&mut self, body: S) { self.message.set_body(body); - self.message.add_header(( - "Content-Type", - format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(), - )); + self.message.add_header(("Content-Type", format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref())); } /// Sets the email body to HTML content @@ -712,43 +697,34 @@ impl EmailBuilder { /// Sets the email body to HTML content pub fn set_html>(&mut self, body: S) { self.message.set_body(body); - self.message.add_header(( - "Content-Type", - format!("{}", mime::TEXT_HTML).as_ref(), - )); + self.message.add_header(("Content-Type", format!("{}", mime::TEXT_HTML).as_ref())); } /// Sets the email content - pub fn alternative, T: Into>( - mut self, - body_html: S, - body_text: T, - ) -> EmailBuilder { + pub fn alternative, T: Into>(mut self, + body_html: S, + body_text: T) + -> EmailBuilder { self.set_alternative(body_html, body_text); self } /// Sets the email content - pub fn set_alternative, T: Into>( - &mut self, - body_html: S, - body_text: T, - ) { + pub fn set_alternative, T: Into>(&mut self, + body_html: S, + body_text: T) { let mut alternate = PartBuilder::new(); alternate.set_message_type(MimeMultipartType::Alternative); - let text = PartBuilder::new() - .body(body_text) - .header(( - "Content-Type", - format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(), - )) - .build(); + let text = PartBuilder::new().body(body_text) + .header(("Content-Type", + format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref())) + .build(); - let html = PartBuilder::new() - .body(body_html) - .header(("Content-Type", format!("{}", mime::TEXT_HTML).as_ref())) - .build(); + let html = PartBuilder::new().body(body_html) + .header(("Content-Type", + format!("{}", mime::TEXT_HTML).as_ref())) + .build(); alternate.add_child(text); alternate.add_child(html); @@ -800,10 +776,9 @@ impl EmailBuilder { // we need to generate the envelope let mut e = Envelope::new(); // add all receivers in to_header and cc_header - for receiver in self.to_header.iter().chain(self.cc_header.iter()).chain( - self.bcc_header.iter(), - ) - { + for receiver in self.to_header.iter() + .chain(self.cc_header.iter()) + .chain(self.bcc_header.iter()) { match *receiver { Address::Mailbox(ref m) => e.add_to(m.address.clone()), Address::Group(_, ref ms) => { @@ -820,12 +795,12 @@ impl EmailBuilder { Some(x) => x.address.clone(), // if we have a sender_header, use it None => { // use a from header - debug_assert!(self.from_header.len()<=1); // else we'd have sender_header + debug_assert!(self.from_header.len() <= 1); // else we'd have sender_header match self.from_header.first() { Some(a) => match *a { // if we have a from header Address::Mailbox(ref mailbox) => mailbox.address.clone(), // use it - Address::Group(_,ref mailbox_list) => match mailbox_list.first() { + Address::Group(_, ref mailbox_list) => match mailbox_list.first() { // if it's an author group, use the first author Some(mailbox) => mailbox.address.clone(), // for an empty author group (the rarest of the rare cases) @@ -843,27 +818,16 @@ impl EmailBuilder { // Add the collected addresses as mailbox-list all at once. // The unwraps are fine because the conversions for Vec
never errs. if !self.to_header.is_empty() { - self.message.add_header( - Header::new_with_value( - "To".into(), - self.to_header, - ).unwrap(), - ); + self.message.add_header(Header::new_with_value("To".into(), self.to_header).unwrap()); } if !self.from_header.is_empty() { - self.message.add_header( - Header::new_with_value("From".into(), self.from_header).unwrap(), - ); + self.message + .add_header(Header::new_with_value("From".into(), self.from_header).unwrap()); } else { return Err(Error::MissingFrom); } if !self.cc_header.is_empty() { - self.message.add_header( - Header::new_with_value( - "Cc".into(), - self.cc_header, - ).unwrap(), - ); + self.message.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap()); } if !self.reply_to_header.is_empty() { self.message.add_header( @@ -872,36 +836,27 @@ impl EmailBuilder { } if !self.date_issued { - self.message.add_header(( - "Date", - Tm::rfc822z(&now()).to_string().as_ref(), - )); + self.message.add_header(("Date", Tm::rfc822z(&now()).to_string().as_ref())); } self.message.add_header(("MIME-Version", "1.0")); let message_id = Uuid::new_v4(); - if let Ok(header) = Header::new_with_value( - "Message-ID".to_string(), - format!("<{}.lettre@localhost>", message_id), - ) - { + if let Ok(header) = Header::new_with_value("Message-ID".to_string(), + format!("<{}.lettre@localhost>", message_id)) { self.message.add_header(header) } - Ok(Email { - message: self.message.build().as_string().into_bytes(), - envelope: envelope, - message_id: message_id, - }) + Ok(Email { message: self.message.build().as_string().into_bytes(), + envelope: envelope, + message_id: message_id, }) } } impl<'a> SendableEmail<'a, &'a [u8]> for Email { fn to(&self) -> Vec { - self.envelope - .to + self.envelope.to .iter() .map(|x| EmailAddress::new(x.clone())) .collect() @@ -957,56 +912,46 @@ mod test { let email_builder = SimpleEmail::default(); let date_now = now(); - let email = email_builder - .to("user@localhost") - .from("user@localhost") - .cc(("cc@localhost", "Alias")) - .reply_to("reply@localhost") - .text("Hello World!") - .date(date_now.clone()) - .subject("Hello") - .header(("X-test", "value")) - .into_email() - .unwrap(); + let email = email_builder.to("user@localhost") + .from("user@localhost") + .cc(("cc@localhost", "Alias")) + .reply_to("reply@localhost") + .text("Hello World!") + .date(date_now.clone()) + .subject("Hello") + .header(("X-test", "value")) + .into_email() + .unwrap(); - assert_eq!( - format!("{}", String::from_utf8_lossy(email.message().as_ref())), - format!( - "Subject: Hello\r\nContent-Type: text/plain; \ - charset=utf-8\r\nX-test: value\r\nTo: \r\nFrom: \ - \r\nCc: \"Alias\" \r\nReply-To: \ - \r\nDate: {}\r\nMIME-Version: 1.0\r\nMessage-ID: \ - <{}.lettre@localhost>\r\n\r\nHello World!\r\n", - date_now.rfc822z(), - email.message_id() - ) - ); + assert_eq!(format!("{}", String::from_utf8_lossy(email.message().as_ref())), + format!("Subject: Hello\r\nContent-Type: text/plain; \ + charset=utf-8\r\nX-test: value\r\nTo: \r\nFrom: \ + \r\nCc: \"Alias\" \r\nReply-To: \ + \r\nDate: {}\r\nMIME-Version: 1.0\r\nMessage-ID: \ + <{}.lettre@localhost>\r\n\r\nHello World!\r\n", + date_now.rfc822z(), + email.message_id())); } #[test] fn test_multiple_from() { let email_builder = EmailBuilder::new(); let date_now = now(); - let email = email_builder - .to("anna@example.com") - .from("dieter@example.com") - .from("joachim@example.com") - .date(&date_now) - .subject("Invitation") - .body("We invite you!") - .build() - .unwrap(); - assert_eq!( - format!("{}", String::from_utf8_lossy(email.message().as_ref())), - format!( - "Date: {}\r\nSubject: Invitation\r\nSender: \ - \r\nTo: \r\nFrom: \ - , \r\nMIME-Version: \ - 1.0\r\nMessage-ID: <{}.lettre@localhost>\r\n\r\nWe invite you!\r\n", - date_now.rfc822z(), - email.message_id() - ) - ); + let email = email_builder.to("anna@example.com") + .from("dieter@example.com") + .from("joachim@example.com") + .date(&date_now) + .subject("Invitation") + .body("We invite you!") + .build() + .unwrap(); + assert_eq!(format!("{}", String::from_utf8_lossy(email.message().as_ref())), + format!("Date: {}\r\nSubject: Invitation\r\nSender: \ + \r\nTo: \r\nFrom: \ + , \r\nMIME-Version: \ + 1.0\r\nMessage-ID: <{}.lettre@localhost>\r\n\r\nWe invite you!\r\n", + date_now.rfc822z(), + email.message_id())); } #[test] @@ -1014,31 +959,26 @@ mod test { let email_builder = EmailBuilder::new(); 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") - .header(("X-test", "value")) - .build() - .unwrap(); + 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") + .header(("X-test", "value")) + .build() + .unwrap(); - assert_eq!( - format!("{}", String::from_utf8_lossy(email.message().as_ref())), - format!( - "Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \ - \r\nTo: \r\nFrom: \ - \r\nCc: \"Alias\" \r\nReply-To: \ - \r\nMIME-Version: 1.0\r\nMessage-ID: \ - <{}.lettre@localhost>\r\n\r\nHello World!\r\n", - date_now.rfc822z(), - email.message_id() - ) - ); + assert_eq!(format!("{}", String::from_utf8_lossy(email.message().as_ref())), + format!("Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \ + \r\nTo: \r\nFrom: \ + \r\nCc: \"Alias\" \r\nReply-To: \ + \r\nMIME-Version: 1.0\r\nMessage-ID: \ + <{}.lettre@localhost>\r\n\r\nHello World!\r\n", + date_now.rfc822z(), + email.message_id())); } #[test] @@ -1046,29 +986,24 @@ mod test { let email_builder = EmailBuilder::new(); let date_now = now(); - let email = email_builder - .to("user@localhost") - .from("user@localhost") - .cc(("cc@localhost", "Alias")) - .bcc("bcc@localhost") - .reply_to("reply@localhost") - .sender("sender@localhost") - .body("Hello World!") - .date(&date_now) - .subject("Hello") - .header(("X-test", "value")) - .build() - .unwrap(); + let email = email_builder.to("user@localhost") + .from("user@localhost") + .cc(("cc@localhost", "Alias")) + .bcc("bcc@localhost") + .reply_to("reply@localhost") + .sender("sender@localhost") + .body("Hello World!") + .date(&date_now) + .subject("Hello") + .header(("X-test", "value")) + .build() + .unwrap(); assert_eq!(email.from().to_string(), "sender@localhost".to_string()); - assert_eq!( - email.to(), - vec![ - EmailAddress::new("user@localhost".to_string()), - EmailAddress::new("cc@localhost".to_string()), - EmailAddress::new("bcc@localhost".to_string()), - ] - ); + assert_eq!(email.to(), + vec![EmailAddress::new("user@localhost".to_string()), + EmailAddress::new("cc@localhost".to_string()), + EmailAddress::new("bcc@localhost".to_string())]); } } diff --git a/rustfmt.toml b/rustfmt.toml index c10d7f1..2ffbced 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,7 @@ write_mode = "Overwrite" reorder_imports = true reorder_imported_names = true +reorder_imports_in_group = true +struct_field_align_threshold = 20 +indent_style = "Visual" +chain_indent = "Visual" diff --git a/website/content/sending-messages/smtp.md b/website/content/sending-messages/smtp.md index b186ac0..e4fdec6 100644 --- a/website/content/sending-messages/smtp.md +++ b/website/content/sending-messages/smtp.md @@ -98,15 +98,15 @@ use lettre::smtp::commands::*; let mut email_client: Client = Client::new(); let _ = email_client.connect(&("localhost", SMTP_PORT), None); -let _ = email_client.smtp_command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); -let _ = email_client.smtp_command( +let _ = email_client.command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); +let _ = email_client.command( MailCommand::new(Some(EmailAddress::new("user@example.com".to_string())), vec![]) ); -let _ = email_client.smtp_command( +let _ = email_client.command( RcptCommand::new(EmailAddress::new("user@example.org".to_string()), vec![]) ); -let _ = email_client.smtp_command(DataCommand); +let _ = email_client.command(DataCommand); let _ = email_client.message(Box::new("Test email".as_bytes())); -let _ = email_client.smtp_command(QuitCommand); +let _ = email_client.command(QuitCommand); ```