diff --git a/deltachat-rpc-client/tests/test_multitransport.py b/deltachat-rpc-client/tests/test_multitransport.py index 055c0b787c..b37c40e415 100644 --- a/deltachat-rpc-client/tests/test_multitransport.py +++ b/deltachat-rpc-client/tests/test_multitransport.py @@ -52,10 +52,9 @@ def test_add_second_address(acf) -> None: def test_change_address(acf) -> None: - """Test Alice configuring a second transport and setting it as a primary one.""" + """Test Alice configuring a second transport and removing the first one.""" alice, bob = acf.get_online_accounts(2) - bob_addr = bob.get_config("configured_addr") bob.create_chat(alice) alice_chat_bob = alice.create_chat(bob) @@ -65,22 +64,14 @@ def test_change_address(acf) -> None: sender_addr1 = msg1.sender.get_snapshot().address alice.stop_io() - old_alice_addr = alice.get_config("configured_addr") + old_alice_addr = alice.list_transports()[0]["addr"] alice_vcard = alice.self_contact.make_vcard() assert old_alice_addr in alice_vcard qr = acf.get_account_qr() alice.add_transport_from_qr(qr) new_alice_addr = alice.list_transports()[1]["addr"] - with pytest.raises(JsonRpcError): - # Cannot use the address that is not - # configured for any transport. - alice.set_config("configured_addr", bob_addr) - # Load old address so it is cached. - assert alice.get_config("configured_addr") == old_alice_addr - alice.set_config("configured_addr", new_alice_addr) - # Make sure that setting `configured_addr` invalidated the cache. - assert alice.get_config("configured_addr") == new_alice_addr + alice.delete_transport(old_alice_addr) alice_vcard = alice.self_contact.make_vcard() assert old_alice_addr not in alice_vcard diff --git a/src/config.rs b/src/config.rs index 12ab9f45ae..9b922ef9ff 100644 --- a/src/config.rs +++ b/src/config.rs @@ -42,10 +42,12 @@ use crate::{constants, stats}; #[strum(serialize_all = "snake_case")] pub enum Config { /// Deprecated(2026-04). - /// Use ConfiguredAddr, [`crate::login_param::EnteredLoginParam`], - /// or add_transport{from_qr}()/list_transports() instead. /// - /// Email address, used in the `From:` field. + /// Email address used by the deprecated configure() procedure. + /// + /// Use add_transport{from_qr}() to configure new transports, + /// Use list_transports() to learn about configured transports, + /// including their addresses. Addr, /// Deprecated(2026-04). @@ -195,9 +197,9 @@ pub enum Config { #[strum(props(default = "0"))] DeleteDeviceAfter, - /// The address of the transport used for sending. + /// Deprecated(2026-09). /// - /// Device-local, other devices choose their own sending transport. + /// Use ConfiguredLoginParam and list_transports() instead. ConfiguredAddr, /// Deprecated(2026-04). @@ -487,11 +489,6 @@ impl Config { | Self::ForceEncryption, ) } - - /// Whether the config option needs an IO scheduler restart to take effect. - pub(crate) fn needs_io_restart(&self) -> bool { - matches!(self, Config::ConfiguredAddr) - } } impl Context { @@ -670,10 +667,6 @@ impl Context { pub async fn set_config(&self, key: Config, value: Option<&str>) -> Result<()> { Self::check_config(key, value)?; - let _pause = match key.needs_io_restart() { - true => self.scheduler.pause(self).await?, - _ => Default::default(), - }; if key == Config::StatsSending { let old_value = self.get_config(key).await?; let old_value = bool_from_config(old_value.as_deref()); diff --git a/src/ephemeral/ephemeral_tests.rs b/src/ephemeral/ephemeral_tests.rs index dae560c562..e095094d15 100644 --- a/src/ephemeral/ephemeral_tests.rs +++ b/src/ephemeral/ephemeral_tests.rs @@ -684,6 +684,10 @@ async fn test_ephemeral_msg_offline() -> Result<()> { check_msg_will_be_deleted(alice, msg.id, &chat, now, now + i64::from(duration) + 1).await?; assert!(alice.sql.exists(stmt, (msg.id,)).await?); + alice + .assert_warn("No SMTP connection candidates provided") + .await; + Ok(()) } diff --git a/src/message/message_tests.rs b/src/message/message_tests.rs index fb81760448..f74d98b588 100644 --- a/src/message/message_tests.rs +++ b/src/message/message_tests.rs @@ -631,6 +631,10 @@ async fn test_delete_msgs_offline() -> Result<()> { delete_msgs(alice, &[msg.id]).await?; assert!(!alice.sql.exists(stmt, (msg.id,)).await?); + alice + .assert_warn("No SMTP connection candidates provided") + .await; + Ok(()) } diff --git a/src/smtp.rs b/src/smtp.rs index 56ca6ca448..d0352f70e6 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -98,19 +98,36 @@ impl Smtp { } self.connectivity.set_connecting(context); - let (_transport_id, lp) = ConfiguredLoginParam::load(context) - .await? - .context("Not configured")?; let proxy_config = ProxyConfig::load(context).await?; - self.connect( - context, - &lp.smtp, - &lp.smtp_password, - &proxy_config, - &lp.addr, - lp.strict_tls(proxy_config.is_some())?, - ) - .await + let transports = ConfiguredLoginParam::load_all(context).await?; + + // Try to connect to the newest transport first. If sending is unreliable, + // user can configure a new transport and it will be the one used. + // Conversely, if user just added a new transport and sending got less reliable, + // user can restore old state by removing the just added transport. + for (transport_id, lp) in transports.into_iter().rev() { + info!(context, "Trying to connect to transport {transport_id}."); + match self + .connect( + context, + &lp.smtp, + &lp.smtp_password, + &proxy_config, + &lp.addr, + lp.strict_tls(proxy_config.is_some())?, + ) + .await + { + Ok(()) => return Ok(()), + Err(err) => { + warn!( + context, + "Failed to connect to SMTP transport {transport_id}: {err:#}." + ); + } + } + } + bail!("Failed to connect to any SMTP server"); } /// Connect using the provided login params.