From a6baba18521e186897578cda3f4eaeb9de90082d Mon Sep 17 00:00:00 2001 From: Nico de Haen Date: Thu, 22 Jan 2026 21:15:26 +0100 Subject: [PATCH] fix: forward message with file (#7755) resolves #7724: When forwarding a message with file to another profile, the file was not copied to the target $blobdir and so the forwarded message missed it --------- Co-authored-by: Hocuri --- src/chat.rs | 9 ++++- src/chat/chat_tests.rs | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/chat.rs b/src/chat.rs index 8f9c997cb..e62627331 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -4380,7 +4380,14 @@ pub async fn forward_msgs_2ctx( } let param = &mut param; - msg.param.steal(param, Param::File); + + // When forwarding between different accounts, blob files must be physically copied + // because each account has its own blob directory. + if let Some(src_path) = param.get_file_path(ctx_src)? { + let new_blob = BlobObject::create_and_deduplicate(ctx_dst, &src_path, &src_path) + .context("Failed to copy blob file to destination account")?; + msg.param.set(Param::File, new_blob.as_name()); + } msg.param.steal(param, Param::Filename); msg.param.steal(param, Param::Width); msg.param.steal(param, Param::Height); diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index 85e492eeb..a289322a4 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -5491,6 +5491,97 @@ async fn test_forward_msgs_2ctx() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_forward_msgs_2ctx_with_file() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + // First, establish a chat between Alice and Bob to have the chat IDs + let alice_chat = alice.create_chat(bob).await; + let alice_initial = alice.send_text(alice_chat.id, "hi").await; + let bob_alice_msg = bob.recv_msg(&alice_initial).await; + let bob_chat_id = bob_alice_msg.chat_id; + bob_chat_id.accept(bob).await?; + + // Alice sends a message with an attached file to her self-chat + let alice_self_chat = alice.get_self_chat().await; + let file_bytes = b"test file content"; + let file = alice.get_blobdir().join("test.txt"); + tokio::fs::write(&file, file_bytes).await?; + + let mut msg = Message::new(Viewtype::File); + msg.set_file_and_deduplicate(alice, &file, Some("test.txt"), Some("text/plain"))?; + msg.set_text("Here's a file".to_string()); + + alice.send_msg(alice_self_chat.id, &mut msg).await; + let alice_self_msg = alice.get_last_msg().await; + + // Verify the file exists in Alice's blobdir + assert_eq!(alice_self_msg.viewtype, Viewtype::File); + let alice_original_file_path = alice_self_msg.get_file(alice).unwrap(); + let alice_original_content = tokio::fs::read(&alice_original_file_path).await?; + assert_eq!(alice_original_content, file_bytes); + + // Alice forwards the message to Bob using forward_msgs_2ctx + forward_msgs_2ctx(alice, &[alice_self_msg.id], bob, bob_chat_id).await?; + + // Bob should have the forwarded message with the file in his database + let bob_msg = bob.get_last_msg().await; + assert_eq!(bob_msg.viewtype, Viewtype::File); + assert!(bob_msg.is_forwarded()); + assert_eq!(bob_msg.text, "Here's a file"); + assert_eq!(bob_msg.from_id, ContactId::SELF); + + // Verify Bob has the file in his blobdir with correct content + let bob_file_path = bob_msg.get_file(bob).unwrap(); + let bob_file_content = tokio::fs::read(&bob_file_path).await?; + assert_eq!(bob_file_content, file_bytes); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_forward_msgs_2ctx_missing_blob() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + let alice_chat = alice.create_chat(bob).await; + let alice_initial = alice.send_text(alice_chat.id, "hi").await; + let bob_alice_msg = bob.recv_msg(&alice_initial).await; + let bob_chat_id = bob_alice_msg.chat_id; + bob_chat_id.accept(bob).await?; + // Alice sends a file to her self-chat + let alice_self_chat = alice.get_self_chat().await; + let file_bytes = b"test content"; + let file = alice.get_blobdir().join("test.txt"); + tokio::fs::write(&file, file_bytes).await?; + + let mut msg = Message::new(Viewtype::File); + msg.set_file_and_deduplicate(alice, &file, Some("test.txt"), Some("text/plain"))?; + msg.set_text("File message".to_string()); + + alice.send_msg(alice_self_chat.id, &mut msg).await; + let alice_self_msg = alice.get_last_msg().await; + + // Delete the blob file from Alice's blobdir to simulate a missing file + let alice_file_path = alice_self_msg.get_file(alice).unwrap(); + tokio::fs::remove_file(&alice_file_path).await?; + + // Alice tries to forward the message - this should fail with an error + let result = forward_msgs_2ctx(alice, &[alice_self_msg.id], bob, bob_chat_id).await; + assert!(result.is_err()); + assert!( + result + .unwrap_err() + .to_string() + .contains("Failed to copy blob file") + ); + + Ok(()) +} + /// Tests that in multi-device setup /// second device learns the key of a contact /// via Autocrypt-Gossip in 1:1 chats.