diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 961290d8c..0d971bf24 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -253,6 +253,10 @@ impl MimeFactory { let mut missing_key_addresses = BTreeSet::new(); context .sql + // Sort recipients by `add_timestamp DESC` so that if the group is large and there + // are multiple SMTP messages, a newly added member receives the member addition + // message earlier and has gossiped keys of other members (otherwise the new member + // may receive messages from other members earlier and fail to verify them). .query_map( "SELECT c.authname, @@ -266,7 +270,8 @@ impl MimeFactory { LEFT JOIN contacts c ON cc.contact_id=c.id LEFT JOIN public_keys k ON k.fingerprint=c.fingerprint WHERE cc.chat_id=? - AND (cc.contact_id>9 OR (cc.contact_id=1 AND ?))", + AND (cc.contact_id>9 OR (cc.contact_id=1 AND ?)) + ORDER BY cc.add_timestamp DESC", (msg.chat_id, chat.typ == Chattype::Group), |row| { let authname: String = row.get(0)?; diff --git a/src/mimefactory/mimefactory_tests.rs b/src/mimefactory/mimefactory_tests.rs index 95a1e41a5..49fbdc228 100644 --- a/src/mimefactory/mimefactory_tests.rs +++ b/src/mimefactory/mimefactory_tests.rs @@ -2,6 +2,7 @@ use deltachat_contact_tools::ContactAddress; use mail_builder::headers::Header; use mailparse::{MailHeaderMap, addrparse_header}; use std::str; +use std::time::Duration; use super::*; use crate::chat::{ @@ -16,6 +17,7 @@ use crate::message; use crate::mimeparser::MimeMessage; use crate::receive_imf::receive_imf; use crate::test_utils::{TestContext, TestContextManager, get_chat_msg}; +use crate::tools::SystemTime; fn render_email_address(display_name: &str, addr: &str) -> String { let mut output = Vec::::new(); @@ -867,6 +869,43 @@ async fn test_dont_remove_self() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_new_member_is_first_recipient() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + let charlie = &tcm.charlie().await; + + let bob_id = alice.add_or_lookup_contact_id(bob).await; + let charlie_id = alice.add_or_lookup_contact_id(charlie).await; + + let group = alice + .create_group_with_members(ProtectionStatus::Unprotected, "Group", &[bob]) + .await; + alice.send_text(group, "Hi! I created a group.").await; + + SystemTime::shift(Duration::from_secs(60)); + add_contact_to_chat(alice, group, charlie_id).await?; + let sent_msg = alice.pop_sent_msg().await; + assert!( + sent_msg + .recipients + .starts_with(&charlie.get_config(Config::Addr).await?.unwrap()) + ); + + remove_contact_from_chat(alice, group, bob_id).await?; + alice.pop_sent_msg().await; + 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::Addr).await?.unwrap()) + ); + Ok(()) +} + /// Regression test: mimefactory should never create an empty to header, /// also not if the Selftalk parameter is missing #[tokio::test(flavor = "multi_thread", worker_threads = 2)]