diff --git a/lettre/Cargo.toml b/lettre/Cargo.toml index ea556dc..446bb1a 100644 --- a/lettre/Cargo.toml +++ b/lettre/Cargo.toml @@ -19,8 +19,8 @@ bufstream = "^0.1" log = "^0.3" native-tls = "^0.1" base64 = "^0.6" -hex = "^0.2" -rust-crypto = "^0.2" +hex = { version = "^0.2", optional = true } +rust-crypto = { version = "^0.2", optional = true } serde = { version = "^1.0", optional = true } serde_json = { version = "^1.0", optional = true } serde_derive = { version = "^1.0", optional = true } @@ -29,7 +29,8 @@ serde_derive = { version = "^1.0", optional = true } env_logger = "^0.4" [features] -default = ["file-transport"] +default = ["file-transport", "crammd5-auth"] unstable = [] serde-impls = ["serde", "serde_derive"] file-transport = ["serde-impls", "serde_json"] +crammd5-auth = ["rust-crypto", "hex"] \ No newline at end of file diff --git a/lettre/src/lib.rs b/lettre/src/lib.rs index dd02188..b7402f1 100644 --- a/lettre/src/lib.rs +++ b/lettre/src/lib.rs @@ -9,7 +9,9 @@ #[macro_use] extern crate log; extern crate base64; +#[cfg(feature = "crammd5-auth")] extern crate hex; +#[cfg(feature = "crammd5-auth")] extern crate crypto; extern crate bufstream; extern crate native_tls; diff --git a/lettre/src/smtp/authentication.rs b/lettre/src/smtp/authentication.rs index 240ffac..0af35ff 100644 --- a/lettre/src/smtp/authentication.rs +++ b/lettre/src/smtp/authentication.rs @@ -1,14 +1,37 @@ //! Provides authentication mechanisms +#[cfg(feature = "crammd5-auth")] use crypto::hmac::Hmac; +#[cfg(feature = "crammd5-auth")] use crypto::mac::Mac; +#[cfg(feature = "crammd5-auth")] use crypto::md5::Md5; +#[cfg(feature = "crammd5-auth")] use hex::ToHex; use smtp::NUL; use smtp::error::Error; use std::fmt; use std::fmt::{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] = + &[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]; + +/// Accepted authentication mecanisms on an unencrypted connection +#[cfg(feature = "crammd5-auth")] +pub const DEFAULT_UNENCRYPTED_MECHANISMS: &'static [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] = &[]; + + /// Convertable to user credentials pub trait IntoCredentials { /// Converts to a `Credentials` struct @@ -57,6 +80,7 @@ pub enum Mechanism { Login, /// CRAM-MD5 authentication mechanism /// RFC 2195: https://tools.ietf.org/html/rfc2195 + #[cfg(feature = "crammd5-auth")] CramMd5, } @@ -68,6 +92,7 @@ impl Display for Mechanism { match *self { Mechanism::Plain => "PLAIN", Mechanism::Login => "LOGIN", + #[cfg(feature = "crammd5-auth")] Mechanism::CramMd5 => "CRAM-MD5", } ) @@ -76,10 +101,12 @@ impl Display for Mechanism { impl Mechanism { /// Does the mechanism supports initial response + #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] pub fn supports_initial_response(&self) -> bool { match *self { Mechanism::Plain => true, - Mechanism::Login | + Mechanism::Login => false, + #[cfg(feature = "crammd5-auth")] Mechanism::CramMd5 => false, } } @@ -120,6 +147,7 @@ impl Mechanism { Err(Error::Client("Unrecognized challenge")) } + #[cfg(feature = "crammd5-auth")] Mechanism::CramMd5 => { let decoded_challenge = match challenge { Some(challenge) => challenge, @@ -174,6 +202,7 @@ mod test { } #[test] + #[cfg(feature = "crammd5-auth")] fn test_cram_md5() { let mechanism = Mechanism::CramMd5; diff --git a/lettre/src/smtp/commands.rs b/lettre/src/smtp/commands.rs index 48a74c8..ce79acb 100644 --- a/lettre/src/smtp/commands.rs +++ b/lettre/src/smtp/commands.rs @@ -317,7 +317,9 @@ impl AuthCommand { mod test { use super::*; use smtp::extension::MailBodyParameter; + #[cfg(feature = "crammd5-auth")] use smtp::response::Code; + #[cfg(feature = "crammd5-auth")] use std::str::FromStr; #[test] @@ -395,6 +397,7 @@ mod test { ), "AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=\r\n" ); + #[cfg(feature = "crammd5-auth")] assert_eq!( format!( "{}", @@ -413,6 +416,7 @@ mod test { ), "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 0fccee4..e010508 100644 --- a/lettre/src/smtp/extension.rs +++ b/lettre/src/smtp/extension.rs @@ -128,6 +128,7 @@ impl ServerInfo { "LOGIN" => { features.insert(Extension::Authentication(Mechanism::Login)); } + #[cfg(feature = "crammd5-auth")] "CRAM-MD5" => { features.insert(Extension::Authentication(Mechanism::CramMd5)); } @@ -330,6 +331,7 @@ mod test { assert!(server_info.supports_feature(Extension::EightBitMime)); assert!(!server_info.supports_feature(Extension::StartTls)); + #[cfg(feature = "crammd5-auth")] assert!(!server_info.supports_auth_mechanism(Mechanism::CramMd5)); let response2 = Response::new( @@ -351,6 +353,7 @@ mod test { assert!(features2.insert( Extension::Authentication(Mechanism::Plain), )); + #[cfg(feature = "crammd5-auth")] assert!(features2.insert( Extension::Authentication(Mechanism::CramMd5), )); @@ -364,6 +367,7 @@ mod test { assert!(server_info2.supports_feature(Extension::EightBitMime)); assert!(server_info2.supports_auth_mechanism(Mechanism::Plain)); + #[cfg(feature = "crammd5-auth")] assert!(server_info2.supports_auth_mechanism(Mechanism::CramMd5)); assert!(!server_info2.supports_feature(Extension::StartTls)); } diff --git a/lettre/src/smtp/mod.rs b/lettre/src/smtp/mod.rs index 634c5f6..edb6f20 100644 --- a/lettre/src/smtp/mod.rs +++ b/lettre/src/smtp/mod.rs @@ -68,7 +68,7 @@ //! // Enable SMTPUTF8 if the server supports it //! .smtp_utf8(true) //! // Configure expected authentication mechanism -//! .authentication_mechanism(Mechanism::CramMd5) +//! .authentication_mechanism(Mechanism::Plain) //! // Enable connection reuse //! .connection_reuse(true).build(); //! @@ -110,7 +110,8 @@ use EmailTransport; use SendableEmail; use native_tls::TlsConnector; -use smtp::authentication::{Credentials, Mechanism}; +use smtp::authentication::{Credentials, DEFAULT_ENCRYPTED_MECHANISMS, + DEFAULT_UNENCRYPTED_MECHANISMS, Mechanism}; use smtp::client::Client; use smtp::commands::*; use smtp::error::{Error, SmtpResult}; @@ -458,13 +459,9 @@ impl EmailTransport for SmtpTransport { Some(mechanism) => vec![mechanism], None => { if self.client.is_encrypted() { - // If encrypted, allow all mechanisms, with a preference for the - // simplest - // Login is obsolete so try it last - vec![Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login] + DEFAULT_ENCRYPTED_MECHANISMS.to_vec() } else { - // If not encrypted, do not allow clear-text passwords by default - vec![Mechanism::CramMd5] + DEFAULT_UNENCRYPTED_MECHANISMS.to_vec() } } }; diff --git a/lettre/tests/lib.rs b/lettre/tests/lib.rs deleted file mode 100644 index 6b9b76d..0000000 --- a/lettre/tests/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate lettre; - -mod transport_smtp; -mod transport_sendmail; -mod transport_stub; -#[cfg(feature = "file-transport")] -mod transport_file; diff --git a/lettre/tests/transport_file.rs b/lettre/tests/transport_file.rs index 1c1497d..dc2431f 100644 --- a/lettre/tests/transport_file.rs +++ b/lettre/tests/transport_file.rs @@ -1,37 +1,42 @@ extern crate lettre; -use lettre::{EmailAddress, EmailTransport, SendableEmail, SimpleSendableEmail}; +#[cfg(test)] #[cfg(feature = "file-transport")] -use lettre::file::FileEmailTransport; -use std::env::temp_dir; -use std::fs::File; -use std::fs::remove_file; -use std::io::Read; +mod test { -#[test] -#[cfg(feature = "file-transport")] -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 result = sender.send(email.clone()); - assert!(result.is_ok()); + use lettre::{EmailAddress, EmailTransport, SendableEmail, SimpleSendableEmail}; + #[cfg(feature = "file-transport")] + use lettre::file::FileEmailTransport; + use std::env::temp_dir; + use std::fs::File; + use std::fs::remove_file; + use std::io::Read; - let message_id = email.message_id(); - let file = format!("{}/{}.txt", temp_dir().to_str().unwrap(), message_id); - let mut f = File::open(file.clone()).unwrap(); - let mut buffer = String::new(); - let _ = f.read_to_string(&mut buffer); + #[test] + #[cfg(feature = "file-transport")] + 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 result = sender.send(email.clone()); + assert!(result.is_ok()); - assert_eq!( - buffer, - "{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\ - \"message_id\":\"file_id\",\"message\":\"Hello file\"}" - ); + let message_id = email.message_id(); + let file = format!("{}/{}.txt", temp_dir().to_str().unwrap(), message_id); + let mut f = File::open(file.clone()).unwrap(); + let mut buffer = String::new(); + let _ = f.read_to_string(&mut buffer); - remove_file(file).unwrap(); + assert_eq!( + buffer, + "{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\ + \"message_id\":\"file_id\",\"message\":\"Hello file\"}" + ); + + remove_file(file).unwrap(); + } }