diff --git a/src/imap.rs b/src/imap.rs index 6378137b1b..b70cce5e69 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -142,6 +142,18 @@ pub(crate) struct ServerMetadata { pub ice_servers_expiration_timestamp: i64, } +/// Selects the most actionable error from failed IMAP connection candidates. +/// +/// A login error proves that a connection succeeded, so it takes precedence over network errors. +fn select_connect_error( + first_connection_error: Option, + first_login_error: Option, +) -> anyhow::Error { + first_login_error + .or(first_connection_error) + .unwrap_or_else(|| format_err!("No IMAP connection candidates provided")) +} + struct UidGrouper> { inner: Peekable, } @@ -310,7 +322,8 @@ impl Imap { self.conn_backoff_ms = max(BACKOFF_MIN_MS, self.conn_backoff_ms); let login_params = prioritize_server_login_params(&context.sql, &self.lp, "imap").await?; - let mut first_error = None; + let mut first_connection_error = None; + let mut first_login_error = None; 'candidate: for lp in login_params { info!(context, "IMAP trying to connect to {}.", lp.connection); let connection_candidate = lp.connection.clone(); @@ -326,7 +339,7 @@ impl Imap { Ok(client) => client, Err(err) => { warn!(context, "{err:#}."); - first_error.get_or_insert(err); + first_connection_error.get_or_insert(err); continue 'candidate; } }; @@ -406,7 +419,7 @@ impl Imap { let message = stock_str::cannot_login(context, &imap_user); warn!(context, "IMAP failed to login: {err:#}."); - first_error.get_or_insert(format_err!("{message} ({err:#})")); + first_login_error.get_or_insert(format_err!("{message} ({err:#})")); // If it looks like the password is wrong, send a notification: let _lock = context.wrong_pw_warning_mutex.lock().await; @@ -442,7 +455,10 @@ impl Imap { } } - Err(first_error.unwrap_or_else(|| format_err!("No IMAP connection candidates provided"))) + Err(select_connect_error( + first_connection_error, + first_login_error, + )) } /// Prepare a new IMAP session. diff --git a/src/imap/imap_tests.rs b/src/imap/imap_tests.rs index bb4c22fc70..b6b0ca3cc5 100644 --- a/src/imap/imap_tests.rs +++ b/src/imap/imap_tests.rs @@ -1,6 +1,16 @@ use super::*; use crate::test_utils::TestContext; +#[test] +fn test_connect_prefers_login_error() { + let connection_error = format_err!("All connection attempts failed"); + let login_error = format_err!("Cannot login, please check the password"); + + let error = select_connect_error(Some(connection_error), Some(login_error)); + + assert_eq!(error.to_string(), "Cannot login, please check the password"); +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_set_uid_next_validity() { let t = TestContext::new_alice().await;