From ec82188d541e4fb31aa75038cef4b1c532437c13 Mon Sep 17 00:00:00 2001 From: link2xt Date: Mon, 19 Oct 2026 18:39:53 +0000 Subject: [PATCH] refactor!: remove `configured_addr` config configured_addr was already not used by real SMTP code. Rust tests which emulate sending with TestContext.pop_sent_msg() now have a way to set the "sending" address manually in memory. --- benches/decrypting.rs | 6 +- benches/receive_emails.rs | 11 -- deltachat-ffi/deltachat.h | 7 +- deltachat-rpc-client/tests/test_cross_core.py | 29 ++++- .../tests/test_multitransport.py | 29 ----- deltachat-rpc-client/tests/test_securejoin.py | 3 +- deltachat-rpc-client/tests/test_something.py | 5 +- python/src/deltachat/testplugin.py | 7 +- python/tests/test_0_complex_or_slow.py | 1 - python/tests/test_3_offline.py | 12 +-- src/chat.rs | 12 +-- src/config.rs | 67 +----------- src/configure.rs | 16 +-- src/contact.rs | 44 ++------ src/contact/contact_tests.rs | 12 +-- src/imex.rs | 5 - src/key.rs | 10 -- src/keyupdate/keyupdate_tests.rs | 4 - src/mimefactory.rs | 18 ++-- src/mimefactory/mimefactory_tests.rs | 21 ++-- .../shared_secret_decryption_tests.rs | 6 +- src/pgp/pgp_tests.rs | 4 - src/quota.rs | 8 -- src/receive_imf/receive_imf_tests.rs | 11 +- src/securejoin/securejoin_tests.rs | 8 +- src/smtp.rs | 15 +-- src/test_utils.rs | 67 ++++++------ src/tests/aeap.rs | 14 ++- src/transport.rs | 100 +++++++----------- src/transport/transport_tests.rs | 91 ---------------- 30 files changed, 179 insertions(+), 464 deletions(-) diff --git a/benches/decrypting.rs b/benches/decrypting.rs index c823f0fae..122ac9c95 100644 --- a/benches/decrypting.rs +++ b/benches/decrypting.rs @@ -31,7 +31,7 @@ use deltachat::internals_for_benches::create_broadcast_secret; use deltachat::internals_for_benches::save_broadcast_secret; use deltachat::securejoin::get_securejoin_qr; use deltachat::{ - Events, chat::ChatId, config::Config, context::Context, internals_for_benches::key_from_asc, + Events, chat::ChatId, context::Context, internals_for_benches::key_from_asc, internals_for_benches::parse_and_get_text, internals_for_benches::store_self_keypair, stock_str::StockStrings, }; @@ -57,10 +57,6 @@ async fn create_context() -> Context { .await .unwrap(); - context - .set_config(Config::ConfiguredAddr, Some("bob@example.net")) - .await - .unwrap(); let secret = key_from_asc(include_str!("../test-data/key/bob-secret.asc")).unwrap(); store_self_keypair(&context, &secret) .await diff --git a/benches/receive_emails.rs b/benches/receive_emails.rs index 89a3cffee..bb63384b1 100644 --- a/benches/receive_emails.rs +++ b/benches/receive_emails.rs @@ -5,7 +5,6 @@ use std::path::PathBuf; use criterion::{Criterion, criterion_group, criterion_main}; use deltachat::{ Events, - config::Config, context::Context, imex::{ImexMode, imex}, receive_imf::receive_imf, @@ -109,16 +108,6 @@ async fn create_context() -> Context { .unwrap(); } - let addr = "alice@example.com"; - context.set_config(Config::Addr, Some(addr)).await.unwrap(); - context - .set_config(Config::ConfiguredAddr, Some(addr)) - .await - .unwrap(); - context - .set_config(Config::Configured, Some("1")) - .await - .unwrap(); context } diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index f75020116..d7bc3ffc2 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -389,9 +389,6 @@ char* dc_get_blobdir (const dc_context_t* context); /** * Configure the context. The configuration is handled by key=value pairs as: * - * - `configured_addr` = Email address in use. - * Unless for testing, do not set this value using dc_set_config(). - * Instead, set `addr` and call dc_configure(). * - `displayname` = Own name to use when sending messages. MUAs are allowed to spread this way e.g. using CC, defaults to empty * - `selfstatus` = Own status to display, e.g. in e-mail footers, defaults to empty * - `selfavatar` = File containing avatar. Will immediately be copied to the @@ -481,8 +478,6 @@ char* dc_get_blobdir (const dc_context_t* context); * if you want to use the deprecated dc_configure() API, such as: * * - `addr` = Email address to use for configuration. - * If dc_configure() fails this is not the email address actually in use. - * Use `configured_addr` to find out the email address actually in use. * - `mail_server` = IMAP-server, guessed if left out * - `mail_user` = IMAP-username, guessed if left out * - `mail_pw` = IMAP-password (always needed) @@ -6474,7 +6469,7 @@ void dc_event_unref(dc_event_t* event); * The event is emitted on the device modifying the transports * as well as on other devices applying the synced change, * for the JSON-RPC calls `add_or_update_transport`, `add_transport_from_qr`, - * `delete_transport` or `set_config(configured_addr)`. + * `delete_transport`. */ #define DC_EVENT_TRANSPORTS_MODIFIED 2600 diff --git a/deltachat-rpc-client/tests/test_cross_core.py b/deltachat-rpc-client/tests/test_cross_core.py index 1c2e1c618..3baff4b7c 100644 --- a/deltachat-rpc-client/tests/test_cross_core.py +++ b/deltachat-rpc-client/tests/test_cross_core.py @@ -87,6 +87,31 @@ def test_second_device(acf, alice_and_remote_bob) -> None: assert new_account.get_config("addr") == remote_eval("bob.get_config('addr')") +def test_old_device_delete_transport(acf, alice_and_remote_bob) -> None: + """Test setting up old core as the second device for the new core and deleting a transport. + + Old core unfortunately expects that deprecated configured_addr config value is set + and fails to delete a transport otherwise. The test makes sure new core sets some configured_addr + for backwards compatibility.""" + alice, _alice_contact_bob, remote_eval = alice_and_remote_bob("2.62.0") + + alice.set_config("displayname", "Alice123") + qr = acf.get_account_qr() + alice.add_transport_from_qr(qr) + + alice_provide_backup_future = alice._rpc.provide_backup.future(alice.id) + qr = alice._rpc.get_backup_qr(alice.id) + + remote_eval("locals().setdefault('new_account', bob.manager.add_account())") + remote_eval(f"new_account._rpc.get_backup(new_account.id, {qr!r})") + alice_provide_backup_future() + + assert alice.get_config("displayname") == remote_eval("new_account.get_config('displayname')") + + first_transport_addr = alice.list_transports()[0]["addr"] + remote_eval(f"new_account.delete_transport({first_transport_addr!r})") + + @pytest.mark.parametrize("replace_relay", [False, True], ids=["add", "replace"]) def test_keyupdate_against_core_2_48_march_2026(acf, alice_and_remote_bob, replace_relay): """Test 2.48 Bob learns a relay change of Alice from a keyupdate, and is shown nothing.""" @@ -104,7 +129,6 @@ def test_keyupdate_against_core_2_48_march_2026(acf, alice_and_remote_bob, repla # an accepted chat alone is not enough, a message must have flowed. alice_chat = alice_contact_bob.create_chat() alice.set_config("keyupdate_debounce", "1") - old_addr = alice.get_config("configured_addr") alice_chat.send_text("hi") assert remote_eval("bob.wait_for_incoming_msg().get_snapshot().text") == "hi" before = bob_sees() @@ -113,8 +137,9 @@ def test_keyupdate_against_core_2_48_march_2026(acf, alice_and_remote_bob, repla # and signature timestamps have one-second resolution: # without waiting, the re-signed key can tie with the copy Bob holds, keeping his. time.sleep(2) + old_addr = alice.list_transports()[0]["addr"] alice.add_transport_from_qr(acf.get_account_qr()) - (new_addr,) = [t["addr"] for t in alice.list_transports() if t["addr"] != old_addr] + new_addr = alice.list_transports()[1]["addr"] if replace_relay: alice.delete_transport(old_addr) alice.bring_online() diff --git a/deltachat-rpc-client/tests/test_multitransport.py b/deltachat-rpc-client/tests/test_multitransport.py index 4a804cfb0..f62df29f0 100644 --- a/deltachat-rpc-client/tests/test_multitransport.py +++ b/deltachat-rpc-client/tests/test_multitransport.py @@ -45,10 +45,8 @@ def test_add_second_address(acf) -> None: second_addr = account.list_transports()[1]["addr"] third_addr = account.list_transports()[2]["addr"] - assert account.get_config("configured_addr") == first_addr account.delete_transport(first_addr) assert len(account.list_transports()) == 2 - assert account.get_config("configured_addr") != first_addr account.delete_transport(second_addr) assert len(account.list_transports()) == 1 @@ -187,9 +185,6 @@ def test_transport_synchronization(acf, log) -> None: wait_for_io_started(ac1) [transport1, transport3] = ac1.list_transports() - log.section("ac1 changes the sending transport") - ac1.set_config("configured_addr", transport3["addr"]) - log.section("ac1 removes the first transport") ac1.delete_transport(transport1["addr"]) @@ -197,7 +192,6 @@ def test_transport_synchronization(acf, log) -> None: wait_for_io_started(ac1_clone) [transport3] = ac1_clone.list_transports() assert transport3["addr"] == addr3 - assert ac1_clone.get_config("configured_addr") == addr3 ac2_chat = ac2.create_chat(ac1) ac2_chat.send_text("Hello!") @@ -221,10 +215,6 @@ def test_transport_sync_new_as_primary(acf, log) -> None: [transport1, transport2] = ac1_transports ac1_clone.wait_for_event(EventType.TRANSPORTS_MODIFIED) assert len(ac1_clone.list_transports()) == 2 - assert ac1_clone.get_config("configured_addr") == transport1["addr"] - - log.section("ac1 changes the primary transport") - ac1.set_config("configured_addr", transport2["addr"]) log.section("ac1_clone receives a message via the new transport") ac1_chat = ac1.create_chat(bob) @@ -236,22 +226,6 @@ def test_transport_sync_new_as_primary(acf, log) -> None: assert ac1_clone.wait_for_incoming_msg().get_snapshot().text == "hello back" -def test_recognize_self_address(acf) -> None: - alice, bob = acf.get_online_accounts(2) - - bob_chat = bob.create_chat(alice) - - qr = acf.get_account_qr() - alice.add_transport_from_qr(qr) - - new_alice_addr = alice.list_transports()[1]["addr"] - alice.set_config("configured_addr", new_alice_addr) - - bob_chat.send_text("Hello!") - msg = alice.wait_for_incoming_msg().get_snapshot() - assert msg.chat == alice.create_chat(bob) - - def test_transport_limit(acf) -> None: """Test transports limit.""" account = acf.get_online_account() @@ -294,7 +268,6 @@ def test_message_info_imap_urls(acf) -> None: # Alice switches to another transport and removes the rest of the transports. new_alice_addr = alice.list_transports()[1]["addr"] - alice.set_config("configured_addr", new_alice_addr) removed_addrs = [] for transport in alice.list_transports(): if transport["addr"] != new_alice_addr: @@ -326,7 +299,6 @@ def test_remove_primary_transport(acf, log) -> None: log.section("Alice removes the primary relay") alice.delete_transport(transport1["addr"]) - assert alice.get_config("configured_addr") == transport2["addr"] alice.stop_io() alice.start_io() @@ -369,7 +341,6 @@ def test_qr_works_after_removing_primary_transport(acf, log) -> None: def test_background_fetch_from_second_transport(acf, direct_imap, dc): alice, alice_chat, bob_chat = alice_with_two_transports_and_bob(acf) [transport1, transport2] = alice.list_transports() - assert alice.get_config("configured_addr") == transport1["addr"] alice.stop_io() bob_chat.send_text("hello") diff --git a/deltachat-rpc-client/tests/test_securejoin.py b/deltachat-rpc-client/tests/test_securejoin.py index ed5dc8af9..c0a402ac4 100644 --- a/deltachat-rpc-client/tests/test_securejoin.py +++ b/deltachat-rpc-client/tests/test_securejoin.py @@ -647,9 +647,8 @@ def test_qr_scan_updates_new_relay_address(acf): bob.wait_for_securejoin_joiner_success() for ac in [alice, bob]: - old_addr = ac.get_config("configured_addr") + old_addr = ac.list_transports()[0]["addr"] ac.add_transport_from_qr(acf.get_account_qr()) - ac.set_config("configured_addr", ac.list_transports()[1]["addr"]) ac.delete_transport(old_addr) bob.secure_join(alice.get_qr_code()) diff --git a/deltachat-rpc-client/tests/test_something.py b/deltachat-rpc-client/tests/test_something.py index d39e5dbca..1457ce207 100644 --- a/deltachat-rpc-client/tests/test_something.py +++ b/deltachat-rpc-client/tests/test_something.py @@ -100,7 +100,6 @@ def test_lowercase_address(acf) -> None: ) assert account.is_configured() assert addr_upper != addr - assert account.get_config("configured_addr") == addr assert account.list_transports()[0]["addr"] == addr param = account.get_info()["used_transport_settings"] @@ -168,7 +167,7 @@ def test_list_transports(acf) -> None: def test_account(acf) -> None: alice, bob = acf.get_online_accounts(2) - bob_addr = bob.get_config("configured_addr") + bob_addr = bob.list_transports()[0]["addr"] alice_contact_bob = alice.create_contact(bob, "Bob") alice_chat_bob = alice_contact_bob.create_chat() alice_chat_bob.send_text("Hello!") @@ -331,7 +330,7 @@ def test_chat(acf) -> None: def test_contact(acf) -> None: alice, bob = acf.get_online_accounts(2) - bob_addr = bob.get_config("configured_addr") + bob_addr = bob.list_transports()[0]["addr"] alice_contact_bob = alice.create_contact(bob, "Bob") assert alice_contact_bob == alice.get_contact_by_id(alice_contact_bob.id) diff --git a/python/src/deltachat/testplugin.py b/python/src/deltachat/testplugin.py index 7dab54298..c74e1f9b4 100644 --- a/python/src/deltachat/testplugin.py +++ b/python/src/deltachat/testplugin.py @@ -370,12 +370,7 @@ class ACFactory: acname = ac._logid addr = f"{acname}@offline.org" lib.dc_add_pseudo_transport(ac._dc_context, as_dc_charpointer(addr)) - ac.update_config( - { - "configured_addr": addr, - "displayname": acname, - }, - ) + ac.set_config("displayname", acname) self._preconfigure_key(ac) self._acsetup.init_logging(ac) assert ac.is_configured(), "Pseudo configured account should look like if it is configured" diff --git a/python/tests/test_0_complex_or_slow.py b/python/tests/test_0_complex_or_slow.py index 3873ae33a..ee0c20c57 100644 --- a/python/tests/test_0_complex_or_slow.py +++ b/python/tests/test_0_complex_or_slow.py @@ -51,7 +51,6 @@ class TestGroupStressTests: lp.sec("ac2: receiving system message about contact addition") sysmsg = ac2._evtracker.wait_next_incoming_message() - assert ac5.get_config("configured_addr") in sysmsg.text assert sysmsg.chat.num_contacts() == 4 lp.sec("ac5: waiting for message about addition to the chat") diff --git a/python/tests/test_3_offline.py b/python/tests/test_3_offline.py index 71946db21..47959dcbb 100644 --- a/python/tests/test_3_offline.py +++ b/python/tests/test_3_offline.py @@ -132,7 +132,7 @@ class TestOfflineContact: def test_create_self_contact(self, acfactory): ac1 = acfactory.get_pseudo_configured_account() - contact1 = ac1.create_contact(ac1.get_config("configured_addr")) + contact1 = ac1.create_contact(ac1.get_self_contact().addr) assert contact1.id == 1 def test_get_contacts_and_delete(self, acfactory): @@ -223,7 +223,7 @@ class TestOfflineChat: ac2 = acfactory.get_pseudo_configured_account() chat = ac1.create_group_chat(name="title1") contact = chat.add_contact(ac2) - assert contact.addr == ac2.get_config("configured_addr") + assert contact.addr == ac2.get_self_contact().addr assert contact.name == ac2.get_config("displayname") assert contact.account == ac1 chat.remove_contact(ac2) @@ -456,7 +456,7 @@ class TestOfflineChat: contacts = ac2.get_contacts() assert len(contacts) == 1 contact2 = contacts[0] - assert contact2.addr == ac_contact.get_config("configured_addr") + assert contact2.addr == ac_contact.get_self_contact().addr chat2 = contact2.create_chat() messages = chat2.get_messages() assert len(messages) == 2 + E2EE_INFO_MSGS @@ -552,7 +552,7 @@ class TestOfflineChat: contacts = ac2.get_contacts() assert len(contacts) == 1 contact2 = contacts[0] - assert contact2.addr == ac_contact.get_config("configured_addr") + assert contact2.addr == ac_contact.get_self_contact().addr chat2 = contact2.create_chat() messages = chat2.get_messages() assert len(messages) == 2 + E2EE_INFO_MSGS @@ -604,7 +604,7 @@ class TestOfflineChat: contacts = ac2.get_contacts() assert len(contacts) == 1 contact2 = contacts[0] - assert contact2.addr == ac_contact.get_config("configured_addr") + assert contact2.addr == ac_contact.get_self_contact().addr chat2 = contact2.create_chat() messages = chat2.get_messages() assert len(messages) == 2 + E2EE_INFO_MSGS @@ -621,7 +621,7 @@ class TestOfflineChat: contacts = ac2.get_contacts() assert len(contacts) == 1 contact2 = contacts[0] - assert contact2.addr == ac_contact.get_config("configured_addr") + assert contact2.addr == ac_contact.get_self_contact().addr chat2 = contact2.create_chat() messages = chat2.get_messages() assert len(messages) == 2 + E2EE_INFO_MSGS diff --git a/src/chat.rs b/src/chat.rs index eb8526a53..f786b1ce5 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2893,15 +2893,9 @@ async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -> Result { - let Some(addr) = value else { - bail!("Cannot unset configured_addr"); - }; - - self.sql - .transaction(|transaction| { - if transaction.query_row( - "SELECT COUNT(*) FROM transports WHERE addr=?", - (addr,), - |row| { - let res: i64 = row.get(0)?; - Ok(res) - }, - )? == 0 - { - bail!("Address does not belong to any transport."); - } - transaction.execute( - "INSERT OR REPLACE INTO config (keyname, value) VALUES ('configured_addr', ?)", - (addr,), - )?; - - Ok(()) - }) - .await?; - self.sql.uncache_raw_config("configured_addr").await; - } _ => { self.sql.set_raw_config(key.as_ref(), value).await?; } @@ -854,18 +821,6 @@ impl Context { /// Determine whether the specified addr maps to the/a self addr. /// Returns `false` if no addresses are configured. pub(crate) async fn is_self_addr(&self, addr: &str) -> Result { - // Employ the config cache to optimize for `ConfiguredAddr` passed. - if !addr.is_empty() - && addr_cmp( - addr, - &self - .get_config(Config::ConfiguredAddr) - .await? - .unwrap_or_default(), - ) - { - return Ok(true); - } Ok(self .get_self_addrs() .await? @@ -873,20 +828,6 @@ impl Context { .any(|a| addr_cmp(addr, a))) } - /// Sets `primary_new` as the address used for sending. - /// - /// This should only be used by test code and during configure. - #[cfg(test)] // AEAP is disabled, but there are still tests for it - pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> { - self.quota.write().await.clear(); - - self.sql - .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(primary_new)) - .await?; - self.emit_event(EventType::ConnectivityChanged); - Ok(()) - } - /// Returns all self addresses, newest first. pub(crate) async fn get_self_addrs(&self) -> Result> { let query_only = true; @@ -895,10 +836,10 @@ impl Context { .await } - /// Returns the address of the transport used for sending. - /// Returns an error if no self addr is configured. + /// Returns address of the first transport. pub async fn get_primary_self_addr(&self) -> Result { - self.get_config(Config::ConfiguredAddr) + self.sql + .query_get_value("SELECT addr FROM transports ORDER BY id LIMIT 1", ()) .await? .context("No self addr configured") } diff --git a/src/configure.rs b/src/configure.rs index 98309e6b2..b9fb6e362 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -37,8 +37,8 @@ use crate::sync::Sync::Nosync; use crate::tools::time; use crate::transport::{ ConfiguredCertificateChecks, ConfiguredLoginParam, ConfiguredServerLoginParam, - ConnectionCandidate, delete_transport_row, maybe_update_sending_transport, - purge_transport_caches, send_sync_transports, transport_addrs, + ConnectionCandidate, delete_transport_row, purge_transport_caches, send_sync_transports, + transport_addrs, update_configured_addr, }; use crate::{EventType, autorelay, stock_str}; @@ -275,7 +275,7 @@ impl Context { /// another one is chosen automatically. pub async fn delete_transport(&self, addr: &str) -> Result<()> { let now = time(); - let (removed_transport_id, reelected) = self + let removed_transport_id = self .sql .transaction(|transaction| { if transport_addrs(transaction)?.len() <= 1 { @@ -294,14 +294,10 @@ impl Context { let remove_timestamp = std::cmp::max(now, add_timestamp); let transport_id = delete_transport_row(transaction, addr, remove_timestamp)? .context("Transport disappeared")?; - let reelected = maybe_update_sending_transport(transaction)?; - Ok((transport_id, reelected)) + update_configured_addr(transaction)?; + Ok(transport_id) }) .await?; - if let Some(new_addr) = reelected { - info!(self, "Using transport {new_addr:?} for sending now."); - self.sql.uncache_raw_config("configured_addr").await; - } send_sync_transports(self).await?; purge_transport_caches(self, removed_transport_id).await; // Restarting all IO also stops the removed transport's IMAP loop. @@ -801,8 +797,6 @@ mod tests { let t = &tcm.unconfigured().await; add_pseudo_transport(t, "primary@example.org").await?; - t.set_config(Config::ConfiguredAddr, Some("primary@example.org")) - .await?; assert_eq!(t.count_transports().await?, 1); t.check_relay_limit().await?; diff --git a/src/contact.rs b/src/contact.rs index fb6716baa..86eb1365e 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -718,9 +718,9 @@ impl Contact { .await? .unwrap_or_default(); contact.addr = context - .get_config(Config::ConfiguredAddr) - .await? - .unwrap_or_default(); + .get_primary_self_addr() + .await + .context("Cannot get address for self-contact")?; if let Some(self_fp) = self_fingerprint_opt(context).await? { contact.fingerprint = Some(self_fp.to_string()); } @@ -1421,10 +1421,6 @@ WHERE addr=? ); let contact = Contact::get_by_id(context, contact_id).await?; - let addr = context - .get_config(Config::ConfiguredAddr) - .await? - .unwrap_or_default(); let Some(fingerprint_other) = contact.fingerprint() else { return Ok(stock_str::encr_none(context)); @@ -1444,32 +1440,12 @@ WHERE addr=? .await? .dc_fingerprint() .human_readable(); - if addr < contact.addr { - cat_fingerprint( - &mut ret, - &stock_str::self_msg(context), - &addr, - &fingerprint_self, - ); - cat_fingerprint( - &mut ret, - contact.get_display_name(), - &contact.addr, - &fingerprint_other, - ); + if fingerprint_self < fingerprint_other { + cat_fingerprint(&mut ret, &stock_str::self_msg(context), &fingerprint_self); + cat_fingerprint(&mut ret, contact.get_display_name(), &fingerprint_other); } else { - cat_fingerprint( - &mut ret, - contact.get_display_name(), - &contact.addr, - &fingerprint_other, - ); - cat_fingerprint( - &mut ret, - &stock_str::self_msg(context), - &addr, - &fingerprint_self, - ); + cat_fingerprint(&mut ret, contact.get_display_name(), &fingerprint_other); + cat_fingerprint(&mut ret, &stock_str::self_msg(context), &fingerprint_self); } if let Some(public_key) = contact.public_key(context).await? @@ -1968,8 +1944,8 @@ pub(crate) async fn update_last_seen( Ok(()) } -fn cat_fingerprint(ret: &mut String, name: &str, addr: &str, fingerprint: &str) { - *ret += &format!("\n\n{name} ({addr}):\n{fingerprint}"); +fn cat_fingerprint(ret: &mut String, name: &str, fingerprint: &str) { + *ret += &format!("\n\n{name}:\n{fingerprint}"); } fn split_address_book(book: &str) -> Vec<(&str, &str)> { diff --git a/src/contact/contact_tests.rs b/src/contact/contact_tests.rs index 5a7e748d4..ca2d19000 100644 --- a/src/contact/contact_tests.rs +++ b/src/contact/contact_tests.rs @@ -887,11 +887,11 @@ async fn test_contact_get_encrinfo() -> Result<()> { "Messages are end-to-end encrypted. Fingerprints: -Me (alice@example.org): +Me: 2E6F A2CB 23B5 32D7 2863 4B58 64B0 8F61 A9ED 9443 -bob@example.net (bob@example.net): +bob@example.net: CCCB 5AA9 F6E1 141C 9431 65F1 DB18 B18C BCF7 0487 @@ -908,11 +908,11 @@ bob@example.net" "No encryption. Fingerprints: -Me (alice@example.org): +Me: 2E6F A2CB 23B5 32D7 2863 4B58 64B0 8F61 A9ED 9443 -bob@example.net (bob@example.net): +bob@example.net: CCCB 5AA9 F6E1 141C 9431 65F1 DB18 B18C BCF7 0487" ); @@ -1252,7 +1252,7 @@ async fn test_make_n_import_vcard() -> Result<()> { tokio::fs::write(&avatar_path, avatar_bytes).await?; bob.set_config(Config::Selfavatar, Some(avatar_path.to_str().unwrap())) .await?; - let bob_addr = bob.get_config(Config::ConfiguredAddr).await?.unwrap(); + let bob_addr = bob.get_primary_self_addr().await?; let bob_biography = bob.get_config(Config::Selfstatus).await?.unwrap(); let chat = bob.create_chat(alice).await; let sent_msg = bob.send_text(chat.id, "moin").await; @@ -1348,7 +1348,7 @@ async fn test_make_n_import_vcard() -> Result<()> { async fn test_import_vcard_key_change() -> Result<()> { let alice = &TestContext::new_alice().await; let bob = &TestContext::new_bob().await; - let bob_addr = &bob.get_config(Config::ConfiguredAddr).await?.unwrap(); + let bob_addr = &bob.get_primary_self_addr().await?; bob.set_config(Config::Displayname, Some("Bob")).await?; let vcard = make_vcard(bob, &[ContactId::SELF]).await?; alice.evtracker.clear_events(); diff --git a/src/imex.rs b/src/imex.rs index 00afc432c..c94726aef 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -961,10 +961,6 @@ mod tests { .await; assert!(context2.is_configured().await?); - assert_eq!( - context2.get_config(Config::ConfiguredAddr).await?, - Some("alice@example.org".to_string()) - ); Ok(()) } @@ -1074,7 +1070,6 @@ mod tests { .await; assert!(!context2.is_configured().await?); - assert_eq!(context2.get_config(Config::ConfiguredAddr).await?, None); Ok(()) } diff --git a/src/key.rs b/src/key.rs index 0c7d23b50..bca1341fe 100644 --- a/src/key.rs +++ b/src/key.rs @@ -657,7 +657,6 @@ mod tests { use std::time::Duration; use super::*; - use crate::config::Config; use crate::test_utils::{TestContext, TestContextManager, alice_keypair}; use crate::tools::SystemTime; use crate::transport::add_pseudo_transport; @@ -811,9 +810,6 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD async fn test_load_self_generate_public() { let t = TestContext::new().await; add_pseudo_transport(&t, "alice@example.org").await.unwrap(); - t.set_config(Config::ConfiguredAddr, Some("alice@example.org")) - .await - .unwrap(); let key = load_self_public_key(&t).await; assert!(key.is_ok()); } @@ -822,9 +818,6 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD async fn test_load_self_generate_secret() { let t = TestContext::new().await; add_pseudo_transport(&t, "alice@example.org").await.unwrap(); - t.set_config(Config::ConfiguredAddr, Some("alice@example.org")) - .await - .unwrap(); let key = load_self_secret_key(&t).await; assert!(key.is_ok()); } @@ -835,9 +828,6 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD let t = TestContext::new().await; add_pseudo_transport(&t, "alice@example.org").await.unwrap(); - t.set_config(Config::ConfiguredAddr, Some("alice@example.org")) - .await - .unwrap(); let thr0 = { let ctx = t.clone(); thread::spawn(move || { diff --git a/src/keyupdate/keyupdate_tests.rs b/src/keyupdate/keyupdate_tests.rs index b08cbd29d..4567ee74b 100644 --- a/src/keyupdate/keyupdate_tests.rs +++ b/src/keyupdate/keyupdate_tests.rs @@ -260,10 +260,6 @@ async fn test_keyupdate_on_replacement() -> Result<()> { alice.add_transport("alice@relay.example.net").await; SystemTime::shift(Duration::from_secs(2)); alice.delete_transport("alice@example.org").await?; - assert_eq!( - alice.get_config(Config::ConfiguredAddr).await?.as_deref(), - Some("alice@relay.example.net") - ); maybe_send_keyupdate_message(alice).await?; let keyupdate = alice.pop_sent_msg().await; diff --git a/src/mimefactory.rs b/src/mimefactory.rs index b23be697d..16fc60c4f 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -432,9 +432,9 @@ pub(crate) fn render_queued_mail( #[cfg(test)] pub(crate) async fn render_queued_mail_with_context( queued_mail: QueuedMail, - context: &Context, + context: &crate::test_utils::TestContext, ) -> Result { - let from_addr = context.get_primary_self_addr().await?; + let from_addr = context.sending_addr().await; let public_key = crate::key::load_self_public_key(context).await?; let secret_key = crate::key::load_self_secret_key(context).await?; @@ -998,14 +998,10 @@ impl MimeFactory { true => context.get_config(Config::Displayname).await?, false => None, }; - let self_name = &match self_name { - Some(name) => name, - None => context - .get_config(Config::ConfiguredAddr) - .await? - .unwrap_or_default(), - }; - Ok(stock_str::subject_for_new_contact(context, self_name)) + Ok(stock_str::subject_for_new_contact( + context, + self_name.as_deref().unwrap_or("..."), + )) } pub fn recipients(&self) -> Vec { @@ -1227,7 +1223,7 @@ impl MimeFactory { /// /// Used only for tests. #[cfg(test)] - pub async fn render(self, context: &Context) -> Result { + pub async fn render(self, context: &crate::test_utils::TestContext) -> Result { // Does not matter, we are not going to return the QueuedMail. let bcc_self = false; let (queued_mail, _side_effects) = diff --git a/src/mimefactory/mimefactory_tests.rs b/src/mimefactory/mimefactory_tests.rs index ea348b29e..50c60503b 100644 --- a/src/mimefactory/mimefactory_tests.rs +++ b/src/mimefactory/mimefactory_tests.rs @@ -185,7 +185,7 @@ async fn test_subject_outgoing() { let t = TestContext::new_alice().await; t.allow_unencrypted().await.unwrap(); - assert_eq!(first_subject_str(t).await, "Message from alice@example.org"); + assert_eq!(first_subject_str(t).await, "Message from ..."); let t = TestContext::new_alice().await; t.allow_unencrypted().await.unwrap(); @@ -731,9 +731,9 @@ async fn test_remove_member_bcc() -> Result<()> { let charlie = &tcm.charlie().await; alice.allow_unencrypted().await?; - let alice_addr = alice.get_config(Config::ConfiguredAddr).await?.unwrap(); - let bob_addr = bob.get_config(Config::ConfiguredAddr).await?.unwrap(); - let charlie_addr = charlie.get_config(Config::ConfiguredAddr).await?.unwrap(); + let alice_addr = alice.get_primary_self_addr().await?; + let bob_addr = bob.get_primary_self_addr().await?; + let charlie_addr = charlie.get_primary_self_addr().await?; let bob_id = alice.add_or_lookup_address_contact_id(bob).await; let charlie_id = alice.add_or_lookup_address_contact_id(charlie).await; @@ -896,7 +896,7 @@ async fn test_new_member_is_first_recipient() -> Result<()> { assert!( sent_msg .recipients - .starts_with(&charlie.get_config(Config::ConfiguredAddr).await?.unwrap()) + .starts_with(&charlie.get_primary_self_addr().await?) ); remove_contact_from_chat(alice, group, bob_id).await?; @@ -904,11 +904,8 @@ async fn test_new_member_is_first_recipient() -> Result<()> { SystemTime::shift(Duration::from_secs(60)); add_contact_to_chat(alice, group, bob_id).await?; let sent_msg = alice.pop_sent_msg().await; - assert!( - sent_msg - .recipients - .starts_with(&bob.get_config(Config::ConfiguredAddr).await?.unwrap()) - ); + let bob_addr = bob.get_primary_self_addr().await?; + assert!(sent_msg.recipients.starts_with(&bob_addr)); Ok(()) } @@ -1115,7 +1112,7 @@ Autocrypt: addr=alice@example.org; prefer-encrypt=mutual; Content-Type: text/plain; charset="utf-8" Date: DATE To: -Subject: Message from alice@example.org +Subject: Message from ... References: Chat-Version: 1.0 Content-Transfer-Encoding: 7bit @@ -1167,7 +1164,7 @@ Content-Type: multipart/mixed; boundary="BOUNDARY" Date: DATE To: -Subject: Message from alice@example.org +Subject: Message from ... References: Chat-Version: 1.0 diff --git a/src/mimeparser/shared_secret_decryption_tests.rs b/src/mimeparser/shared_secret_decryption_tests.rs index 68db48b16..4f5546e00 100644 --- a/src/mimeparser/shared_secret_decryption_tests.rs +++ b/src/mimeparser/shared_secret_decryption_tests.rs @@ -131,7 +131,7 @@ async fn test_broadcast_security_attacker_signature() -> Result<()> { let secret = load_broadcast_secret(alice, alice_chat_id).await?.unwrap(); - let charlie_addr = charlie.get_config(Config::ConfiguredAddr).await?.unwrap(); + let charlie_addr = charlie.get_primary_self_addr().await?; test_shared_secret_decryption_ext( bob, @@ -183,7 +183,7 @@ async fn test_broadcast_security_happy_path() -> Result<()> { let secret = load_broadcast_secret(alice, alice_chat_id).await?.unwrap(); - let alice_addr = alice.get_config(Config::ConfiguredAddr).await?.unwrap(); + let alice_addr = alice.get_primary_self_addr().await?; test_shared_secret_decryption_ext(bob, &alice_addr, &secret, Some(alice), None).await } @@ -202,7 +202,7 @@ async fn test_qr_code_security() -> Result<()> { // Start a securejoin process, but don't finish it: join_securejoin(bob, &qr).await?; - let charlie_addr = charlie.get_config(Config::ConfiguredAddr).await?.unwrap(); + let charlie_addr = charlie.get_primary_self_addr().await?; let alice_fp = self_fingerprint(alice).await?; let secret_for_encryption = format!("securejoin/{alice_fp}/{authcode}"); diff --git a/src/pgp/pgp_tests.rs b/src/pgp/pgp_tests.rs index 65d85ab22..e4047c3e2 100644 --- a/src/pgp/pgp_tests.rs +++ b/src/pgp/pgp_tests.rs @@ -4,7 +4,6 @@ use tokio::sync::OnceCell; use super::*; use crate::transport::add_pseudo_transport; use crate::{ - config::Config, decrypt, key::{load_self_public_key, self_fingerprint, store_self_keypair}, mimefactory::{part_to_bytes, wrap_encrypted_part}, @@ -24,9 +23,6 @@ async fn decrypt_bytes( add_pseudo_transport(t, "alice@example.org") .await .expect("Failed to add pseudo transport"); - t.set_config(Config::ConfiguredAddr, Some("alice@example.org")) - .await - .expect("Failed to configure address"); for secret in auth_tokens_for_decryption { token::save(t, token::Namespace::Auth, None, secret, 0).await?; diff --git a/src/quota.rs b/src/quota.rs index 64fb97110..069b62930 100644 --- a/src/quota.rs +++ b/src/quota.rs @@ -156,14 +156,6 @@ mod tests { }; assert!(!t.quota_needs_update(0, TIMEOUT).await); - t.evtracker.clear_events(); - t.set_primary_self_addr("new@addr").await?; - assert!(t.quota.read().await.is_empty()); - t.evtracker - .get_matching(|evt| matches!(evt, EventType::ConnectivityChanged)) - .await; - assert!(t.quota_needs_update(0, TIMEOUT).await); - Ok(()) } } diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index 2206b74ab..f247ec9ac 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -3853,7 +3853,7 @@ async fn test_unsigned_chat_group_hdr() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; let bob = &tcm.bob().await; - let bob_addr = bob.get_config(Config::ConfiguredAddr).await?.unwrap(); + let bob_addr = bob.get_primary_self_addr().await?; let bob_id = alice.add_or_lookup_contact_id(bob).await; let alice_chat_id = create_group(alice, "foos").await?; add_contact_to_chat(alice, alice_chat_id, bob_id).await?; @@ -4541,7 +4541,7 @@ async fn test_outgoing_msg_forgery() -> Result<()> { let mut tcm = TestContextManager::new(); let export_dir = tempfile::tempdir().unwrap(); let alice = &tcm.alice().await; - let alice_addr = &alice.get_config(Config::ConfiguredAddr).await?.unwrap(); + let alice_addr = &alice.get_primary_self_addr().await?; imex(alice, ImexMode::ExportSelfKeys, export_dir.path(), None).await?; // We need Bob only to encrypt the forged message to Alice's key, actually Bob doesn't // participate in the scenario. @@ -5638,7 +5638,7 @@ async fn test_bcc_not_a_group() -> Result<()> { async fn test_lookup_key_contact_by_address_self() -> Result<()> { let mut tcm = TestContextManager::new(); let t = &tcm.alice().await; - let addr = &t.get_config(Config::ConfiguredAddr).await?.unwrap(); + let addr = &t.get_primary_self_addr().await?; assert_eq!( lookup_key_contact_by_address(t, addr, None).await?, Some(ContactId::SELF) @@ -5690,10 +5690,7 @@ async fn test_outgoing_determined_by_signature() -> Result<()> { let alice_dev2 = &tcm.unconfigured().await; alice_dev2.configure_addr(different_from).await; key::store_self_keypair(alice_dev2, &alice_keypair()).await?; - assert_ne!( - alice.get_config(Config::ConfiguredAddr).await?.unwrap(), - different_from - ); + assert_ne!(alice.get_primary_self_addr().await?, different_from); // Send message from alice_dev2 and check alice sees it as outgoing let chat_id = alice_dev2.create_chat_id(bob).await; diff --git a/src/securejoin/securejoin_tests.rs b/src/securejoin/securejoin_tests.rs index d3ec129f6..56be83ad7 100644 --- a/src/securejoin/securejoin_tests.rs +++ b/src/securejoin/securejoin_tests.rs @@ -50,11 +50,7 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon let mut tcm = TestContextManager::new(); let alice = tcm.alice().await; - let alice_addr = &alice - .get_config(Config::ConfiguredAddr) - .await - .unwrap() - .unwrap(); + let alice_addr = alice.get_primary_self_addr().await.unwrap(); if case == SetupContactCase::AliceHasName { alice .set_config(Config::Displayname, Some("Alice")) @@ -118,7 +114,7 @@ async fn test_setup_contact_ext(case: SetupContactCase) -> (TestContext, TestCon let contact_alice_id = bob.add_or_lookup_contact_no_key(&alice).await.id; let sent = bob.pop_sent_msg().await; assert!(!sent.payload.contains("Bob Examplenet")); - assert_eq!(sent.recipient(), EmailAddress::new(alice_addr).unwrap()); + assert_eq!(sent.recipients, *alice_addr); let msg = alice.parse_msg(&sent).await; assert!(msg.signature.is_none()); assert_eq!( diff --git a/src/smtp.rs b/src/smtp.rs index 4171bada8..d0d22e283 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -13,7 +13,6 @@ use rusqlite::OptionalExtension as _; use tokio::task; use crate::chat::{ChatId, add_info_msg_with_cmd}; -use crate::config::Config; use crate::contact::{Contact, ContactId}; use crate::context::Context; use crate::events::EventType; @@ -555,7 +554,7 @@ pub(crate) async fn send_msg_to_smtp(context: &Context, smtp: &mut Smtp, rowid: .max(1); let rendered_mail = - mimefactory::render_queued_mail(queued_mail, &public_key, &secret_key, from_addr)?; + mimefactory::render_queued_mail(queued_mail, &public_key, &secret_key, from_addr.clone())?; let body = rendered_mail.message; info!( @@ -626,15 +625,9 @@ pub(crate) async fn send_msg_to_smtp(context: &Context, smtp: &mut Smtp, rowid: .await?; if let Some((chat_id, timestamp_sort)) = res { - let addr = context.get_config(Config::ConfiguredAddr).await?; - let text = unencrypted_email( - context, - addr.unwrap_or_default() - .split('@') - .nth(1) - .unwrap_or_default(), - ) - .await; + let text = + unencrypted_email(context, from_addr.split('@').nth(1).unwrap_or_default()) + .await; add_info_msg_with_cmd( context, chat_id, diff --git a/src/test_utils.rs b/src/test_utils.rs index 991b2cd78..d481d9f56 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -193,21 +193,11 @@ impl TestContextManager { to.recv_msg(&sent).await } - pub async fn change_addr(&self, test_context: &TestContext, new_addr: &str) { - self.section(&format!( - "{} changes her self address and reconfigures", - test_context.name() - )); - + pub async fn change_addr(&self, test_context: &mut TestContext, new_addr: &str) { test_context.add_transport(new_addr).await; - test_context.set_primary_self_addr(new_addr).await.unwrap(); + test_context.set_sending_addr(new_addr).await; // ensure_secret_key_exists() is called during configure key::ensure_secret_key_exists(test_context).await.unwrap(); - - assert_eq!( - test_context.get_primary_self_addr().await.unwrap(), - new_addr - ); } /// Executes SecureJoin protocol between `scanner` and `scanned`. @@ -252,10 +242,9 @@ impl TestContextManager { qr: &str, ) -> ChatId { assert!(joiner.pop_sent_msg_opt().await.is_none()); - let inviter_addr = inviters[0].get_primary_self_addr().await.unwrap(); + let inviter_addr = inviters[0].sending_addr().await; for inviter in inviters { assert!(inviter.pop_sent_msg_opt().await.is_none()); - assert_eq!(inviter.get_primary_self_addr().await.unwrap(), inviter_addr); } let chat_id = join_securejoin(&joiner.ctx, qr).await.unwrap(); @@ -435,6 +424,9 @@ pub struct TestContext { pub evtracker: EventTracker, log_sink: LogSink, + + /// From address to be used when emulating sending. + from_for_test_msgs: Option, } impl TestContext { @@ -530,6 +522,7 @@ impl TestContext { dir, evtracker: EventTracker::new(evtracker_receiver), log_sink, + from_for_test_msgs: None, } } @@ -560,10 +553,6 @@ impl TestContext { add_pseudo_transport(&self.ctx, addr) .await .expect("Failed to add pseudo transport"); - self.ctx - .set_config(Config::ConfiguredAddr, Some(addr)) - .await - .expect("Failed to configure address"); if let Some(name) = addr.split('@').next() { self.set_name(name); @@ -586,6 +575,28 @@ impl TestContext { self.self_public_key.lock().await.take(); } + pub async fn set_sending_addr(&mut self, addr: &str) { + if !self + .sql + .exists("SELECT COUNT(*) FROM transports WHERE addr=?", (addr,)) + .await + .unwrap() + { + panic!("Address {addr} is not configured"); + } + self.from_for_test_msgs = Some(addr.to_string()); + } + + pub async fn sending_addr(&self) -> String { + if let Some(ref from) = self.from_for_test_msgs { + from.clone() + } else { + self.get_primary_self_addr() + .await + .expect("Cannot get From address") + } + } + /// Retrieves a sent message from the jobs table. /// /// This retrieves and removes a message which has been scheduled to send from the jobs @@ -632,10 +643,7 @@ ORDER BY id" .await .expect("Failed to load queued mail"); if queued_mail.bcc_self { - let from = self - .get_primary_self_addr() - .await - .expect("Cannot get From address"); + let from = self.sending_addr().await; smtp::add_self_recipients( &self.ctx, &mut queued_mail.recipients, @@ -734,10 +742,7 @@ ORDER BY id" .await .expect("Failed to load queued mail"); if queued_mail.bcc_self { - let from = self - .get_primary_self_addr() - .await - .expect("Cannot get self address"); + let from = self.sending_addr().await; smtp::add_self_recipients( &self.ctx, &mut queued_mail.recipients, @@ -897,7 +902,7 @@ ORDER BY id" /// Returns the [`ContactId`] for the other [`TestContext`], creating a contact if necessary. pub async fn add_or_lookup_address_contact_id(&self, other: &TestContext) -> ContactId { - let primary_self_addr = other.ctx.get_primary_self_addr().await.unwrap(); + let primary_self_addr = other.sending_addr().await; let addr = ContactAddress::new(&primary_self_addr).unwrap(); // MailinglistAddress is the lowest allowed origin, we'd prefer to not modify the // origin when creating this contact. @@ -952,7 +957,7 @@ ORDER BY id" /// If the contact does not exist yet, a new contact will be created /// with the correct fingerprint, but without the public key. pub async fn add_or_lookup_contact_id_no_key(&self, other: &TestContext) -> ContactId { - let primary_self_addr = other.ctx.get_primary_self_addr().await.unwrap(); + let primary_self_addr = other.sending_addr().await; let addr = ContactAddress::new(&primary_self_addr).unwrap(); let fingerprint = self_fingerprint(other).await.unwrap(); @@ -1242,13 +1247,14 @@ ORDER BY id" } pub async fn encrypt_raw_message( - context: &Context, + context: &TestContext, receivers: &[&TestContext], payload: &[u8], ) -> Result { let public_key = key::load_self_public_key(context).await?; + let from = context.sending_addr().await; let aheader = Aheader { - addr: context.get_primary_self_addr().await?, + addr: from.clone(), public_key: public_key.clone(), prefer_encrypt: EncryptPreference::Mutual, }; @@ -1259,7 +1265,6 @@ pub async fn encrypt_raw_message( encryption_keyring.push(key::load_self_public_key(receiver).await?); } - let from = context.get_primary_self_addr().await?; let compress = false; let mut cleartext = format!("Autocrypt: {aheader}").into_bytes(); diff --git a/src/tests/aeap.rs b/src/tests/aeap.rs index 9e862571a..8c65bea0d 100644 --- a/src/tests/aeap.rs +++ b/src/tests/aeap.rs @@ -20,13 +20,14 @@ use crate::test_utils::TestContextManager; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_change_primary_self_addr() -> Result<()> { let mut tcm = TestContextManager::new(); - let alice = tcm.alice().await; + let mut alice = tcm.alice().await; let bob = tcm.bob().await; tcm.send_recv_accept(&alice, &bob, "Hi").await; let bob_alice_chat = bob.create_chat(&alice).await; - tcm.change_addr(&alice, "alice@someotherdomain.xyz").await; + tcm.change_addr(&mut alice, "alice@someotherdomain.xyz") + .await; tcm.section("Bob sends a message to Alice, encrypting to her previous key"); let sent = bob.send_text(bob_alice_chat.id, "hi back").await; @@ -63,7 +64,7 @@ async fn check_aeap_transition(chat_for_transition: ChatForTransition) { const ALICE_NEW_ADDR: &str = "alice2@example.net"; let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; + let alice = &mut tcm.alice().await; let bob = &tcm.bob().await; tcm.send_recv_accept(alice, bob, "Hi").await; @@ -105,10 +106,7 @@ async fn check_aeap_transition(chat_for_transition: ChatForTransition) { check_that_transition_worked(bob, &groups, alice_contact, ALICE_NEW_ADDR).await; tcm.section("Test switching back"); - alice - .set_primary_self_addr("alice@example.org") - .await - .unwrap(); + alice.set_sending_addr("alice@example.org").await; let sent = alice .send_text(chat_to_send, "Hello from my old addr!") .await; @@ -205,7 +203,7 @@ async fn test_aeap_replay_attack() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_write_to_alice_after_aeap() -> Result<()> { let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; + let alice = &mut tcm.alice().await; let bob = &tcm.bob().await; let alice_grp_id = chat::create_group(alice, "Group").await?; diff --git a/src/transport.rs b/src/transport.rs index 70ca9abba..755b55174 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -276,14 +276,15 @@ impl ConfiguredLoginParam { .await } - /// Loads legacy configured param. Only used for tests and the migration. + /// Loads legacy configured param. Only used for tests and migration 131. pub(crate) async fn load_legacy(context: &Context) -> Result> { if !context.get_config_bool(Config::Configured).await? { return Ok(None); } let addr = context - .get_config(Config::ConfiguredAddr) + .sql + .get_raw_config("configured_addr") .await? .unwrap_or_default() .trim() @@ -480,11 +481,11 @@ pub(crate) async fn save_transport( ); let addr = addr_normalize(&configured.addr); - let configured_addr = context.get_config(Config::ConfiguredAddr).await?; - let mut modified = context + let modified = context .sql - .execute( - "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp) + .transaction(|transaction| { + let modified = transaction.execute( + "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp) VALUES (?, ?, ?, ?) ON CONFLICT (addr) DO UPDATE SET entered_param=excluded.entered_param, @@ -493,24 +494,18 @@ pub(crate) async fn save_transport( WHERE entered_param != excluded.entered_param OR configured_param != excluded.configured_param OR add_timestamp < excluded.add_timestamp", - ( - &addr, - serde_json::to_string(entered_param)?, - serde_json::to_string(configured)?, - add_timestamp, - ), - ) - .await? - > 0; + ( + &addr, + serde_json::to_string(entered_param)?, + serde_json::to_string(configured)?, + add_timestamp, + ), + )? > 0; + update_configured_addr(transaction)?; + Ok(modified) + }) + .await?; - if configured_addr.is_none() { - // If there is no transport yet, use the new transport for sending - context - .sql - .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr)) - .await?; - modified = true; - } Ok(modified) } @@ -625,7 +620,7 @@ pub(crate) async fn sync_transports( ) .collect(); - let (deleted_ids, reelected) = context + let deleted_ids = context .sql .transaction(|transaction| { let mut deleted_ids = Vec::new(); @@ -636,10 +631,10 @@ pub(crate) async fn sync_transports( } deleted_ids.extend(delete_transport_row(transaction, addr, *timestamp)?); } + update_configured_addr(transaction)?; modified |= !deleted_ids.is_empty(); - let reelected = maybe_update_sending_transport(transaction)?; - Ok((deleted_ids, reelected)) + Ok(deleted_ids) }) .await?; @@ -647,12 +642,6 @@ pub(crate) async fn sync_transports( purge_transport_caches(context, *transport_id).await; } - if let Some(new_addr) = reelected { - info!(context, "Re-elected sending transport {new_addr:?}."); - context.sql.uncache_raw_config("configured_addr").await; - modified = true; - } - if modified { context.self_public_key.lock().await.take(); context @@ -711,34 +700,10 @@ pub(crate) async fn purge_transport_caches(context: &Context, transport_id: u32) context.metadata.write().await.remove(&transport_id); } -/// Elects another transport for sending if the current one vanished. -/// Any remaining transport works and selection is anyway moving -/// to the authority of the SMTP loop, see -pub(crate) fn maybe_update_sending_transport( - transaction: &mut rusqlite::Transaction, -) -> Result> { - let configured_addr: String = transaction.query_row( - "SELECT value FROM config WHERE keyname='configured_addr'", - (), - |row| row.get(0), - )?; - let addrs = transport_addrs(transaction)?; - if addrs.contains(&configured_addr) { - return Ok(None); - } - let Some(new_addr) = addrs.into_iter().next() else { - return Ok(None); - }; - transaction.execute( - "UPDATE config SET value=? WHERE keyname='configured_addr'", - (&new_addr,), - )?; - Ok(Some(new_addr)) -} - /// Adds transport entry to the `transports` table with empty configuration. pub async fn add_pseudo_transport(context: &Context, addr: &str) -> Result<()> { - context.sql + context.sql.transaction(|transaction| { + transaction .execute( "INSERT INTO transports (addr, entered_param, configured_param) VALUES (?, ?, ?)", ( @@ -746,8 +711,25 @@ pub async fn add_pseudo_transport(context: &Context, addr: &str) -> Result<()> { serde_json::to_string(&EnteredLoginParam{addr: addr.to_string(), ..Default::default()})?, format!(r#"{{"addr":"{addr}","imap":[],"imap_user":"","imap_password":"","smtp":[],"smtp_user":"","smtp_password":"","certificate_checks":"Automatic","oauth2":false}}"#) ), + ).with_context(|| format!("Failed to insert pseudo transport for {addr:?}"))?; + update_configured_addr(transaction)?; + Ok(()) + }).await +} + +/// Update deprecated `configured_addr` config so it points to the first transport. +/// +/// `configured_addr` is not used anymore, but old core fails when importing a backup without it, +/// so we need to set it for backwards compatibility. +pub fn update_configured_addr(transaction: &rusqlite::Transaction) -> Result<()> { + transaction + .execute( + "INSERT OR REPLACE INTO config (keyname, value) + SELECT 'configured_addr', addr + FROM transports ORDER BY id LIMIT 1", + (), ) - .await.with_context(|| format!("Failed to insert pseudo transport for {addr:?}"))?; + .context("Failed to update configured_addr")?; Ok(()) } diff --git a/src/transport/transport_tests.rs b/src/transport/transport_tests.rs index a981c40b6..4ca90968b 100644 --- a/src/transport/transport_tests.rs +++ b/src/transport/transport_tests.rs @@ -197,10 +197,6 @@ async fn test_delete_transport() -> Result<()> { .evtracker .get_matching(|e| matches!(e, EventType::TransportsModified)) .await; - assert_eq!( - alice.get_config(Config::ConfiguredAddr).await?.as_deref(), - Some("alice@otherprovider.com") - ); sync_and_check_recipients(alice, alice2, "alice@otherprovider.com").await; check_addrs( @@ -226,50 +222,6 @@ async fn test_delete_transport() -> Result<()> { Ok(()) } -/// Tests that selecting sending transport by setting "configured_addr" does not -/// send the sync message, is not synchronized between devices even if sync message is forced, -/// and does not bump sending transport `add_timestamp`. -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_no_configured_addr_synchronization() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - let alice2 = &tcm.alice().await; - for a in [alice, alice2] { - a.set_config_bool(Config::SyncMsgs, true).await?; - a.set_config_bool(Config::BccSelf, true).await?; - } - - let addr = "alice@otherprovider.com"; - add_dummy_transport(alice, addr).await?; - send_sync_transports(alice).await?; - sync_and_check_recipients(alice, alice2, &format!("{addr} alice@example.org")).await; - - // Selects `addr` on `alice` as the sending transport - // and syncs the transport update to `alice2`, - // whose own sending transport must stay unchanged. - let old_timestamp = add_timestamp(alice2, addr).await; - let alice2_primary = alice2.get_config(Config::ConfiguredAddr).await?; - alice.set_config(Config::ConfiguredAddr, Some(addr)).await?; - assert_eq!(add_timestamp(alice, addr).await, old_timestamp); - - send_sync_transports(alice).await?; - alice.send_sync_msg().await?.unwrap(); - let sync_msg = alice.pop_sent_msg().await; - assert_eq!(sync_msg.recipients, format!("alice@example.org {addr}")); - // The sync message comes from the new sending address, - // which must not make `alice2` adopt it as its own sending address. - assert!(sync_msg.payload.contains(&format!("From: <{addr}>"))); - alice2.recv_msg_trash(&sync_msg).await; - - // add_timestamp must not change. - assert_eq!(add_timestamp(alice2, addr).await, old_timestamp); - assert_eq!( - alice2.get_config(Config::ConfiguredAddr).await?, - alice2_primary - ); - Ok(()) -} - /// Tests that `sync_transports()` requests an IO restart /// if and only if it modified anything. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -288,14 +240,6 @@ async fn test_sync_transports_requests_io_restart() -> Result<()> { Ok(()) } -async fn add_timestamp(t: &TestContext, addr: &str) -> i64 { - t.sql - .query_get_value("SELECT add_timestamp FROM transports WHERE addr=?", (addr,)) - .await - .unwrap() - .unwrap() -} - /// Tests that removing the last transport keeps it. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_removing_last_transport() -> Result<()> { @@ -308,10 +252,6 @@ async fn test_removing_last_transport() -> Result<()> { }; sync_transports(alice, &[], std::slice::from_ref(&removed)).await?; assert_eq!(alice.count_transports().await?, 1); - assert_eq!( - alice.get_config(Config::ConfiguredAddr).await?.as_deref(), - Some("alice@otherprovider.com") - ); let removed = RemovedTransportData { addr: "alice@otherprovider.com".to_string(), @@ -319,37 +259,6 @@ async fn test_removing_last_transport() -> Result<()> { }; sync_transports(alice, &[], std::slice::from_ref(&removed)).await?; assert_eq!(alice.count_transports().await?, 1); - assert_eq!( - alice.get_config(Config::ConfiguredAddr).await?.as_deref(), - Some("alice@otherprovider.com") - ); - Ok(()) -} - -/// Tests which transport is elected for sending. -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_maybe_update_sending_transport() -> Result<()> { - let t = &TestContext::new_alice().await; - - add_dummy_transport(t, "alice@one.com").await?; - assert_eq!( - t.sql.transaction(maybe_update_sending_transport).await?, - None - ); - - t.sql - .execute( - "DELETE FROM transports WHERE addr=?", - ("alice@example.org",), - ) - .await?; - assert_eq!( - t.sql - .transaction(maybe_update_sending_transport) - .await? - .as_deref(), - Some("alice@one.com") - ); Ok(()) }