diff --git a/src/transport/smtp/authentication.rs b/src/transport/smtp/authentication.rs index 7562686..bf30771 100644 --- a/src/transport/smtp/authentication.rs +++ b/src/transport/smtp/authentication.rs @@ -98,13 +98,17 @@ impl Mechanism { let decoded_challenge = challenge .ok_or_else(|| error::client("This mechanism does expect a challenge"))?; - if ["User Name", "Username:", "Username", "User Name\0"] - .contains(&decoded_challenge) - { + if contains_ignore_ascii_case( + decoded_challenge, + ["User Name", "Username:", "Username", "User Name\0"], + ) { return Ok(credentials.authentication_identity.clone()); } - if ["Password", "Password:", "Password\0"].contains(&decoded_challenge) { + if contains_ignore_ascii_case( + decoded_challenge, + ["Password", "Password:", "Password\0"], + ) { return Ok(credentials.secret.clone()); } @@ -121,6 +125,15 @@ impl Mechanism { } } +fn contains_ignore_ascii_case<'a>( + haystack: &str, + needles: impl IntoIterator, +) -> bool { + needles + .into_iter() + .any(|item| item.eq_ignore_ascii_case(haystack)) +} + #[cfg(test)] mod test { use super::{Credentials, Mechanism}; @@ -155,6 +168,23 @@ mod test { assert!(mechanism.response(&credentials, None).is_err()); } + #[test] + fn test_login_case_insensitive() { + let mechanism = Mechanism::Login; + + let credentials = Credentials::new("alice".to_owned(), "wonderland".to_owned()); + + 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()); + } + #[test] fn test_xoauth2() { let mechanism = Mechanism::Xoauth2;