git.lucas.co / cce-mail
mail client (IMAP/SMTP)
git clone https://git.lucas.co/cce-mail.git

commitc4572811035a21b345865795f4f949a608270754
parent440435fd97
authorLucas Galante <[email protected]>
date2026-08-31 10:28
fix: don't dial out with no password from the background IMAP ops

set_seen_on_server, fetch_attachment, and delete_on_server called
open_imap_session unguarded, so a non-OAuth account whose keyring never
yielded a password reached Gmail and got the baffling "IMAP Login
failed: No Response: Empty username or password." in the session log.
Same guard shape as start_sync and fetch_html_part: skip the connection
and say on stderr what is actually wrong — no password, keyring locked
or entry missing. fetch_attachment also reports through its
AttachmentFetched channel so the user sees the toast instead of a
silent no-op.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/main.rs | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index e7a8cea..da99602 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2055,6 +2055,13 @@ fn delete_on_server(
         if is_mock_account(&account) {
             return;
         }
+        // Same guard as start_sync: dialing out with an empty password gets
+        // the server's baffling "Empty username or password" instead of the
+        // actual problem — the keyring never yielded the credential.
+        if !account.is_oauth && account.password.is_empty() {
+            eprintln!("cce-mail: server delete: no password (keyring locked or entry missing); not connecting");
+            return;
+        }
         let Some(mut session) = open_imap_session(&mut account, &sender, true) else {
             return;
         };
@@ -2137,6 +2144,14 @@ fn fetch_attachment(
             report(&sender, Err("This account has no server copy".to_string()));
             return;
         }
+        // Same guard as start_sync: dialing out with an empty password gets
+        // the server's baffling "Empty username or password" instead of the
+        // actual problem — the keyring never yielded the credential.
+        if !account.is_oauth && account.password.is_empty() {
+            eprintln!("cce-mail: attachment fetch: no password (keyring locked or entry missing); not connecting");
+            report(&sender, Err("No password — is the keyring unlocked?".to_string()));
+            return;
+        }
         let Some(mut session) = open_imap_session(&mut account, &sender, false) else {
             report(&sender, Err("IMAP connection failed".to_string()));
             return;
@@ -2262,6 +2277,13 @@ fn set_seen_on_server(mut account: AccountInfo, uid: u32, seen: bool, sender: ca
         if is_mock_account(&account) {
             return;
         }
+        // Same guard as start_sync: dialing out with an empty password gets
+        // the server's baffling "Empty username or password" instead of the
+        // actual problem — the keyring never yielded the credential.
+        if !account.is_oauth && account.password.is_empty() {
+            eprintln!("cce-mail: seen-flag push: no password (keyring locked or entry missing); not connecting");
+            return;
+        }
         let Some(mut session) = open_imap_session(&mut account, &sender, false) else {
             return;
         };