diff --git a/src/config.rs b/src/config.rs index 753ad577c..7642ab9ff 100644 --- a/src/config.rs +++ b/src/config.rs @@ -412,6 +412,10 @@ pub enum Config { /// storing the same token multiple times on the server. EncryptedDeviceToken, + /// Make `TestContext::pop_sent_msg_opt()` and related functions pop messages from the `smtp` + /// head, i.e. make it a queue. For historical reasons the default is a stack. + PopSentMsgFromHead, + /// Enables running test hooks, e.g. see `InnerContext::pre_encrypt_mime_hook`. /// This way is better than conditional compilation, i.e. `#[cfg(test)]`, because tests not /// using this still run unmodified code. diff --git a/src/context.rs b/src/context.rs index 92c1a36ce..90d5cfe2b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1052,6 +1052,13 @@ impl Context { .await? .to_string(), ); + res.insert( + "pop_sent_msg_from_head", + self.sql + .get_raw_config("pop_sent_msg_from_head") + .await? + .unwrap_or_default(), + ); res.insert( "test_hooks", self.sql diff --git a/src/message.rs b/src/message.rs index d292f5224..1c92f5273 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1909,9 +1909,10 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> Result<()> // // We also don't send read receipts for contact requests. // Read receipts will not be sent even after accepting the chat. + let wants_mdn = curr_param.get_bool(Param::WantsMdn).unwrap_or_default(); let to_id = if curr_blocked == Blocked::Not && !curr_hidden - && curr_param.get_bool(Param::WantsMdn).unwrap_or_default() + && wants_mdn && curr_param.get_cmd() == SystemMessage::Unknown && context.should_send_mdns().await? { @@ -1933,6 +1934,11 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> Result<()> None }; if let Some(to_id) = to_id { + info!( + context, + "Queuing MDN to {to_id} for {id} from {curr_from_id}, wants_mdn={wants_mdn}, cmd={}.", + curr_param.get_cmd() + ); context .sql .execute( diff --git a/src/mimeparser.rs b/src/mimeparser.rs index e57c31e10..64a354f68 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -2498,6 +2498,8 @@ async fn handle_mdn( let Some((msg_id, chat_id, has_mdns, is_dup)) = context .sql .query_row_optional( + // MDN on a pre-message references the post-message, see `receive_imf`. So we can't tell + // which one was seen, but this is on purpose. "SELECT m.id AS msg_id, c.id AS chat_id, diff --git a/src/test_utils.rs b/src/test_utils.rs index 1329dcc51..8977a8722 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -623,16 +623,25 @@ impl TestContext { } pub async fn pop_sent_msg_opt(&self, timeout: Duration) -> Option> { + let from_head = self + .get_config_bool(Config::PopSentMsgFromHead) + .await + .unwrap(); + let order_subst = match from_head { + true => "", + false => " DESC", + }; let start = Instant::now(); let (rowid, msg_id, payload, recipients) = loop { let row = self .ctx .sql .query_row_optional( - r#" - SELECT id, msg_id, mime, recipients - FROM smtp - ORDER BY id DESC"#, + &format!( + "SELECT id, msg_id, mime, recipients +FROM smtp +ORDER BY id{order_subst}" + ), (), |row| { let rowid: i64 = row.get(0)?; diff --git a/src/tests/pre_messages/receiving.rs b/src/tests/pre_messages/receiving.rs index a891c0128..74719ca50 100644 --- a/src/tests/pre_messages/receiving.rs +++ b/src/tests/pre_messages/receiving.rs @@ -4,7 +4,6 @@ use pretty_assertions::assert_eq; use crate::EventType; use crate::chat; -use crate::chat::send_msg; use crate::config::Config; use crate::contact; use crate::download::{DownloadState, PRE_MSG_ATTACHMENT_SIZE_THRESHOLD, PostMsgMetadata}; @@ -262,6 +261,64 @@ async fn test_lost_pre_msg() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_pre_msg_mdn_before_sending_full() -> Result<()> { + pre_msg_mdn_before_sending_full("").await +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_pre_msg_mdn_before_sending_full_with_text() -> Result<()> { + pre_msg_mdn_before_sending_full("text").await +} + +async fn pre_msg_mdn_before_sending_full(text: &str) -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + alice + .set_config_bool(Config::PopSentMsgFromHead, true) + .await?; + let bob = &tcm.bob().await; + let alice_chat_id = alice.create_group_with_members("", &[bob]).await; + + let file_bytes = include_bytes!("../../../test-data/image/screenshot.gif"); + let mut msg = Message::new(Viewtype::Image); + msg.set_file_from_bytes(alice, "a.jpg", file_bytes, None)?; + msg.set_text(text.to_string()); + let pre_msg = alice.send_msg(alice_chat_id, &mut msg).await; + let alice_msg_id = msg.id; + + let msg = bob.recv_msg(&pre_msg).await; + assert_eq!(msg.download_state, DownloadState::Available); + assert_eq!(msg.id.get_state(bob).await?, MessageState::InFresh); + assert_eq!(msg.text, text); + assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default()); + msg.chat_id.accept(bob).await?; + markseen_msgs(bob, vec![msg.id]).await?; + assert_eq!(msg.id.get_state(bob).await?, MessageState::InSeen); + assert_eq!( + bob.sql + .count( + "SELECT COUNT(*) FROM smtp_mdns WHERE from_id=?", + (msg.from_id,) + ) + .await?, + 1 + ); + smtp::queue_mdn(bob).await?; + alice.recv_msg_trash(&bob.pop_sent_msg().await).await; + assert_eq!( + alice_msg_id.get_state(alice).await?, + MessageState::OutPending + ); + + let _full_msg = alice.pop_sent_msg().await; + assert_eq!( + alice_msg_id.get_state(alice).await?, + MessageState::OutMdnRcvd + ); + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_post_msg_bad_sender() -> Result<()> { let mut tcm = TestContextManager::new(); @@ -547,7 +604,7 @@ async fn test_webxdc_updates_in_post_message_after_pre_message() -> Result<()> { .send_webxdc_status_update(alice_instance.id, r#"{"payload":42, "info":"i"}"#) .await?; - send_msg(alice, alice_chat_id, &mut alice_instance).await?; + chat::send_msg(alice, alice_chat_id, &mut alice_instance).await?; let post_message = alice.pop_sent_msg().await; let pre_message = alice.pop_sent_msg().await; @@ -588,7 +645,7 @@ async fn test_webxdc_updates_in_post_message_without_pre_message() -> Result<()> .send_webxdc_status_update(alice_instance.id, r#"{"payload":42, "info":"i"}"#) .await?; - send_msg(alice, alice_chat_id, &mut alice_instance).await?; + chat::send_msg(alice, alice_chat_id, &mut alice_instance).await?; let post_message = alice.pop_sent_msg().await; let pre_message = alice.pop_sent_msg().await; @@ -641,7 +698,10 @@ async fn test_markseen_pre_msg() -> Result<()> { assert_eq!( alice .sql - .count("SELECT COUNT(*) FROM smtp_mdns", ()) + .count( + "SELECT COUNT(*) FROM smtp_mdns WHERE from_id=?", + (msg.from_id,) + ) .await?, 1 );