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.
This commit is contained in:
link2xt
2026-05-22 01:18:45 +02:00
parent 72f5dafc68
commit 6b6021c450
3 changed files with 74 additions and 11 deletions

View File

@@ -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<Option<Message>> {
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

View File

@@ -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) =

View File

@@ -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)]