style(all): Fix LOGIN auth detection and improve response tests
This commit is contained in:
@@ -67,7 +67,7 @@ impl Display for ServerInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ServerInfo {
|
impl ServerInfo {
|
||||||
/// Parses a response to create a `ServerInfo`
|
/// Parses a EHLO response to create a `ServerInfo`
|
||||||
pub fn from_response(response: &Response) -> Result<ServerInfo, Error> {
|
pub fn from_response(response: &Response) -> Result<ServerInfo, Error> {
|
||||||
let name = match response.first_word() {
|
let name = match response.first_word() {
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
@@ -77,7 +77,6 @@ impl ServerInfo {
|
|||||||
let mut features: HashSet<Extension> = HashSet::new();
|
let mut features: HashSet<Extension> = HashSet::new();
|
||||||
|
|
||||||
for line in response.message() {
|
for line in response.message() {
|
||||||
|
|
||||||
let splitted: Vec<&str> = line.split_whitespace().collect();
|
let splitted: Vec<&str> = line.split_whitespace().collect();
|
||||||
match splitted[0] {
|
match splitted[0] {
|
||||||
"8BITMIME" => {
|
"8BITMIME" => {
|
||||||
@@ -95,6 +94,9 @@ impl ServerInfo {
|
|||||||
"PLAIN" => {
|
"PLAIN" => {
|
||||||
features.insert(Extension::Authentication(Mechanism::Plain));
|
features.insert(Extension::Authentication(Mechanism::Plain));
|
||||||
}
|
}
|
||||||
|
"LOGIN" => {
|
||||||
|
features.insert(Extension::Authentication(Mechanism::Login));
|
||||||
|
}
|
||||||
"CRAM-MD5" => {
|
"CRAM-MD5" => {
|
||||||
features.insert(Extension::Authentication(Mechanism::CramMd5));
|
features.insert(Extension::Authentication(Mechanism::CramMd5));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,13 +156,13 @@ pub enum SecurityLevel {
|
|||||||
EncryptedWrapper,
|
EncryptedWrapper,
|
||||||
/// Only send an email on encrypted connection (with STARTTLS)
|
/// Only send an email on encrypted connection (with STARTTLS)
|
||||||
///
|
///
|
||||||
/// Recommended mode, prevents MITM when used with verified certificates.
|
/// Default mode, prevents MITM when used with verified certificates.
|
||||||
AlwaysEncrypt,
|
AlwaysEncrypt,
|
||||||
/// Use TLS when available (with STARTTLS)
|
/// Use TLS when available (with STARTTLS)
|
||||||
///
|
///
|
||||||
/// Default mode.
|
/// Should be used when not possible to always encrypt the connection
|
||||||
Opportunistic,
|
Opportunistic,
|
||||||
/// Never use TLS
|
/// Never use encryption
|
||||||
NeverEncrypt,
|
NeverEncrypt,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ impl EmailTransport<SmtpResult> for SmtpTransport {
|
|||||||
// Login is obsolete so try it last
|
// Login is obsolete so try it last
|
||||||
vec![Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login]
|
vec![Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login]
|
||||||
} else {
|
} else {
|
||||||
// If not encrypted, do not allow clear-text passwords
|
// If not encrypted, do not allow clear-text passwords by default
|
||||||
vec![Mechanism::CramMd5]
|
vec![Mechanism::CramMd5]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ impl FromStr for Code {
|
|||||||
impl Code {
|
impl Code {
|
||||||
/// Creates a new `Code` structure
|
/// Creates a new `Code` structure
|
||||||
pub fn new(severity: Severity, category: Category, detail: u8) -> Code {
|
pub fn new(severity: Severity, category: Category, detail: u8) -> Code {
|
||||||
|
if detail > 9 {
|
||||||
|
panic!("The detail code must be between 0 and 9");
|
||||||
|
}
|
||||||
|
|
||||||
Code {
|
Code {
|
||||||
severity: severity,
|
severity: severity,
|
||||||
category: category,
|
category: category,
|
||||||
@@ -174,7 +178,7 @@ impl ResponseParser {
|
|||||||
|
|
||||||
if line.len() < 3 {
|
if line.len() < 3 {
|
||||||
return Err(Error::ResponseParsing(
|
return Err(Error::ResponseParsing(
|
||||||
"Wrong code length (should be 3 digit)",
|
"Incorrect response code (should be 3 digits)",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,13 +287,9 @@ impl Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns only the line word of the message if possible
|
/// Returns only the line of the message if possible
|
||||||
pub fn first_line(&self) -> Option<&str> {
|
pub fn first_line(&self) -> Option<&str> {
|
||||||
if self.message.is_empty() {
|
self.message.first().map(String::as_str)
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(&self.message[0])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +308,7 @@ mod test {
|
|||||||
Severity::TransientNegativeCompletion
|
Severity::TransientNegativeCompletion
|
||||||
);
|
);
|
||||||
assert!("1".parse::<Severity>().is_err());
|
assert!("1".parse::<Severity>().is_err());
|
||||||
|
assert!("51".parse::<Severity>().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -343,6 +344,16 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_code_new_panic() {
|
||||||
|
let _ = Code::new(
|
||||||
|
Severity::TransientNegativeCompletion,
|
||||||
|
Category::Connections,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_code_from_str() {
|
fn test_code_from_str() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -353,6 +364,12 @@ mod test {
|
|||||||
detail: 1,
|
detail: 1,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
assert!("2222".parse::<Code>().is_err());
|
||||||
|
assert!("aaa".parse::<Code>().is_err());
|
||||||
|
assert!("-32".parse::<Code>().is_err());
|
||||||
|
assert!("-333".parse::<Code>().is_err());
|
||||||
|
assert!("".parse::<Code>().is_err());
|
||||||
|
assert!("292".parse::<Code>().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -707,4 +724,82 @@ mod test {
|
|||||||
None
|
None
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_response_first_line() {
|
||||||
|
assert_eq!(
|
||||||
|
Response::new(
|
||||||
|
Code {
|
||||||
|
severity: "2".parse::<Severity>().unwrap(),
|
||||||
|
category: "4".parse::<Category>().unwrap(),
|
||||||
|
detail: 1,
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
"me".to_string(),
|
||||||
|
"8BITMIME".to_string(),
|
||||||
|
"SIZE 42".to_string(),
|
||||||
|
],
|
||||||
|
).first_line(),
|
||||||
|
Some("me")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Response::new(
|
||||||
|
Code {
|
||||||
|
severity: "2".parse::<Severity>().unwrap(),
|
||||||
|
category: "4".parse::<Category>().unwrap(),
|
||||||
|
detail: 1,
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
"me mo".to_string(),
|
||||||
|
"8BITMIME".to_string(),
|
||||||
|
"SIZE 42".to_string(),
|
||||||
|
],
|
||||||
|
).first_line(),
|
||||||
|
Some("me mo")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Response::new(
|
||||||
|
Code {
|
||||||
|
severity: "2".parse::<Severity>().unwrap(),
|
||||||
|
category: "4".parse::<Category>().unwrap(),
|
||||||
|
detail: 1,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
).first_line(),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Response::new(
|
||||||
|
Code {
|
||||||
|
severity: "2".parse::<Severity>().unwrap(),
|
||||||
|
category: "4".parse::<Category>().unwrap(),
|
||||||
|
detail: 1,
|
||||||
|
},
|
||||||
|
vec![" ".to_string()],
|
||||||
|
).first_line(),
|
||||||
|
Some(" ")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Response::new(
|
||||||
|
Code {
|
||||||
|
severity: "2".parse::<Severity>().unwrap(),
|
||||||
|
category: "4".parse::<Category>().unwrap(),
|
||||||
|
detail: 1,
|
||||||
|
},
|
||||||
|
vec![" ".to_string()],
|
||||||
|
).first_line(),
|
||||||
|
Some(" ")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Response::new(
|
||||||
|
Code {
|
||||||
|
severity: "2".parse::<Severity>().unwrap(),
|
||||||
|
category: "4".parse::<Category>().unwrap(),
|
||||||
|
detail: 1,
|
||||||
|
},
|
||||||
|
vec!["".to_string()],
|
||||||
|
).first_line(),
|
||||||
|
Some("")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user