From 6b6021c4504ba37b0b9f3925936fdbdb8f0e2c26 Mon Sep 17 00:00:00 2001 From: link2xt Date: Fri, 22 May 2026 01:18:45 +0200 Subject: [PATCH] fix: do not fail to receive post-message with status updates for deleted webxdc receive_imf should not fail in this case, but trash the post-message and updates. The problem with previously used message::rfc724_mid_exists check was that rfc724_mid_exists may return a trashed message. --- src/message.rs | 29 +++++++++++++++++++++ src/receive_imf.rs | 17 +++++-------- src/tests/pre_messages/receiving.rs | 39 +++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/message.rs b/src/message.rs index e31bf8310..240231a5d 100644 --- a/src/message.rs +++ b/src/message.rs @@ -602,6 +602,35 @@ impl Message { Ok(msg) } + /// Loads the message with given Message-ID from the database. + /// + /// Cannot return a trashed message. + pub async fn load_by_rfc724_mid_optional( + context: &Context, + rfc724_mid: &str, + ) -> Result> { + if let Some(msg_id) = context + .sql + .query_row_optional( + "SELECT id FROM msgs WHERE rfc724_mid=? AND chat_id != ?", + (rfc724_mid, DC_CHAT_ID_TRASH), + |row| { + let msg_id: MsgId = row.get(0)?; + Ok(msg_id) + }, + ) + .await? + { + // Non-trashed message exists, load it. + let msg = Self::load_from_db(context, msg_id) + .await + .context("Failed to load the message we just checked exists")?; + Ok(Some(msg)) + } else { + Ok(None) + } + } + /// Returns additional text which is appended to the message's text field /// when it is loaded from the database. /// Currently this is used to add infomation to pre-messages of what the download will be and how large it is diff --git a/src/receive_imf.rs b/src/receive_imf.rs index c1bc3e491..718ed871f 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -835,21 +835,16 @@ UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1 { can_info_msg = false; if mime_parser.pre_message == PreMessageMode::Post - && let Some(msg_id) = message::rfc724_mid_exists(context, rfc724_mid_orig).await? + && let Some(msg) = + Message::load_by_rfc724_mid_optional(context, rfc724_mid_orig).await? { // The messsage is a post-message and pre-message exists. // Assign status update to existing message because just received post-message will be trashed. - Some( - Message::load_from_db(context, msg_id) - .await - .context("Failed to load webxdc instance that we just checked exists")?, - ) + Some(msg) } else { - Some( - Message::load_from_db(context, insert_msg_id) - .await - .context("Failed to load just created webxdc instance")?, - ) + Message::load_from_db_optional(context, insert_msg_id) + .await + .context("Failed to load just created webxdc instance")? } } else if let Some(field) = mime_parser.get_header(HeaderDef::InReplyTo) { if let Some(instance) = diff --git a/src/tests/pre_messages/receiving.rs b/src/tests/pre_messages/receiving.rs index e92f6d21d..4e4c73f80 100644 --- a/src/tests/pre_messages/receiving.rs +++ b/src/tests/pre_messages/receiving.rs @@ -565,6 +565,45 @@ async fn test_webxdc_updates_in_post_message_after_pre_message() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_webxdc_updates_in_post_message_after_deleted_pre_message() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + let alice_chat_id = alice.create_chat_id(bob).await; + let big_webxdc_app = big_webxdc_app().await?; + + let mut alice_instance = Message::new(Viewtype::Webxdc); + alice_instance.set_file_from_bytes(alice, "test.xdc", &big_webxdc_app, None)?; + alice_instance.set_text("Test".to_string()); + alice_chat_id + .set_draft(alice, Some(&mut alice_instance)) + .await?; + alice + .send_webxdc_status_update(alice_instance.id, r#"{"payload":42, "info":"i"}"#) + .await?; + + 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; + + let bob_instance = bob.recv_msg(&pre_message).await; + assert_eq!(bob_instance.download_state, DownloadState::Available); + delete_msgs(bob, &[bob_instance.id]).await?; + + bob.recv_msg_trash(&post_message).await; + + // Deleted message stays trashed because of a tombstone. + assert!( + Message::load_from_db_optional(bob, bob_instance.id) + .await? + .is_none() + ); + + Ok(()) +} + /// Tests receiving of a large webxdc post-message with updates attached /// to the the .xdc post-message when pre-message arrives later. #[tokio::test(flavor = "multi_thread", worker_threads = 2)]