feat(transport): Make use of hex and rust-crypto for crammd5 an optionnal feature
This commit is contained in:
@@ -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"]
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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!(
|
||||
"{}",
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<SmtpResult> 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()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
extern crate lettre;
|
||||
|
||||
mod transport_smtp;
|
||||
mod transport_sendmail;
|
||||
mod transport_stub;
|
||||
#[cfg(feature = "file-transport")]
|
||||
mod transport_file;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user