feat: do not create device messages for IMAP authentication errors

Authentication failures may happen because of internal server errors.
Device message saying "Please check if the email address and the password are correct"
was written for classic email setups when the user knows the password.
For users of chatmail relays this message is not actionable,
but still appears when relay fails to check the password.
This commit is contained in:
link2xt
2026-08-26 13:53:02 +00:00
committed by l
parent 49496756e4
commit 7421e67ab6
8 changed files with 14 additions and 84 deletions
+1 -4
View File
@@ -6672,10 +6672,7 @@ void dc_event_unref(dc_event_t* event);
/// Used as the name for the corresponding chatlist entry. /// Used as the name for the corresponding chatlist entry.
#define DC_STR_ARCHIVEDCHATS 40 #define DC_STR_ARCHIVEDCHATS 40
/// "Cannot login as %1$s." /// @deprecated 2026-08-24
///
/// Used in error strings.
/// - %1$s will be replaced by the failing login name
#define DC_STR_CANNOT_LOGIN 60 #define DC_STR_CANNOT_LOGIN 60
/// "Location streaming enabled." /// "Location streaming enabled."
+4 -6
View File
@@ -1133,8 +1133,9 @@ def test_configure_error_msgs_wrong_pw(acfactory):
print(f"Configuration progress: {ev.data1}") print(f"Configuration progress: {ev.data1}")
if ev.data1 == 0: if ev.data1 == 0:
break break
# Password is wrong so it definitely has to say something about "password" # Password is wrong so the error should be about authentication
assert "password" in ev.data2 # and not e.g. connection failure.
assert "Authentication" in ev.data2
ac1.stop_io() ac1.stop_io()
ac1.set_config("mail_pw", "abc") # Wrong mail pw ac1.set_config("mail_pw", "abc") # Wrong mail pw
@@ -1144,10 +1145,7 @@ def test_configure_error_msgs_wrong_pw(acfactory):
print(f"Configuration progress: {ev.data1}") print(f"Configuration progress: {ev.data1}")
if ev.data1 == 0: if ev.data1 == 0:
break break
assert "password" in ev.data2 assert "Authentication" in ev.data2
# Account will continue to work with the old password, so if it becomes wrong, a notification
# must be shown.
assert ac1.get_config("notify_about_wrong_pw") == "1"
def test_configure_error_msgs_invalid_server(acfactory): def test_configure_error_msgs_invalid_server(acfactory):
-6
View File
@@ -343,11 +343,6 @@ pub enum Config {
#[strum(props(default = "0"))] #[strum(props(default = "0"))]
SkipStartMessages, SkipStartMessages,
/// Whether we send a warning if the password is wrong (set to false when we send a warning
/// because we do not want to send a second warning)
#[strum(props(default = "0"))]
NotifyAboutWrongPw,
/// Timestamp of the last time housekeeping was run /// Timestamp of the last time housekeeping was run
LastHousekeeping, LastHousekeeping,
@@ -663,7 +658,6 @@ impl Context {
| Config::MdnsEnabled | Config::MdnsEnabled
| Config::Configured | Config::Configured
| Config::Bot | Config::Bot
| Config::NotifyAboutWrongPw
| Config::SyncMsgs | Config::SyncMsgs
| Config::DisableIdle => { | Config::DisableIdle => {
ensure!( ensure!(
+1 -4
View File
@@ -341,8 +341,6 @@ impl Context {
); );
return Err(error); return Err(error);
}; };
self.set_config_internal(Config::NotifyAboutWrongPw, Some("1"))
.await?;
if provider::legacy_settings_for_addr(&param.addr)?.worse_media_quality if provider::legacy_settings_for_addr(&param.addr)?.worse_media_quality
&& !self.config_exists(Config::MediaQuality).await? && !self.config_exists(Config::MediaQuality).await?
{ {
@@ -567,8 +565,7 @@ pub(crate) async fn configure(
let transport_id = 0; let transport_id = 0;
let (_s, r) = async_channel::bounded(1); let (_s, r) = async_channel::bounded(1);
let mut imap = Imap::new(ctx, transport_id, configured_param.clone(), r).await?; let mut imap = Imap::new(ctx, transport_id, configured_param.clone(), r).await?;
let configuring = true; let imap_session = match imap.connect(ctx).await {
let imap_session = match imap.connect(ctx, configuring).await {
Ok(imap_session) => imap_session, Ok(imap_session) => imap_session,
Err(err) => { Err(err) => {
bail!("{}", nicer_configuration_error(ctx, format!("{err:#}"))); bail!("{}", nicer_configuration_error(ctx, format!("{err:#}")));
-3
View File
@@ -231,8 +231,6 @@ pub struct InnerContext {
/// This is a global mutex-like state for operations which should be modal in the /// This is a global mutex-like state for operations which should be modal in the
/// clients. /// clients.
running_state: RwLock<RunningState>, running_state: RwLock<RunningState>,
/// Mutex to prevent a race condition when a "your pw is wrong" warning is sent, resulting in multiple messages being sent.
pub(crate) wrong_pw_warning_mutex: Mutex<()>,
/// Mutex to prevent running housekeeping or relay management from multiple threads at once. /// Mutex to prevent running housekeeping or relay management from multiple threads at once.
pub(crate) background_task_mutex: Mutex<()>, pub(crate) background_task_mutex: Mutex<()>,
@@ -486,7 +484,6 @@ impl Context {
blobdir, blobdir,
running_state: RwLock::new(Default::default()), running_state: RwLock::new(Default::default()),
sql: Sql::new(dbfile), sql: Sql::new(dbfile),
wrong_pw_warning_mutex: Mutex::new(()),
background_task_mutex: Mutex::new(()), background_task_mutex: Mutex::new(()),
fetch_msgs_mutex: Mutex::new(()), fetch_msgs_mutex: Mutex::new(()),
translated_stockstrings: stockstrings, translated_stockstrings: stockstrings,
-1
View File
@@ -278,7 +278,6 @@ async fn test_get_info_completeness() {
"mail_pw", "mail_pw",
"mail_port", "mail_port",
"mail_security", "mail_security",
"notify_about_wrong_pw",
"selfstatus", "selfstatus",
"send_server", "send_server",
"send_user", "send_user",
+8 -50
View File
@@ -28,7 +28,6 @@ use crate::context::Context;
use crate::ensure_and_debug_assert; use crate::ensure_and_debug_assert;
use crate::events::EventType; use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::log::LogExt;
use crate::log::warn; use crate::log::warn;
use crate::message::{self, Message}; use crate::message::{self, Message};
use crate::mimeparser; use crate::mimeparser;
@@ -37,7 +36,6 @@ use crate::net::session::SessionStream;
use crate::push::encrypt_device_token; use crate::push::encrypt_device_token;
use crate::receive_imf::{ReceivedMsg, from_field_to_contact_id, receive_imf_inner}; use crate::receive_imf::{ReceivedMsg, from_field_to_contact_id, receive_imf_inner};
use crate::scheduler::connectivity::ConnectivityStore; use crate::scheduler::connectivity::ConnectivityStore;
use crate::stock_str;
use crate::tools::{self, create_id, duration_to_str, time}; use crate::tools::{self, create_id, duration_to_str, time};
use crate::transport::{ use crate::transport::{
ConfiguredLoginParam, ConfiguredServerLoginParam, prioritize_server_login_params, ConfiguredLoginParam, ConfiguredServerLoginParam, prioritize_server_login_params,
@@ -87,8 +85,6 @@ pub(crate) struct Imap {
/// Watched folder. /// Watched folder.
pub(crate) folder: String, pub(crate) folder: String,
authentication_failed_once: bool,
pub(crate) connectivity: ConnectivityStore, pub(crate) connectivity: ConnectivityStore,
conn_last_try: tools::Time, conn_last_try: tools::Time,
@@ -234,7 +230,6 @@ impl Imap {
proxy_config, proxy_config,
strict_tls, strict_tls,
folder, folder,
authentication_failed_once: false,
connectivity: Default::default(), connectivity: Default::default(),
conn_last_try: UNIX_EPOCH, conn_last_try: UNIX_EPOCH,
conn_backoff_ms: 0, conn_backoff_ms: 0,
@@ -267,11 +262,7 @@ impl Imap {
/// Calling this function is not enough to perform IMAP operations. Use [`Imap::prepare`] /// Calling this function is not enough to perform IMAP operations. Use [`Imap::prepare`]
/// instead if you are going to actually use connection rather than trying connection /// instead if you are going to actually use connection rather than trying connection
/// parameters. /// parameters.
pub(crate) async fn connect( pub(crate) async fn connect(&mut self, context: &Context) -> Result<Session> {
&mut self,
context: &Context,
configuring: bool,
) -> Result<Session> {
let now = tools::Time::now(); let now = tools::Time::now();
let until_can_send = max( let until_can_send = max(
min(self.conn_last_try, now) min(self.conn_last_try, now)
@@ -342,7 +333,10 @@ impl Imap {
let imap_pw: &str = &self.password; let imap_pw: &str = &self.password;
info!(context, "Logging into IMAP server with LOGIN."); info!(context, "Logging into IMAP server with LOGIN.");
let login_res = client.login(imap_user, imap_pw).await; let login_res = client
.login(imap_user, imap_pw)
.await
.with_context(|| format!("IMAP failed to login as {imap_user}"));
match login_res { match login_res {
Ok((mut session, login_capabilities_opt)) => { Ok((mut session, login_capabilities_opt)) => {
@@ -395,7 +389,6 @@ impl Imap {
let mut lock = context.server_id.write().await; let mut lock = context.server_id.write().await;
lock.clone_from(&session.capabilities.server_id); lock.clone_from(&session.capabilities.server_id);
self.authentication_failed_once = false;
context.emit_event(EventType::ImapConnected(format!( context.emit_event(EventType::ImapConnected(format!(
"IMAP-LOGIN as {}", "IMAP-LOGIN as {}",
lp.user lp.user
@@ -406,42 +399,8 @@ impl Imap {
} }
Err(err) => { Err(err) => {
let imap_user = lp.user.to_owned(); warn!(context, "{err:#}.");
let message = stock_str::cannot_login(context, &imap_user); first_error.get_or_insert(err);
warn!(context, "IMAP failed to login: {err:#}.");
first_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;
if err.to_string().to_lowercase().contains("authentication") {
if self.authentication_failed_once
&& !configuring
&& context.get_config_bool(Config::NotifyAboutWrongPw).await?
{
let mut msg = Message::new_text(message);
if let Err(e) = chat::add_device_msg_with_importance(
context,
None,
Some(&mut msg),
true,
)
.await
{
warn!(context, "Failed to add device message: {e:#}.");
} else {
context
.set_config_internal(Config::NotifyAboutWrongPw, None)
.await
.log_err(context)
.ok();
}
} else {
self.authentication_failed_once = true;
}
} else {
self.authentication_failed_once = false;
}
} }
} }
} }
@@ -454,8 +413,7 @@ impl Imap {
/// This creates a new IMAP connection and ensures /// This creates a new IMAP connection and ensures
/// that folders are created and IMAP capabilities are determined. /// that folders are created and IMAP capabilities are determined.
pub(crate) async fn prepare(&mut self, context: &Context) -> Result<Session> { pub(crate) async fn prepare(&mut self, context: &Context) -> Result<Session> {
let configuring = false; let session = match self.connect(context).await {
let session = match self.connect(context, configuring).await {
Ok(session) => session, Ok(session) => session,
Err(err) => { Err(err) => {
self.connectivity.set_err(context, format!("{err:#}")); self.connectivity.set_err(context, format!("{err:#}"));
-10
View File
@@ -75,11 +75,6 @@ pub enum StockMessage {
#[strum(props(fallback = "Archived chats"))] #[strum(props(fallback = "Archived chats"))]
ArchivedChats = 40, ArchivedChats = 40,
#[strum(props(
fallback = "Cannot login as \"%1$s\". Please check if the email address and the password are correct."
))]
CannotLogin = 60,
#[strum(props(fallback = "Location streaming enabled."))] #[strum(props(fallback = "Location streaming enabled."))]
MsgLocationEnabled = 64, MsgLocationEnabled = 64,
@@ -903,11 +898,6 @@ pub(crate) fn sync_msg_body(context: &Context) -> String {
translated(context, StockMessage::SyncMsgBody) translated(context, StockMessage::SyncMsgBody)
} }
/// Stock string: `Cannot login as \"%1$s\". Please check...`.
pub(crate) fn cannot_login(context: &Context, user: &str) -> String {
translated(context, StockMessage::CannotLogin).replace1(user)
}
/// Stock string: `Location streaming enabled.`. /// Stock string: `Location streaming enabled.`.
pub(crate) fn msg_location_enabled(context: &Context) -> String { pub(crate) fn msg_location_enabled(context: &Context) -> String {
translated(context, StockMessage::MsgLocationEnabled) translated(context, StockMessage::MsgLocationEnabled)