Repository: lettre/lettre (language: rust)

Issue:
No Auth header at all causes no supported mechanisms to be reported
**Describe the bug**
Some mail servers like Mailcow don't send an `AUTH ...` header when querying features, which results in lettre believing the server does not support any auth methods.

**To Reproduce**
_Any code using lettre sending mail via SMTP_

**Expected behavior**
Hard to decide, maybe lettre should assume `PLAIN` is supported when the server does not specify the supported auth mechanisms?
Maybe add an option to force a specific auth mechanism?

**Environment (please complete the following information):**
- 0.9.2 (lettre & lettre_email)
- Arch Linux 64bit, Mail server: Mailcow (Postcow)

**Additional context**
It isn't a requirement for the mail server to specifiy `AUTH PLAIN` in the features, so lettre could possible assume its supported if no `AUTH` is sent at all.


Discussion / hints:

Fix patch:
diff --git a/lettre/src/smtp/mod.rs b/lettre/src/smtp/mod.rs
index 58c8d1f4c..b537c95ba 100644
--- a/lettre/src/smtp/mod.rs
+++ b/lettre/src/smtp/mod.rs
@@ -96,6 +96,8 @@ pub struct SmtpClient {
     smtp_utf8: bool,
     /// Optional enforced authentication mechanism
     authentication_mechanism: Option<Mechanism>,
+    /// Force use of the set authentication mechanism even if server does not report to support it
+    force_set_auth: bool,
     /// Define network timeout
     /// It can be changed later for specific needs (like a different timeout for each SMTP command)
     timeout: Option<Duration>,
@@ -125,6 +127,7 @@ impl SmtpClient {
                 connection_reuse: ConnectionReuseParameters::NoReuse,
                 hello_name: ClientId::hostname(),
                 authentication_mechanism: None,
+                force_set_auth: false,
                 timeout: Some(Duration::new(60, 0)),
             }),
             None => Err(Error::Resolution),
@@ -182,6 +185,12 @@ impl SmtpClient {
         self
     }
 
+    /// Set if the set authentication mechanism should be force
+    pub fn force_set_auth(mut self, force: bool) -> SmtpClient {
+        self.force_set_auth = force;
+        self
+    }
+
     /// Set the timeout duration
     pub fn timeout(mut self, timeout: Option<Duration>) -> SmtpClient {
         self.timeout = timeout;
@@ -311,33 +320,42 @@ impl<'a> SmtpTransport {
         if self.client_info.credentials.is_some() {
             let mut found = false;
 
-            // Compute accepted mechanism
-            let accepted_mechanisms = match self.client_info.authentication_mechanism {
-                Some(mechanism) => vec![mechanism],
-                None => {
-                    if self.client.is_encrypted() {
-                        DEFAULT_ENCRYPTED_MECHANISMS.to_vec()
-                    } else {
-                        DEFAULT_UNENCRYPTED_MECHANISMS.to_vec()
+            if !self.client_info.force_set_auth {
+                // Compute accepted mechanism
+                let accepted_mechanisms = match self.client_info.authentication_mechanism {
+                    Some(mechanism) => vec![mechanism],
+                    None => {
+                        if self.client.is_encrypted() {
+                            DEFAULT_ENCRYPTED_MECHANISMS.to_vec()
+                        } else {
+                            DEFAULT_UNENCRYPTED_MECHANISMS.to_vec()
+                        }
                     }
-                }
-            };
-
-            for mechanism in accepted_mechanisms {
-                if self
-                    .server_info
-                    .as_ref()
-                    .unwrap()
-                    .supports_auth_mechanism(mechanism)
-                {
-                    found = true;
-                    try_smtp!(
+                };
+
+                for mechanism in accepted_mechanisms {
+                    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
                     );
-                    break;
+                        break;
+                    }
                 }
+            } else {
+                try_smtp!(
+                self.client.auth(self.client_info.authentication_mechanism.expect(
+                    "force_set_auth set to true, but no authentication mechanism set"), self.client_info.credentials.as_ref().unwrap(),),
+                    self
+                );
+                found = true;
             }
 
             if !found {


Test patch: