diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index a32da4a1a2..866e6a7ce0 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -7241,13 +7241,7 @@ void dc_event_unref(dc_event_t* event); /// `%1$s` will be replaced by the percentage used #define DC_STR_QUOTA_EXCEEDING_MSG_BODY 98 -/// "%1$s message" -/// -/// Used as the message body when a message -/// was not yet downloaded completely -/// (dc_msg_get_download_state() is e.g. @ref DC_DOWNLOAD_AVAILABLE). -/// -/// `%1$s` will be replaced by human-readable size (e.g. "1.2 MiB"). +/// @deprecated Deprecated 2025-11-12, this string is no longer needed. #define DC_STR_PARTIAL_DOWNLOAD_MSG_BODY 99 /// "Multi Device Synchronization" diff --git a/deltachat-rpc-client/tests/test_chatlist_events.py b/deltachat-rpc-client/tests/test_chatlist_events.py index 05bec0795f..edc7cffe03 100644 --- a/deltachat-rpc-client/tests/test_chatlist_events.py +++ b/deltachat-rpc-client/tests/test_chatlist_events.py @@ -1,7 +1,5 @@ from __future__ import annotations -import base64 -import os from typing import TYPE_CHECKING from deltachat_rpc_client import Account, EventType, const @@ -111,40 +109,6 @@ def test_delivery_status_failed(acfactory: ACFactory) -> None: assert failing_message.get_snapshot().state == const.MessageState.OUT_FAILED -def test_download_on_demand(acfactory: ACFactory) -> None: - """ - Test if download on demand emits chatlist update events. - This is only needed for last message in chat, but finding that out is too expensive, so it's always emitted - """ - alice, bob = acfactory.get_online_accounts(2) - - alice_contact_bob = alice.create_contact(bob, "Bob") - alice_chat_bob = alice_contact_bob.create_chat() - alice_chat_bob.send_text("hi") - - alice.set_config("download_limit", "1") - - msg = bob.wait_for_incoming_msg() - chat_id = msg.get_snapshot().chat_id - msg.get_snapshot().chat.accept() - bob.get_chat_by_id(chat_id).send_message( - "Hello World, this message is bigger than 5 bytes", - html=base64.b64encode(os.urandom(300000)).decode("utf-8"), - ) - - message = alice.wait_for_incoming_msg() - snapshot = message.get_snapshot() - assert snapshot.download_state == const.DownloadState.AVAILABLE - - alice.clear_all_events() - - snapshot = message.get_snapshot() - chat_id = snapshot.chat_id - alice._rpc.download_full_message(alice.id, message.id) - - wait_for_chatlist_specific_item(alice, chat_id) - - def get_multi_account_test_setup(acfactory: ACFactory) -> [Account, Account, Account]: alice, bob = acfactory.get_online_accounts(2) diff --git a/deltachat-rpc-client/tests/test_something.py b/deltachat-rpc-client/tests/test_something.py index 70b2c58039..ad30f5eac7 100644 --- a/deltachat-rpc-client/tests/test_something.py +++ b/deltachat-rpc-client/tests/test_something.py @@ -336,7 +336,7 @@ def test_receive_imf_failure(acfactory) -> None: alice_contact_bob = alice.create_contact(bob, "Bob") alice_chat_bob = alice_contact_bob.create_chat() - bob.set_config("fail_on_receiving_full_msg", "1") + bob.set_config("simulate_receive_imf_error", "1") alice_chat_bob.send_text("Hello!") event = bob.wait_for_event(EventType.MSGS_CHANGED) assert event.chat_id == bob.get_device_chat().id @@ -345,12 +345,12 @@ def test_receive_imf_failure(acfactory) -> None: snapshot = message.get_snapshot() assert ( snapshot.text == "❌ Failed to receive a message:" - " Condition failed: `!context.get_config_bool(Config::FailOnReceivingFullMsg).await?`." + " Condition failed: `!context.get_config_bool(Config::SimulateReceiveImfError).await?`." " Please report this bug to delta@merlinux.eu or https://support.delta.chat/." ) # The failed message doesn't break the IMAP loop. - bob.set_config("fail_on_receiving_full_msg", "0") + bob.set_config("simulate_receive_imf_error", "0") alice_chat_bob.send_text("Hello again!") event = bob.wait_for_incoming_msg_event() msg_id = event.msg_id @@ -604,60 +604,6 @@ def test_mdn_doesnt_break_autocrypt(acfactory) -> None: assert snapshot.show_padlock -def test_reaction_to_partially_fetched_msg(acfactory, tmp_path): - """See https://github.com/deltachat/deltachat-core-rust/issues/3688 "Partially downloaded - messages are received out of order". - - If the Inbox contains X small messages followed by Y large messages followed by Z small - messages, Delta Chat first downloaded a batch of X+Z messages, and then a batch of Y messages. - - This bug was discovered by @Simon-Laux while testing reactions PR #3644 and can be reproduced - with online test as follows: - - Bob enables download limit and goes offline. - - Alice sends a large message to Bob and reacts to this message with a thumbs-up. - - Bob goes online - - Bob first processes a reaction message and throws it away because there is no corresponding - message, then processes a partially downloaded message. - - As a result, Bob does not see a reaction - """ - download_limit = 300000 - ac1, ac2 = acfactory.get_online_accounts(2) - ac1_addr = ac1.get_config("addr") - chat = ac1.create_chat(ac2) - ac2.set_config("download_limit", str(download_limit)) - ac2.stop_io() - - logging.info("sending small+large messages from ac1 to ac2") - msgs = [] - msgs.append(chat.send_text("hi")) - path = tmp_path / "large" - path.write_bytes(os.urandom(download_limit + 1)) - msgs.append(chat.send_file(str(path))) - for m in msgs: - m.wait_until_delivered() - - logging.info("sending a reaction to the large message from ac1 to ac2") - # TODO: Find the reason of an occasional message reordering on the server (so that the reaction - # has a lower UID than the previous message). W/a is to sleep for some time to let the reaction - # have a later INTERNALDATE. - time.sleep(1.1) - react_str = "\N{THUMBS UP SIGN}" - msgs.append(msgs[-1].send_reaction(react_str)) - msgs[-1].wait_until_delivered() - - ac2.start_io() - - logging.info("wait for ac2 to receive a reaction") - msg2 = Message(ac2, ac2.wait_for_reactions_changed().msg_id) - assert msg2.get_sender_contact().get_snapshot().address == ac1_addr - assert msg2.get_snapshot().download_state == DownloadState.AVAILABLE - reactions = msg2.get_reactions() - contacts = [Contact(ac2, int(i)) for i in reactions.reactions_by_contact] - assert len(contacts) == 1 - assert contacts[0].get_snapshot().address == ac1_addr - assert list(reactions.reactions_by_contact.values())[0] == [react_str] - - def test_reactions_for_a_reordering_move(acfactory, direct_imap): """When a batch of messages is moved from Inbox to DeltaChat folder with a single MOVE command, their UIDs may be reordered (e.g. Gmail is known for that) which led to that messages were @@ -702,50 +648,6 @@ def test_reactions_for_a_reordering_move(acfactory, direct_imap): assert list(reactions.reactions_by_contact.values())[0] == [react_str] -@pytest.mark.parametrize("n_accounts", [3, 2]) -def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts): - download_limit = 300000 - - alice, *others = acfactory.get_online_accounts(n_accounts) - bob = others[0] - - alice_group = alice.create_group("test group") - for account in others: - chat = account.create_chat(alice) - chat.send_text("Hello Alice!") - assert alice.get_message_by_id(alice.wait_for_incoming_msg_event().msg_id).get_snapshot().text == "Hello Alice!" - - contact = alice.create_contact(account) - alice_group.add_contact(contact) - - if n_accounts == 2: - bob_chat_alice = bob.create_chat(alice) - bob.set_config("download_limit", str(download_limit)) - - alice_group.send_text("hi") - snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot() - assert snapshot.text == "hi" - bob_group = snapshot.chat - - path = tmp_path / "large" - path.write_bytes(os.urandom(download_limit + 1)) - - for i in range(10): - logging.info("Sending message %s", i) - alice_group.send_file(str(path)) - snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot() - assert snapshot.download_state == DownloadState.AVAILABLE - if n_accounts > 2: - assert snapshot.chat == bob_group - else: - # Group contains only Alice and Bob, - # so partially downloaded messages are - # hard to distinguish from private replies to group messages. - # - # Message may be a private reply, so we assign it to 1:1 chat with Alice. - assert snapshot.chat == bob_chat_alice - - def test_markseen_contact_request(acfactory): """ Test that seen status is synchronized for contact request messages diff --git a/python/tests/test_1_online.py b/python/tests/test_1_online.py index 7c235960e7..4950891f02 100644 --- a/python/tests/test_1_online.py +++ b/python/tests/test_1_online.py @@ -1,7 +1,6 @@ import os import queue import sys -import base64 from datetime import datetime, timezone import pytest @@ -222,38 +221,6 @@ def test_webxdc_huge_update(acfactory, data, lp): assert update["payload"] == payload -def test_webxdc_download_on_demand(acfactory, data, lp): - ac1, ac2 = acfactory.get_online_accounts(2) - acfactory.introduce_each_other([ac1, ac2]) - chat = acfactory.get_accepted_chat(ac1, ac2) - - msg1 = Message.new_empty(ac1, "webxdc") - msg1.set_text("message1") - msg1.set_file(data.get_path("webxdc/minimal.xdc")) - msg1 = chat.send_msg(msg1) - assert msg1.is_webxdc() - assert msg1.filename - - msg2 = ac2._evtracker.wait_next_incoming_message() - assert msg2.is_webxdc() - - lp.sec("ac2 sets download limit") - ac2.set_config("download_limit", "100") - assert msg1.send_status_update({"payload": base64.b64encode(os.urandom(300000))}, "some test data") - ac2_update = ac2._evtracker.wait_next_incoming_message() - assert ac2_update.download_state == dc.const.DC_DOWNLOAD_AVAILABLE - assert not msg2.get_status_updates() - - ac2_update.download_full() - ac2._evtracker.get_matching("DC_EVENT_WEBXDC_STATUS_UPDATE") - assert msg2.get_status_updates() - - # Get a event notifying that the message disappeared from the chat. - msgs_changed_event = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") - assert msgs_changed_event.data1 == msg2.chat.id - assert msgs_changed_event.data2 == 0 - - def test_enable_mvbox_move(acfactory, lp): (ac1,) = acfactory.get_online_accounts(1) diff --git a/src/calls/calls_tests.rs b/src/calls/calls_tests.rs index 3f983d8438..23b3947a2e 100644 --- a/src/calls/calls_tests.rs +++ b/src/calls/calls_tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::chat::forward_msgs; use crate::config::Config; use crate::constants::DC_CHAT_ID_TRASH; -use crate::receive_imf::{receive_imf, receive_imf_from_inbox}; +use crate::receive_imf::receive_imf; use crate::test_utils::{TestContext, TestContextManager}; struct CallSetup { @@ -610,65 +610,3 @@ async fn test_end_text_call() -> Result<()> { Ok(()) } - -/// Tests that partially downloaded "call ended" -/// messages are not processed. -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_no_partial_calls() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - - let seen = false; - - // The messages in the test - // have no `Date` on purpose, - // so they are treated as new. - let received_call = receive_imf( - alice, - b"From: bob@example.net\n\ - To: alice@example.org\n\ - Message-ID: \n\ - Chat-Version: 1.0\n\ - Chat-Content: call\n\ - Chat-Webrtc-Room: YWFhYWFhYWFhCg==\n\ - \n\ - Hello, this is a call\n", - seen, - ) - .await? - .unwrap(); - assert_eq!(received_call.msg_ids.len(), 1); - let call_msg = Message::load_from_db(alice, received_call.msg_ids[0]) - .await - .unwrap(); - assert_eq!(call_msg.viewtype, Viewtype::Call); - assert_eq!(call_state(alice, call_msg.id).await?, CallState::Alerting); - - let imf_raw = b"From: bob@example.net\n\ - To: alice@example.org\n\ - Message-ID: \n\ - In-Reply-To: \n\ - Chat-Version: 1.0\n\ - Chat-Content: call-ended\n\ - \n\ - Call ended\n"; - receive_imf_from_inbox( - alice, - "second@example.net", - imf_raw, - seen, - Some(imf_raw.len().try_into().unwrap()), - ) - .await?; - - // The call is still not ended. - assert_eq!(call_state(alice, call_msg.id).await?, CallState::Alerting); - - // Fully downloading the message ends the call. - receive_imf_from_inbox(alice, "second@example.net", imf_raw, seen, None) - .await - .context("Failed to fully download end call message")?; - assert_eq!(call_state(alice, call_msg.id).await?, CallState::Missed); - - Ok(()) -} diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index 0181c1b975..812b9a7ddd 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -3130,7 +3130,7 @@ async fn test_broadcast_channel_protected_listid() -> Result<()> { .await? .grpid; - let parsed = mimeparser::MimeMessage::from_bytes(bob, sent.payload.as_bytes(), None).await?; + let parsed = mimeparser::MimeMessage::from_bytes(bob, sent.payload.as_bytes()).await?; assert_eq!( parsed.get_mailinglist_header().unwrap(), format!("My Channel <{}>", alice_list_id) @@ -3325,7 +3325,7 @@ async fn test_leave_broadcast_multidevice() -> Result<()> { remove_contact_from_chat(bob0, bob_chat_id, ContactId::SELF).await?; let leave_msg = bob0.pop_sent_msg().await; - let parsed = MimeMessage::from_bytes(bob1, leave_msg.payload().as_bytes(), None).await?; + let parsed = MimeMessage::from_bytes(bob1, leave_msg.payload().as_bytes()).await?; assert_eq!( parsed.parts[0].msg, stock_str::msg_group_left_remote(bob0).await diff --git a/src/config.rs b/src/config.rs index 57a5ce4151..15183daf30 100644 --- a/src/config.rs +++ b/src/config.rs @@ -438,8 +438,8 @@ pub enum Config { /// storing the same token multiple times on the server. EncryptedDeviceToken, - /// Return an error from `receive_imf_inner()` for a fully downloaded message. For tests. - FailOnReceivingFullMsg, + /// Return an error from `receive_imf_inner()`. For tests. + SimulateReceiveImfError, } impl Config { diff --git a/src/context/context_tests.rs b/src/context/context_tests.rs index 4a20c3af37..35aea14034 100644 --- a/src/context/context_tests.rs +++ b/src/context/context_tests.rs @@ -297,6 +297,7 @@ async fn test_get_info_completeness() { "encrypted_device_token", "stats_last_update", "stats_last_old_contact_id", + "simulate_receive_imf_error", ]; let t = TestContext::new().await; let info = t.get_info().await.unwrap(); diff --git a/src/download.rs b/src/download.rs index 4176ef2306..bf54662175 100644 --- a/src/download.rs +++ b/src/download.rs @@ -1,27 +1,16 @@ //! # Download large messages manually. -use std::cmp::max; use std::collections::BTreeMap; use anyhow::{Result, anyhow, bail, ensure}; use deltachat_derive::{FromSql, ToSql}; use serde::{Deserialize, Serialize}; -use crate::config::Config; use crate::context::Context; use crate::imap::session::Session; use crate::log::info; -use crate::message::{Message, MsgId, Viewtype}; -use crate::mimeparser::{MimeMessage, Part}; -use crate::{EventType, chatlist_events, stock_str}; - -/// Download limits should not be used below `MIN_DOWNLOAD_LIMIT`. -/// -/// For better UX, some messages as add-member, non-delivery-reports (NDN) or read-receipts (MDN) -/// should always be downloaded completely to handle them correctly, -/// also in larger groups and if group and contact avatar are attached. -/// Most of these cases are caught by `MIN_DOWNLOAD_LIMIT`. -pub(crate) const MIN_DOWNLOAD_LIMIT: u32 = 163840; +use crate::message::{Message, MsgId}; +use crate::{EventType, chatlist_events}; /// If a message is downloaded only partially /// and `delete_server_after` is set to small timeouts (eg. "at once"), @@ -64,18 +53,6 @@ pub enum DownloadState { InProgress = 1000, } -impl Context { - // Returns validated download limit or `None` for "no limit". - pub(crate) async fn download_limit(&self) -> Result> { - let download_limit = self.get_config_int(Config::DownloadLimit).await?; - if download_limit <= 0 { - Ok(None) - } else { - Ok(Some(max(MIN_DOWNLOAD_LIMIT, download_limit as u32))) - } - } -} - impl MsgId { /// Schedules full message download for partially downloaded message. pub async fn download_full(self, context: &Context) -> Result<()> { @@ -205,7 +182,7 @@ impl Session { let mut uid_message_ids: BTreeMap = BTreeMap::new(); uid_message_ids.insert(uid, rfc724_mid); let (sender, receiver) = async_channel::unbounded(); - self.fetch_many_msgs(context, folder, vec![uid], &uid_message_ids, false, sender) + self.fetch_many_msgs(context, folder, vec![uid], &uid_message_ids, sender) .await?; if receiver.recv().await.is_err() { bail!("Failed to fetch UID {uid}"); @@ -214,45 +191,14 @@ impl Session { } } -impl MimeMessage { - /// Creates a placeholder part and add that to `parts`. - /// - /// To create the placeholder, only the outermost header can be used, - /// the mime-structure itself is not available. - /// - /// The placeholder part currently contains a text with size and availability of the message. - pub(crate) async fn create_stub_from_partial_download( - &mut self, - context: &Context, - org_bytes: u32, - ) -> Result<()> { - let text = format!( - "[{}]", - stock_str::partial_download_msg_body(context, org_bytes).await - ); - - info!(context, "Partial download: {}", text); - - self.do_add_single_part(Part { - typ: Viewtype::Text, - msg: text, - ..Default::default() - }); - - Ok(()) - } -} - #[cfg(test)] mod tests { use num_traits::FromPrimitive; use super::*; - use crate::chat::{get_chat_msgs, send_msg}; - use crate::ephemeral::Timer; - use crate::message::delete_msgs; + use crate::chat::send_msg; use crate::receive_imf::receive_imf_from_inbox; - use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager}; + use crate::test_utils::TestContext; #[test] fn test_downloadstate_values() { @@ -270,29 +216,6 @@ mod tests { ); } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_download_limit() -> Result<()> { - let t = TestContext::new_alice().await; - - assert_eq!(t.download_limit().await?, None); - - t.set_config(Config::DownloadLimit, Some("200000")).await?; - assert_eq!(t.download_limit().await?, Some(200000)); - - t.set_config(Config::DownloadLimit, Some("20000")).await?; - assert_eq!(t.download_limit().await?, Some(MIN_DOWNLOAD_LIMIT)); - - t.set_config(Config::DownloadLimit, None).await?; - assert_eq!(t.download_limit().await?, None); - - for val in &["0", "-1", "-100", "", "foo"] { - t.set_config(Config::DownloadLimit, Some(val)).await?; - assert_eq!(t.download_limit().await?, None); - } - - Ok(()) - } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_update_download_state() -> Result<()> { let t = TestContext::new_alice().await; @@ -326,7 +249,7 @@ mod tests { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_partial_receive_imf() -> Result<()> { + async fn test_download_stub_message() -> Result<()> { let t = TestContext::new_alice().await; let header = "Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ @@ -338,28 +261,31 @@ mod tests { Date: Sun, 22 Mar 2020 22:37:57 +0000\ Content-Type: text/plain"; - receive_imf_from_inbox( - &t, - "Mr.12345678901@example.com", - header.as_bytes(), - false, - Some(100000), - ) - .await?; + t.sql + .execute( + r#"INSERT INTO chats VALUES( + 11001,100,'bob@example.com',0,'',2,'', + replace('C=1763151754\nt=foo','\n',char(10)),0,0,0,0,0,1763151754,0,NULL,0); + "#, + (), + ) + .await?; + t.sql.execute(r#"INSERT INTO msgs VALUES( + 11001,'Mr.12345678901@example.com','',0, + 11001,11001,1,1763151754,10,10,1,0, + '[97.66 KiB message]','','',0,1763151754,1763151754,0,X'', + '','',1,0,'',0,0,0,'foo',10,replace('Hop: From: userid; Date: Mon, 4 Dec 2006 13:51:39 +0000\n\nDKIM Results: Passed=true','\n',char(10)),1,NULL,0); + "#, ()).await?; let msg = t.get_last_msg().await; assert_eq!(msg.download_state(), DownloadState::Available); assert_eq!(msg.get_subject(), "foo"); - assert!( - msg.get_text() - .contains(&stock_str::partial_download_msg_body(&t, 100000).await) - ); + assert!(msg.get_text().contains("[97.66 KiB message]")); receive_imf_from_inbox( &t, "Mr.12345678901@example.com", format!("{header}\n\n100k text...").as_bytes(), false, - None, ) .await?; let msg = t.get_last_msg().await; @@ -369,185 +295,4 @@ mod tests { Ok(()) } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_partial_download_and_ephemeral() -> Result<()> { - let t = TestContext::new_alice().await; - let chat_id = t - .create_chat_with_contact("bob", "bob@example.org") - .await - .id; - chat_id - .set_ephemeral_timer(&t, Timer::Enabled { duration: 60 }) - .await?; - - // download message from bob partially, this must not change the ephemeral timer - receive_imf_from_inbox( - &t, - "first@example.org", - b"From: Bob \n\ - To: Alice \n\ - Chat-Version: 1.0\n\ - Subject: subject\n\ - Message-ID: \n\ - Date: Sun, 14 Nov 2021 00:10:00 +0000\ - Content-Type: text/plain", - false, - Some(100000), - ) - .await?; - assert_eq!( - chat_id.get_ephemeral_timer(&t).await?, - Timer::Enabled { duration: 60 } - ); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_status_update_expands_to_nothing() -> Result<()> { - let alice = TestContext::new_alice().await; - let bob = TestContext::new_bob().await; - let chat_id = alice.create_chat(&bob).await.id; - - let file = alice.get_blobdir().join("minimal.xdc"); - tokio::fs::write(&file, include_bytes!("../test-data/webxdc/minimal.xdc")).await?; - let mut instance = Message::new(Viewtype::File); - instance.set_file_and_deduplicate(&alice, &file, None, None)?; - let _sent1 = alice.send_msg(chat_id, &mut instance).await; - - alice - .send_webxdc_status_update(instance.id, r#"{"payload":7}"#) - .await?; - alice.flush_status_updates().await?; - let sent2 = alice.pop_sent_msg().await; - let sent2_rfc724_mid = sent2.load_from_db().await.rfc724_mid; - - // not downloading the status update results in an placeholder - receive_imf_from_inbox( - &bob, - &sent2_rfc724_mid, - sent2.payload().as_bytes(), - false, - Some(sent2.payload().len() as u32), - ) - .await?; - let msg = bob.get_last_msg().await; - let chat_id = msg.chat_id; - assert_eq!( - get_chat_msgs(&bob, chat_id).await?.len(), - E2EE_INFO_MSGS + 1 - ); - assert_eq!(msg.download_state(), DownloadState::Available); - - // downloading the status update afterwards expands to nothing and moves the placeholder to trash-chat - // (usually status updates are too small for not being downloaded directly) - receive_imf_from_inbox( - &bob, - &sent2_rfc724_mid, - sent2.payload().as_bytes(), - false, - None, - ) - .await?; - assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), E2EE_INFO_MSGS); - assert!( - Message::load_from_db_optional(&bob, msg.id) - .await? - .is_none() - ); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_mdn_expands_to_nothing() -> Result<()> { - let bob = TestContext::new_bob().await; - let raw = b"Subject: Message opened\n\ - Date: Mon, 10 Jan 2020 00:00:00 +0000\n\ - Chat-Version: 1.0\n\ - Message-ID: \n\ - To: Alice \n\ - From: Bob \n\ - Content-Type: multipart/report; report-type=disposition-notification;\n\t\ - boundary=\"kJBbU58X1xeWNHgBtTbMk80M5qnV4N\"\n\ - \n\ - \n\ - --kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\ - Content-Type: text/plain; charset=utf-8\n\ - \n\ - bla\n\ - \n\ - \n\ - --kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\ - Content-Type: message/disposition-notification\n\ - \n\ - Reporting-UA: Delta Chat 1.88.0\n\ - Original-Recipient: rfc822;bob@example.org\n\ - Final-Recipient: rfc822;bob@example.org\n\ - Original-Message-ID: \n\ - Disposition: manual-action/MDN-sent-automatically; displayed\n\ - \n\ - \n\ - --kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\ - "; - - // not downloading the mdn results in an placeholder - receive_imf_from_inbox(&bob, "bar@example.org", raw, false, Some(raw.len() as u32)).await?; - let msg = bob.get_last_msg().await; - let chat_id = msg.chat_id; - assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 1); - assert_eq!(msg.download_state(), DownloadState::Available); - - // downloading the mdn afterwards expands to nothing and deletes the placeholder directly - // (usually mdn are too small for not being downloaded directly) - receive_imf_from_inbox(&bob, "bar@example.org", raw, false, None).await?; - assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0); - assert!( - Message::load_from_db_optional(&bob, msg.id) - .await? - .is_none() - ); - - Ok(()) - } - - /// Tests that fully downloading the message - /// works even if the Message-ID already exists - /// in the database assigned to the trash chat. - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_partial_download_trashed() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - - let imf_raw = b"From: Bob \n\ - To: Alice \n\ - Chat-Version: 1.0\n\ - Subject: subject\n\ - Message-ID: \n\ - Date: Sun, 14 Nov 2021 00:10:00 +0000\ - Content-Type: text/plain"; - - // Download message from Bob partially. - let partial_received_msg = - receive_imf_from_inbox(alice, "first@example.org", imf_raw, false, Some(100000)) - .await? - .unwrap(); - assert_eq!(partial_received_msg.msg_ids.len(), 1); - - // Delete the received message. - // Not it is still in the database, - // but in the trash chat. - delete_msgs(alice, &[partial_received_msg.msg_ids[0]]).await?; - - // Fully download message after deletion. - let full_received_msg = - receive_imf_from_inbox(alice, "first@example.org", imf_raw, false, None).await?; - - // The message does not reappear. - // However, `receive_imf` should not fail. - assert!(full_received_msg.is_none()); - - Ok(()) - } } diff --git a/src/imap.rs b/src/imap.rs index 7826558afb..1258d944a0 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -67,7 +67,6 @@ const RFC724MID_UID: &str = "(UID BODY.PEEK[HEADER.FIELDS (\ X-MICROSOFT-ORIGINAL-MESSAGE-ID\ )])"; const BODY_FULL: &str = "(FLAGS BODY.PEEK[])"; -const BODY_PARTIAL: &str = "(FLAGS RFC822.SIZE BODY.PEEK[HEADER])"; #[derive(Debug)] pub(crate) struct Imap { @@ -597,8 +596,7 @@ impl Imap { .context("prefetch")?; let read_cnt = msgs.len(); - let download_limit = context.download_limit().await?; - let mut uids_fetch = Vec::<(u32, bool /* partially? */)>::with_capacity(msgs.len() + 1); + let mut uids_fetch = Vec::::with_capacity(msgs.len() + 1); let mut uid_message_ids = BTreeMap::new(); let mut largest_uid_skipped = None; let delete_target = context.get_delete_msgs_target().await?; @@ -681,13 +679,7 @@ impl Imap { ) .await.context("prefetch_should_download")? { - match download_limit { - Some(download_limit) => uids_fetch.push(( - uid, - fetch_response.size.unwrap_or_default() > download_limit, - )), - None => uids_fetch.push((uid, false)), - } + uids_fetch.push(uid); uid_message_ids.insert(uid, message_id); } else { largest_uid_skipped = Some(uid); @@ -722,29 +714,10 @@ impl Imap { }; let actually_download_messages_future = async { - let sender = sender; - let mut uids_fetch_in_batch = Vec::with_capacity(max(uids_fetch.len(), 1)); - let mut fetch_partially = false; - uids_fetch.push((0, !uids_fetch.last().unwrap_or(&(0, false)).1)); - for (uid, fp) in uids_fetch { - if fp != fetch_partially { - session - .fetch_many_msgs( - context, - folder, - uids_fetch_in_batch.split_off(0), - &uid_message_ids, - fetch_partially, - sender.clone(), - ) - .await - .context("fetch_many_msgs")?; - fetch_partially = fp; - } - uids_fetch_in_batch.push(uid); - } - - anyhow::Ok(()) + session + .fetch_many_msgs(context, folder, uids_fetch, &uid_message_ids, sender) + .await + .context("fetch_many_msgs") }; let (largest_uid_fetched, fetch_res) = @@ -1347,7 +1320,6 @@ impl Session { folder: &str, request_uids: Vec, uid_message_ids: &BTreeMap, - fetch_partially: bool, received_msgs_channel: Sender<(u32, Option)>, ) -> Result<()> { if request_uids.is_empty() { @@ -1355,25 +1327,10 @@ impl Session { } for (request_uids, set) in build_sequence_sets(&request_uids)? { - info!( - context, - "Starting a {} FETCH of message set \"{}\".", - if fetch_partially { "partial" } else { "full" }, - set - ); - let mut fetch_responses = self - .uid_fetch( - &set, - if fetch_partially { - BODY_PARTIAL - } else { - BODY_FULL - }, - ) - .await - .with_context(|| { - format!("fetching messages {} from folder \"{}\"", &set, folder) - })?; + info!(context, "Starting a full FETCH of message set \"{}\".", set); + let mut fetch_responses = self.uid_fetch(&set, BODY_FULL).await.with_context(|| { + format!("fetching messages {} from folder \"{}\"", &set, folder) + })?; // Map from UIDs to unprocessed FETCH results. We put unprocessed FETCH results here // when we want to process other messages first. @@ -1430,11 +1387,7 @@ impl Session { count += 1; let is_deleted = fetch_response.flags().any(|flag| flag == Flag::Deleted); - let (body, partial) = if fetch_partially { - (fetch_response.header(), fetch_response.size) // `BODY.PEEK[HEADER]` goes to header() ... - } else { - (fetch_response.body(), None) // ... while `BODY.PEEK[]` goes to body() - and includes header() - }; + let body = fetch_response.body(); if is_deleted { info!(context, "Not processing deleted msg {}.", request_uid); @@ -1468,7 +1421,7 @@ impl Session { context, "Passing message UID {} to receive_imf().", request_uid ); - let res = receive_imf_inner(context, rfc724_mid, body, is_seen, partial).await; + let res = receive_imf_inner(context, rfc724_mid, body, is_seen).await; let received_msg = match res { Err(err) => { warn!(context, "receive_imf error: {err:#}."); diff --git a/src/internals_for_benches.rs b/src/internals_for_benches.rs index 1fd80c5516..61fc9d7fca 100644 --- a/src/internals_for_benches.rs +++ b/src/internals_for_benches.rs @@ -21,7 +21,7 @@ pub async fn store_self_keypair(context: &Context, keypair: &KeyPair) -> Result< } pub async fn parse_and_get_text(context: &Context, imf_raw: &[u8]) -> Result { - let mime_parser = MimeMessage::from_bytes(context, imf_raw, None).await?; + let mime_parser = MimeMessage::from_bytes(context, imf_raw).await?; Ok(mime_parser.parts.into_iter().next().unwrap().msg) } diff --git a/src/message/message_tests.rs b/src/message/message_tests.rs index 267c7acbbd..08bcb96c6a 100644 --- a/src/message/message_tests.rs +++ b/src/message/message_tests.rs @@ -327,112 +327,6 @@ async fn test_markseen_msgs() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_markseen_not_downloaded_msg() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - alice.set_config(Config::DownloadLimit, Some("1")).await?; - let bob = &tcm.bob().await; - let bob_chat_id = bob.create_chat(alice).await.id; - alice.create_chat(bob).await; // Make sure the chat is accepted. - - tcm.section("Bob sends a large message to Alice"); - let file_bytes = include_bytes!("../../test-data/image/screenshot.png"); - let mut msg = Message::new(Viewtype::Image); - msg.set_file_from_bytes(bob, "a.jpg", file_bytes, None)?; - let sent_msg = bob.send_msg(bob_chat_id, &mut msg).await; - - tcm.section("Alice receives a large message from Bob"); - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Available); - assert!(!msg.param.get_bool(Param::WantsMdn).unwrap_or_default()); - assert_eq!(msg.state, MessageState::InFresh); - markseen_msgs(alice, vec![msg.id]).await?; - // A not downloaded message can be seen only if it's seen on another device. - assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed); - // Marking the message as seen again is a no op. - markseen_msgs(alice, vec![msg.id]).await?; - assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed); - - msg.id - .update_download_state(alice, DownloadState::InProgress) - .await?; - markseen_msgs(alice, vec![msg.id]).await?; - assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed); - msg.id - .update_download_state(alice, DownloadState::Failure) - .await?; - markseen_msgs(alice, vec![msg.id]).await?; - assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed); - msg.id - .update_download_state(alice, DownloadState::Undecipherable) - .await?; - markseen_msgs(alice, vec![msg.id]).await?; - assert_eq!(msg.id.get_state(alice).await?, MessageState::InNoticed); - - assert!( - !alice - .sql - .exists("SELECT COUNT(*) FROM smtp_mdns", ()) - .await? - ); - - alice.set_config(Config::DownloadLimit, None).await?; - // Let's assume that Alice and Bob resolved the problem with encryption. - let old_msg = msg; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.chat_id, old_msg.chat_id); - assert_eq!(msg.download_state, DownloadState::Done); - assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default()); - assert!(msg.get_showpadlock()); - // The message state mustn't be downgraded to `InFresh`. - assert_eq!(msg.state, MessageState::InNoticed); - markseen_msgs(alice, vec![msg.id]).await?; - let msg = Message::load_from_db(alice, msg.id).await?; - assert_eq!(msg.state, MessageState::InSeen); - assert_eq!( - alice - .sql - .count("SELECT COUNT(*) FROM smtp_mdns", ()) - .await?, - 1 - ); - Ok(()) -} - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_msg_seen_on_imap_when_downloaded() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - alice.set_config(Config::DownloadLimit, Some("1")).await?; - let bob = &tcm.bob().await; - let bob_chat_id = tcm.send_recv_accept(alice, bob, "hi").await.chat_id; - - let file_bytes = include_bytes!("../../test-data/image/screenshot.png"); - let mut msg = Message::new(Viewtype::Image); - msg.set_file_from_bytes(bob, "a.jpg", file_bytes, None)?; - let sent_msg = bob.send_msg(bob_chat_id, &mut msg).await; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Available); - assert_eq!(msg.state, MessageState::InFresh); - - alice.set_config(Config::DownloadLimit, None).await?; - let seen = true; - let rcvd_msg = receive_imf(alice, sent_msg.payload().as_bytes(), seen) - .await - .unwrap() - .unwrap(); - assert_eq!(rcvd_msg.chat_id, msg.chat_id); - let msg = Message::load_from_db(alice, *rcvd_msg.msg_ids.last().unwrap()) - .await - .unwrap(); - assert_eq!(msg.download_state, DownloadState::Done); - assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default()); - assert!(msg.get_showpadlock()); - assert_eq!(msg.state, MessageState::InSeen); - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_get_state() -> Result<()> { let alice = TestContext::new_alice().await; diff --git a/src/mimefactory/mimefactory_tests.rs b/src/mimefactory/mimefactory_tests.rs index 61cf0fec1e..f6a59c5ec5 100644 --- a/src/mimefactory/mimefactory_tests.rs +++ b/src/mimefactory/mimefactory_tests.rs @@ -558,7 +558,7 @@ async fn test_render_reply() { "1.0" ); - let _mime_msg = MimeMessage::from_bytes(context, rendered_msg.message.as_bytes(), None) + let _mime_msg = MimeMessage::from_bytes(context, rendered_msg.message.as_bytes()) .await .unwrap(); } @@ -824,7 +824,7 @@ async fn test_protected_headers_directive() -> Result<()> { assert!(msg.get_showpadlock()); assert!(sent.payload.contains("\r\nSubject: [...]\r\n")); - let mime = MimeMessage::from_bytes(&alice, sent.payload.as_bytes(), None).await?; + let mime = MimeMessage::from_bytes(&alice, sent.payload.as_bytes()).await?; let mut payload = str::from_utf8(&mime.decoded_data)?.splitn(2, "\r\n\r\n"); let part = payload.next().unwrap(); assert_eq!( @@ -854,7 +854,7 @@ async fn test_dont_remove_self() -> Result<()> { .await; println!("{}", sent.payload); - let mime_message = MimeMessage::from_bytes(alice, sent.payload.as_bytes(), None) + let mime_message = MimeMessage::from_bytes(alice, sent.payload.as_bytes()) .await .unwrap(); assert!(!mime_message.header_exists(HeaderDef::ChatGroupPastMembers)); diff --git a/src/mimeparser.rs b/src/mimeparser.rs index eeb884ecbc..3698db2932 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -239,13 +239,7 @@ const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup"; impl MimeMessage { /// Parse a mime message. - /// - /// If `partial` is set, it contains the full message size in bytes. - pub(crate) async fn from_bytes( - context: &Context, - body: &[u8], - partial: Option, - ) -> Result { + pub(crate) async fn from_bytes(context: &Context, body: &[u8]) -> Result { let mail = mailparse::parse_mail(body)?; let timestamp_rcvd = smeared_time(context); @@ -303,7 +297,7 @@ impl MimeMessage { ); (part, part.ctype.mimetype.parse::()?) } else { - // If it's a partially fetched message, there are no subparts. + // Not a valid signed message, handle it as plaintext. (&mail, mimetype) } } else { @@ -617,31 +611,24 @@ impl MimeMessage { timestamp_sent, }; - match partial { - Some(org_bytes) => { - parser - .create_stub_from_partial_download(context, org_bytes) - .await?; + match mail { + Ok(mail) => { + parser.parse_mime_recursive(context, mail, false).await?; + } + Err(err) => { + let txt = "[This message cannot be decrypted.\n\n• It might already help to simply reply to this message and ask the sender to send the message again.\n\n• If you just re-installed Delta Chat then it is best if you re-setup Delta Chat now and choose \"Add as second device\" or import a backup.]"; + + let part = Part { + typ: Viewtype::Text, + msg_raw: Some(txt.to_string()), + msg: txt.to_string(), + // Don't change the error prefix for now, + // receive_imf.rs:lookup_chat_by_reply() checks it. + error: Some(format!("Decrypting failed: {err:#}")), + ..Default::default() + }; + parser.do_add_single_part(part); } - None => match mail { - Ok(mail) => { - parser.parse_mime_recursive(context, mail, false).await?; - } - Err(err) => { - let txt = "[This message cannot be decrypted.\n\n• It might already help to simply reply to this message and ask the sender to send the message again.\n\n• If you just re-installed Delta Chat then it is best if you re-setup Delta Chat now and choose \"Add as second device\" or import a backup.]"; - - let part = Part { - typ: Viewtype::Text, - msg_raw: Some(txt.to_string()), - msg: txt.to_string(), - // Don't change the error prefix for now, - // receive_imf.rs:lookup_chat_by_reply() checks it. - error: Some(format!("Decrypting failed: {err:#}")), - ..Default::default() - }; - parser.do_add_single_part(part); - } - }, }; let is_location_only = parser.location_kml.is_some() && parser.parts.is_empty(); diff --git a/src/mimeparser/mimeparser_tests.rs b/src/mimeparser/mimeparser_tests.rs index 19060f37c4..ac4ac0b151 100644 --- a/src/mimeparser/mimeparser_tests.rs +++ b/src/mimeparser/mimeparser_tests.rs @@ -25,58 +25,54 @@ impl AvatarAction { async fn test_mimeparser_fromheader() { let ctx = TestContext::new_alice().await; - let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de\n\nhi", None) + let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de\n\nhi") .await .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, None); - let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de \n\nhi", None) + let mimemsg = MimeMessage::from_bytes(&ctx, b"From: g@c.de \n\nhi") .await .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, None); - let mimemsg = MimeMessage::from_bytes(&ctx, b"From: \n\nhi", None) + let mimemsg = MimeMessage::from_bytes(&ctx, b"From: \n\nhi") .await .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, None); - let mimemsg = MimeMessage::from_bytes(&ctx, b"From: Goetz C \n\nhi", None) + let mimemsg = MimeMessage::from_bytes(&ctx, b"From: Goetz C \n\nhi") .await .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, Some("Goetz C".to_string())); - let mimemsg = MimeMessage::from_bytes(&ctx, b"From: \"Goetz C\" \n\nhi", None) + let mimemsg = MimeMessage::from_bytes(&ctx, b"From: \"Goetz C\" \n\nhi") .await .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, Some("Goetz C".to_string())); - let mimemsg = - MimeMessage::from_bytes(&ctx, b"From: =?utf-8?q?G=C3=B6tz?= C \n\nhi", None) - .await - .unwrap(); + let mimemsg = MimeMessage::from_bytes(&ctx, b"From: =?utf-8?q?G=C3=B6tz?= C \n\nhi") + .await + .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, Some("Götz C".to_string())); // although RFC 2047 says, encoded-words shall not appear inside quoted-string, // this combination is used in the wild eg. by MailMate - let mimemsg = MimeMessage::from_bytes( - &ctx, - b"From: \"=?utf-8?q?G=C3=B6tz?= C\" \n\nhi", - None, - ) - .await - .unwrap(); + let mimemsg = + MimeMessage::from_bytes(&ctx, b"From: \"=?utf-8?q?G=C3=B6tz?= C\" \n\nhi") + .await + .unwrap(); let contact = mimemsg.from; assert_eq!(contact.addr, "g@c.de"); assert_eq!(contact.display_name, Some("Götz C".to_string())); @@ -86,7 +82,7 @@ async fn test_mimeparser_fromheader() { async fn test_mimeparser_crash() { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/issue_523.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); @@ -98,7 +94,7 @@ async fn test_mimeparser_crash() { async fn test_get_rfc724_mid_exists() { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/mail_with_message_id.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); @@ -112,7 +108,7 @@ async fn test_get_rfc724_mid_exists() { async fn test_get_rfc724_mid_not_exists() { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/issue_523.txt"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(mimeparser.get_rfc724_mid(), None); @@ -324,7 +320,7 @@ async fn test_mailparse_0_16_0_panic() { // There should be an error, but no panic. assert!( - MimeMessage::from_bytes(&context.ctx, &raw[..], None) + MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .is_err() ); @@ -341,7 +337,7 @@ async fn test_parse_first_addr() { test1\n\ "; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None).await; + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).await; assert!(mimeparser.is_err()); } @@ -356,7 +352,7 @@ async fn test_get_parent_timestamp() { \n\ Some reply\n\ "; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -402,7 +398,7 @@ async fn test_mimeparser_with_context() { --==break==--\n\ \n"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); @@ -438,26 +434,26 @@ async fn test_mimeparser_with_avatars() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/mail_attach_txt.eml"); - let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap(); assert_eq!(mimeparser.user_avatar, None); assert_eq!(mimeparser.group_avatar, None); let raw = include_bytes!("../../test-data/message/mail_with_user_avatar.eml"); - let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert!(mimeparser.user_avatar.unwrap().is_change()); assert_eq!(mimeparser.group_avatar, None); let raw = include_bytes!("../../test-data/message/mail_with_user_avatar_deleted.eml"); - let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert_eq!(mimeparser.user_avatar, Some(AvatarAction::Delete)); assert_eq!(mimeparser.group_avatar, None); let raw = include_bytes!("../../test-data/message/mail_with_user_and_group_avatars.eml"); - let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert!(mimeparser.user_avatar.unwrap().is_change()); @@ -467,9 +463,7 @@ async fn test_mimeparser_with_avatars() { let raw = include_bytes!("../../test-data/message/mail_with_user_and_group_avatars.eml"); let raw = String::from_utf8_lossy(raw).to_string(); let raw = raw.replace("Chat-User-Avatar:", "Xhat-Xser-Xvatar:"); - let mimeparser = MimeMessage::from_bytes(&t, raw.as_bytes(), None) - .await - .unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, raw.as_bytes()).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Image); assert_eq!(mimeparser.user_avatar, None); @@ -485,7 +479,7 @@ async fn test_mimeparser_with_videochat() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/videochat_invitation.eml"); - let mimeparser = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts[0].typ, Viewtype::Text); assert_eq!(mimeparser.parts[0].param.get(Param::WebrtcRoom), None); @@ -528,7 +522,7 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\ --==break==--\n\ ;"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -578,7 +572,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\ --kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\ "; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -659,7 +653,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\ --outer--\n\ "; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -706,7 +700,7 @@ Additional-Message-IDs: \n\ --kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\ "; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -753,7 +747,7 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg== ------=_Part_25_46172632.1581201680436-- "#; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -797,7 +791,7 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg== ------=_Part_25_46172632.1581201680436-- "#; - let message = MimeMessage::from_bytes(&t, &raw[..], None).await.unwrap(); + let message = MimeMessage::from_bytes(&t, &raw[..]).await.unwrap(); assert_eq!(message.parts.len(), 1); assert_eq!(message.parts[0].typ, Viewtype::File); @@ -839,7 +833,7 @@ iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAAAAABoYUP1AAAAAXNSR0IArs4c6QAAAo1JREFUKJFdkdFu ----11019878869865180-- "#; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(message.get_subject(), Some("example".to_string())); @@ -903,7 +897,7 @@ iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAAAAABoYUP1AAAAAXNSR0IArs4c6QAAAo1JREFUKJFdkdFu --------------779C1631600DF3DB8C02E53A--"#; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(message.get_subject(), Some("Test subject".to_string())); @@ -966,7 +960,7 @@ iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAAAAABoYUP1AAAAAXNSR0IArs4c6QAAAo1JREFUKJFdkdFu ------=_NextPart_000_0003_01D622B3.CA753E60-- "#; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -1064,7 +1058,7 @@ From: alice Reply "##; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -1096,7 +1090,7 @@ From: alice > Just a quote. "##; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -1130,7 +1124,7 @@ On 2020-10-25, Bob wrote: > A quote. "##; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(message.get_subject(), Some("Re: top posting".to_string())); @@ -1148,7 +1142,7 @@ On 2020-10-25, Bob wrote: async fn test_attachment_quote() { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/quote_attach.eml"); - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); @@ -1166,7 +1160,7 @@ async fn test_attachment_quote() { async fn test_quote_div() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/gmx-quote.eml"); - let mimeparser = MimeMessage::from_bytes(&t, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, raw).await.unwrap(); assert_eq!(mimeparser.parts[0].msg, "YIPPEEEEEE\n\nMulti-line"); assert_eq!(mimeparser.parts[0].param.get(Param::Quote).unwrap(), "Now?"); } @@ -1176,7 +1170,7 @@ async fn test_allinkl_blockquote() { // all-inkl.com puts quotes into `
`. let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/allinkl-quote.eml"); - let mimeparser = MimeMessage::from_bytes(&t, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t, raw).await.unwrap(); assert!(mimeparser.parts[0].msg.starts_with("It's 1.0.")); assert_eq!( mimeparser.parts[0].param.get(Param::Quote).unwrap(), @@ -1217,7 +1211,7 @@ async fn test_add_subj_to_multimedia_msg() { async fn test_mime_modified_plain() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/text_plain_unspecified.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap(); assert!(!mimeparser.is_mime_modified); assert_eq!( mimeparser.parts[0].msg, @@ -1229,7 +1223,7 @@ async fn test_mime_modified_plain() { async fn test_mime_modified_alt_plain_html() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/text_alt_plain_html.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap(); assert!(mimeparser.is_mime_modified); assert_eq!( mimeparser.parts[0].msg, @@ -1241,7 +1235,7 @@ async fn test_mime_modified_alt_plain_html() { async fn test_mime_modified_alt_plain() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/text_alt_plain.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap(); assert!(!mimeparser.is_mime_modified); assert_eq!( mimeparser.parts[0].msg, @@ -1256,7 +1250,7 @@ async fn test_mime_modified_alt_plain() { async fn test_mime_modified_alt_html() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/text_alt_html.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap(); assert!(mimeparser.is_mime_modified); assert_eq!( mimeparser.parts[0].msg, @@ -1268,7 +1262,7 @@ async fn test_mime_modified_alt_html() { async fn test_mime_modified_html() { let t = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/text_html.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, raw, None).await.unwrap(); + let mimeparser = MimeMessage::from_bytes(&t.ctx, raw).await.unwrap(); assert!(mimeparser.is_mime_modified); assert_eq!( mimeparser.parts[0].msg, @@ -1288,7 +1282,7 @@ async fn test_mime_modified_large_plain() -> Result<()> { assert!(long_txt.len() > DC_DESIRED_TEXT_LEN); { - let mimemsg = MimeMessage::from_bytes(&t, long_txt.as_ref(), None).await?; + let mimemsg = MimeMessage::from_bytes(&t, long_txt.as_ref()).await?; assert!(mimemsg.is_mime_modified); assert!( mimemsg.parts[0].msg.matches("just repeated").count() @@ -1321,7 +1315,7 @@ async fn test_mime_modified_large_plain() -> Result<()> { t.set_config(Config::Bot, Some("1")).await?; { - let mimemsg = MimeMessage::from_bytes(&t, long_txt.as_ref(), None).await?; + let mimemsg = MimeMessage::from_bytes(&t, long_txt.as_ref()).await?; assert!(!mimemsg.is_mime_modified); assert_eq!( format!("{}\n", mimemsg.parts[0].msg), @@ -1368,7 +1362,7 @@ async fn test_x_microsoft_original_message_id() { MIME-Version: 1.0\n\ \n\ Does it work with outlook now?\n\ - ", None) + ") .await .unwrap(); assert_eq!( @@ -1412,7 +1406,7 @@ async fn test_extra_imf_chat_header() -> Result<()> { let payload = sent_msg .payload .replace("Message-ID:", "Chat-Forty-Two: 42\r\nMessage-ID:"); - let msg = MimeMessage::from_bytes(t, payload.as_bytes(), None) + let msg = MimeMessage::from_bytes(t, payload.as_bytes()) .await .unwrap(); assert!(msg.headers.contains_key("chat-version")); @@ -1576,7 +1570,7 @@ async fn test_ms_exchange_mdn() -> Result<()> { // 1. Test mimeparser directly let mdn = include_bytes!("../../test-data/message/ms_exchange_report_disposition_notification.eml"); - let mimeparser = MimeMessage::from_bytes(&t.ctx, mdn, None).await?; + let mimeparser = MimeMessage::from_bytes(&t.ctx, mdn).await?; assert_eq!(mimeparser.mdn_reports.len(), 1); assert_eq!( mimeparser.mdn_reports[0].original_message_id.as_deref(), @@ -1602,7 +1596,6 @@ async fn test_receive_eml() -> Result<()> { let mime_message = MimeMessage::from_bytes( &alice, include_bytes!("../../test-data/message/attached-eml.eml"), - None, ) .await?; @@ -1645,7 +1638,6 @@ Content-Disposition: reaction\n\ \n\ \u{1F44D}" .as_bytes(), - None, ) .await?; @@ -1667,7 +1659,7 @@ async fn test_jpeg_as_application_octet_stream() -> Result<()> { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/jpeg-as-application-octet-stream.eml"); - let msg = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let msg = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(msg.parts.len(), 1); @@ -1685,7 +1677,7 @@ async fn test_schleuder() -> Result<()> { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/schleuder.eml"); - let msg = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let msg = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(msg.parts.len(), 2); @@ -1705,7 +1697,7 @@ async fn test_tlsrpt() -> Result<()> { let context = TestContext::new_alice().await; let raw = include_bytes!("../../test-data/message/tlsrpt.eml"); - let msg = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let msg = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(msg.parts.len(), 1); @@ -1738,7 +1730,6 @@ async fn test_time_in_future() -> Result<()> { Content-Type: text/plain; charset=utf-8\n\ \n\ Hi", - None, ) .await?; @@ -1767,7 +1758,7 @@ Content-Type: text/plain; charset=utf-8 /help "#; - let message = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let message = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!(message.get_subject(), Some("Some subject".to_string())); @@ -1808,7 +1799,7 @@ async fn test_take_last_header() { Hello\n\ "; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) .await .unwrap(); assert_eq!( @@ -1861,9 +1852,7 @@ It DOES end with a linebreak.\r \r This is the epilogue. It is also to be ignored."; - let mimeparser = MimeMessage::from_bytes(&context, &raw[..], None) - .await - .unwrap(); + let mimeparser = MimeMessage::from_bytes(&context, &raw[..]).await.unwrap(); assert_eq!(mimeparser.parts.len(), 2); @@ -1909,7 +1898,7 @@ Message with a correct Message-ID hidden header --luTiGu6GBoVLCvTkzVtmZmwsmhkNMw-- "#; - let message = MimeMessage::from_bytes(t, &raw[..], None).await.unwrap(); + let message = MimeMessage::from_bytes(t, &raw[..]).await.unwrap(); assert_eq!(message.get_rfc724_mid().unwrap(), "foo@example.org"); } @@ -2087,9 +2076,7 @@ Third alternative. --boundary-- "#; - let message = MimeMessage::from_bytes(context, &raw[..], None) - .await - .unwrap(); + let message = MimeMessage::from_bytes(context, &raw[..]).await.unwrap(); assert_eq!(message.parts.len(), 1); assert_eq!(message.parts[0].typ, Viewtype::Text); assert_eq!(message.parts[0].msg, "Third alternative."); diff --git a/src/reaction.rs b/src/reaction.rs index e691b2ddeb..e48bdfe859 100644 --- a/src/reaction.rs +++ b/src/reaction.rs @@ -393,9 +393,8 @@ mod tests { use crate::chatlist::Chatlist; use crate::config::Config; use crate::contact::{Contact, Origin}; - use crate::download::DownloadState; use crate::message::{MessageState, Viewtype, delete_msgs}; - use crate::receive_imf::{receive_imf, receive_imf_from_inbox}; + use crate::receive_imf::receive_imf; use crate::sql::housekeeping; use crate::test_utils::E2EE_INFO_MSGS; use crate::test_utils::TestContext; @@ -925,73 +924,6 @@ Content-Disposition: reaction\n\ Ok(()) } - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_partial_download_and_reaction() -> Result<()> { - let alice = TestContext::new_alice().await; - let bob = TestContext::new_bob().await; - - alice - .create_chat_with_contact("Bob", "bob@example.net") - .await; - - let msg_header = "From: Bob \n\ - To: Alice \n\ - Chat-Version: 1.0\n\ - Subject: subject\n\ - Message-ID: \n\ - Date: Sun, 14 Nov 2021 00:10:00 +0000\ - Content-Type: text/plain"; - let msg_full = format!("{msg_header}\n\n100k text..."); - - // Alice downloads message from Bob partially. - let alice_received_message = receive_imf_from_inbox( - &alice, - "first@example.org", - msg_header.as_bytes(), - false, - Some(100000), - ) - .await? - .unwrap(); - let alice_msg_id = *alice_received_message.msg_ids.first().unwrap(); - - // Bob downloads own message on the other device. - let bob_received_message = receive_imf(&bob, msg_full.as_bytes(), false) - .await? - .unwrap(); - let bob_msg_id = *bob_received_message.msg_ids.first().unwrap(); - - // Bob reacts to own message. - send_reaction(&bob, bob_msg_id, "👍").await.unwrap(); - let bob_reaction_msg = bob.pop_sent_msg().await; - - // Alice receives a reaction. - alice.recv_msg_hidden(&bob_reaction_msg).await; - - let reactions = get_msg_reactions(&alice, alice_msg_id).await?; - assert_eq!(reactions.to_string(), "👍1"); - let msg = Message::load_from_db(&alice, alice_msg_id).await?; - assert_eq!(msg.download_state(), DownloadState::Available); - - // Alice downloads full message. - receive_imf_from_inbox( - &alice, - "first@example.org", - msg_full.as_bytes(), - false, - None, - ) - .await?; - - // Check that reaction is still on the message after full download. - let msg = Message::load_from_db(&alice, alice_msg_id).await?; - assert_eq!(msg.download_state(), DownloadState::Done); - let reactions = get_msg_reactions(&alice, alice_msg_id).await?; - assert_eq!(reactions.to_string(), "👍1"); - - Ok(()) - } - /// Regression test for reaction resetting self-status. /// /// Reactions do not contain the status, diff --git a/src/receive_imf.rs b/src/receive_imf.rs index c0f8dfc102..460736d4fe 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -155,24 +155,7 @@ pub async fn receive_imf( let mail = mailparse::parse_mail(imf_raw).context("can't parse mail")?; let rfc724_mid = crate::imap::prefetch_get_message_id(&mail.headers) .unwrap_or_else(crate::imap::create_message_id); - if let Some(download_limit) = context.download_limit().await? { - let download_limit: usize = download_limit.try_into()?; - if imf_raw.len() > download_limit { - let head = std::str::from_utf8(imf_raw)? - .split("\r\n\r\n") - .next() - .context("No empty line in the message")?; - return receive_imf_from_inbox( - context, - &rfc724_mid, - head.as_bytes(), - seen, - Some(imf_raw.len().try_into()?), - ) - .await; - } - } - receive_imf_from_inbox(context, &rfc724_mid, imf_raw, seen, None).await + receive_imf_from_inbox(context, &rfc724_mid, imf_raw, seen).await } /// Emulates reception of a message from "INBOX". @@ -184,9 +167,8 @@ pub(crate) async fn receive_imf_from_inbox( rfc724_mid: &str, imf_raw: &[u8], seen: bool, - is_partial_download: Option, ) -> Result> { - receive_imf_inner(context, rfc724_mid, imf_raw, seen, is_partial_download).await + receive_imf_inner(context, rfc724_mid, imf_raw, seen).await } /// Inserts a tombstone into `msgs` table @@ -209,7 +191,6 @@ async fn get_to_and_past_contact_ids( context: &Context, mime_parser: &MimeMessage, chat_assignment: &ChatAssignment, - is_partial_download: Option, parent_message: &Option, incoming_origin: Origin, ) -> Result<(Vec>, Vec>)> { @@ -252,7 +233,7 @@ async fn get_to_and_past_contact_ids( ChatAssignment::ExistingChat { chat_id, .. } => Some(*chat_id), ChatAssignment::MailingListOrBroadcast => None, ChatAssignment::OneOneChat => { - if is_partial_download.is_none() && !mime_parser.incoming { + if !mime_parser.incoming { parent_message.as_ref().map(|m| m.chat_id) } else { None @@ -482,15 +463,17 @@ async fn get_to_and_past_contact_ids( /// downloaded again, sets `chat_id=DC_CHAT_ID_TRASH` and returns `Ok(Some(…))`. /// If the message is so wrong that we didn't even create a database entry, /// returns `Ok(None)`. -/// -/// If `is_partial_download` is set, it contains the full message size in bytes. pub(crate) async fn receive_imf_inner( context: &Context, rfc724_mid: &str, imf_raw: &[u8], seen: bool, - is_partial_download: Option, ) -> Result> { + ensure!( + !context + .get_config_bool(Config::SimulateReceiveImfError) + .await? + ); if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { info!( context, @@ -498,16 +481,8 @@ pub(crate) async fn receive_imf_inner( String::from_utf8_lossy(imf_raw), ); } - if is_partial_download.is_none() { - ensure!( - !context - .get_config_bool(Config::FailOnReceivingFullMsg) - .await? - ); - } - let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw, is_partial_download).await - { + let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw).await { Err(err) => { warn!(context, "receive_imf: can't parse MIME: {err:#}."); if rfc724_mid.starts_with(GENERATED_PREFIX) { @@ -549,6 +524,8 @@ pub(crate) async fn receive_imf_inner( // the message was partially downloaded before and is fully downloaded now. info!(context, "Message already partly in DB, replacing."); Some(msg.chat_id) + + // TODO: look at this place } else { // The message was already fully downloaded // or cannot be loaded because it is deleted. @@ -613,11 +590,7 @@ pub(crate) async fn receive_imf_inner( &mime_parser.from, fingerprint, prevent_rename, - is_partial_download.is_some() - && mime_parser - .get_header(HeaderDef::ContentType) - .unwrap_or_default() - .starts_with("multipart/encrypted"), + false, ) .await? { @@ -649,22 +622,14 @@ pub(crate) async fn receive_imf_inner( .await? .filter(|p| Some(p.id) != replace_msg_id); - let chat_assignment = decide_chat_assignment( - context, - &mime_parser, - &parent_message, - rfc724_mid, - from_id, - &is_partial_download, - ) - .await?; + let chat_assignment = + decide_chat_assignment(context, &mime_parser, &parent_message, rfc724_mid, from_id).await?; info!(context, "Chat assignment is {chat_assignment:?}."); let (to_ids, past_ids) = get_to_and_past_contact_ids( context, &mime_parser, &chat_assignment, - is_partial_download, &parent_message, incoming_origin, ) @@ -761,7 +726,6 @@ pub(crate) async fn receive_imf_inner( to_id, allow_creation, &mut mime_parser, - is_partial_download, parent_message, ) .await?; @@ -777,7 +741,6 @@ pub(crate) async fn receive_imf_inner( rfc724_mid_orig, from_id, seen, - is_partial_download, replace_msg_id, prevent_rename, chat_id, @@ -937,9 +900,7 @@ pub(crate) async fn receive_imf_inner( let delete_server_after = context.get_config_delete_server_after().await?; if !received_msg.msg_ids.is_empty() { - let target = if received_msg.needs_delete_job - || (delete_server_after == Some(0) && is_partial_download.is_none()) - { + let target = if received_msg.needs_delete_job || delete_server_after == Some(0) { Some(context.get_delete_msgs_target().await?) } else { None @@ -968,7 +929,7 @@ pub(crate) async fn receive_imf_inner( } } - if is_partial_download.is_none() && mime_parser.is_call() { + if mime_parser.is_call() { context .handle_call_msg(insert_msg_id, &mime_parser, from_id) .await?; @@ -1017,7 +978,7 @@ pub(crate) async fn receive_imf_inner( /// * `find_key_contact_by_addr`: if true, we only know the e-mail address /// of the contact, but not the fingerprint, /// yet want to assign the message to some key-contact. -/// This can happen during prefetch or when the message is partially downloaded. +/// This can happen during prefetch. /// If we get it wrong, the message will be placed into the correct /// chat after downloading. /// @@ -1111,7 +1072,6 @@ async fn decide_chat_assignment( parent_message: &Option, rfc724_mid: &str, from_id: ContactId, - is_partial_download: &Option, ) -> Result { let should_trash = if !mime_parser.mdn_reports.is_empty() { info!(context, "Message is an MDN (TRASH)."); @@ -1127,9 +1087,8 @@ async fn decide_chat_assignment( { info!(context, "Chat edit/delete/iroh/sync message (TRASH)."); true - } else if is_partial_download.is_none() - && (mime_parser.is_system_message == SystemMessage::CallAccepted - || mime_parser.is_system_message == SystemMessage::CallEnded) + } else if mime_parser.is_system_message == SystemMessage::CallAccepted + || mime_parser.is_system_message == SystemMessage::CallEnded { info!(context, "Call state changed (TRASH)."); true @@ -1230,7 +1189,7 @@ async fn decide_chat_assignment( } } else if let Some(parent) = &parent_message { if let Some((chat_id, chat_id_blocked)) = - lookup_chat_by_reply(context, mime_parser, parent, is_partial_download).await? + lookup_chat_by_reply(context, mime_parser, parent).await? { // Try to assign to a chat based on In-Reply-To/References. ChatAssignment::ExistingChat { @@ -1252,7 +1211,7 @@ async fn decide_chat_assignment( } } else if let Some(parent) = &parent_message { if let Some((chat_id, chat_id_blocked)) = - lookup_chat_by_reply(context, mime_parser, parent, is_partial_download).await? + lookup_chat_by_reply(context, mime_parser, parent).await? { // Try to assign to a chat based on In-Reply-To/References. ChatAssignment::ExistingChat { @@ -1294,7 +1253,6 @@ async fn do_chat_assignment( to_id: ContactId, allow_creation: bool, mime_parser: &mut MimeMessage, - is_partial_download: Option, parent_message: Option, ) -> Result<(ChatId, Blocked, bool)> { let is_bot = context.get_config_bool(Config::Bot).await?; @@ -1345,7 +1303,6 @@ async fn do_chat_assignment( if let Some((new_chat_id, new_chat_id_blocked)) = create_group( context, mime_parser, - is_partial_download.is_some(), create_blocked, from_id, to_ids, @@ -1396,7 +1353,6 @@ async fn do_chat_assignment( to_ids, allow_creation || test_normal_chat.is_some(), create_blocked, - is_partial_download.is_some(), ) .await? { @@ -1480,7 +1436,6 @@ async fn do_chat_assignment( if let Some((new_chat_id, new_chat_id_blocked)) = create_group( context, mime_parser, - is_partial_download.is_some(), Blocked::Not, from_id, to_ids, @@ -1545,7 +1500,6 @@ async fn do_chat_assignment( to_ids, allow_creation, Blocked::Not, - is_partial_download.is_some(), ) .await? { @@ -1625,7 +1579,6 @@ async fn add_parts( rfc724_mid: &str, from_id: ContactId, seen: bool, - is_partial_download: Option, mut replace_msg_id: Option, prevent_rename: bool, mut chat_id: ChatId, @@ -1699,10 +1652,9 @@ async fn add_parts( .get_rfc724_mid() .unwrap_or(rfc724_mid.to_string()); - // Extract ephemeral timer from the message or use the existing timer if the message is not fully downloaded. - let mut ephemeral_timer = if is_partial_download.is_some() { - chat_id.get_ephemeral_timer(context).await? - } else if let Some(value) = mime_parser.get_header(HeaderDef::EphemeralTimer) { + // Extract ephemeral timer from the message + let mut ephemeral_timer = if let Some(value) = mime_parser.get_header(HeaderDef::EphemeralTimer) + { match value.parse::() { Ok(timer) => timer, Err(err) => { @@ -1900,7 +1852,6 @@ async fn add_parts( let chat_id = if better_msg .as_ref() .is_some_and(|better_msg| better_msg.is_empty()) - && is_partial_download.is_none() { DC_CHAT_ID_TRASH } else { @@ -1950,9 +1901,8 @@ async fn add_parts( handle_edit_delete(context, mime_parser, from_id).await?; - if is_partial_download.is_none() - && (mime_parser.is_system_message == SystemMessage::CallAccepted - || mime_parser.is_system_message == SystemMessage::CallEnded) + if mime_parser.is_system_message == SystemMessage::CallAccepted + || mime_parser.is_system_message == SystemMessage::CallEnded { if let Some(field) = mime_parser.get_header(HeaderDef::InReplyTo) { if let Some(call) = @@ -2109,8 +2059,6 @@ RETURNING id if trash { 0 } else { ephemeral_timestamp }, if trash { DownloadState::Done - } else if is_partial_download.is_some() { - DownloadState::Available } else if mime_parser.decrypting_failed { DownloadState::Undecipherable } else { @@ -2395,7 +2343,6 @@ async fn lookup_chat_by_reply( context: &Context, mime_parser: &MimeMessage, parent: &Message, - is_partial_download: &Option, ) -> Result> { // If the message is encrypted and has group ID, // lookup by reply should never be needed @@ -2427,10 +2374,7 @@ async fn lookup_chat_by_reply( } // Do not assign unencrypted messages to encrypted chats. - if is_partial_download.is_none() - && parent_chat.is_encrypted(context).await? - && !mime_parser.was_encrypted() - { + if parent_chat.is_encrypted(context).await? && !mime_parser.was_encrypted() { return Ok(None); } @@ -2447,18 +2391,7 @@ async fn lookup_or_create_adhoc_group( to_ids: &[Option], allow_creation: bool, create_blocked: Blocked, - is_partial_download: bool, ) -> Result> { - // Partial download may be an encrypted message with protected Subject header. We do not want to - // create a group with "..." or "Encrypted message" as a subject. The same is for undecipherable - // messages. Instead, assign the message to 1:1 chat with the sender. - if is_partial_download { - info!( - context, - "Ad-hoc group cannot be created from partial download." - ); - return Ok(None); - } if mime_parser.decrypting_failed { warn!( context, @@ -2594,11 +2527,9 @@ async fn is_probably_private_reply( /// than two members, a new ad hoc group is created. /// /// On success the function returns the created (chat_id, chat_blocked) tuple. -#[expect(clippy::too_many_arguments)] async fn create_group( context: &Context, mime_parser: &mut MimeMessage, - is_partial_download: bool, create_blocked: Blocked, from_id: ContactId, to_ids: &[Option], @@ -2680,7 +2611,7 @@ async fn create_group( if let Some(chat_id) = chat_id { Ok(Some((chat_id, chat_id_blocked))) - } else if is_partial_download || mime_parser.decrypting_failed { + } else if mime_parser.decrypting_failed { // It is possible that the message was sent to a valid, // yet unknown group, which was rejected because // Chat-Group-Name, which is in the encrypted part, was diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index 4e179bde04..97fbc0a3b1 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -10,7 +10,6 @@ use crate::chat::{ use crate::chatlist::Chatlist; use crate::constants::DC_GCL_FOR_FORWARDING; use crate::contact; -use crate::download::MIN_DOWNLOAD_LIMIT; use crate::imap::prefetch_should_download; use crate::imex::{ImexMode, imex}; use crate::securejoin::get_securejoin_qr; @@ -19,8 +18,6 @@ use crate::test_utils::{ }; use crate::tools::{SystemTime, time}; -use rand::distr::SampleString; - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_outgoing() -> Result<()> { let context = TestContext::new_alice().await; @@ -28,7 +25,7 @@ async fn test_outgoing() -> Result<()> { From: alice@example.org\n\ \n\ hello"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None).await?; + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).await?; assert_eq!(mimeparser.incoming, false); Ok(()) } @@ -43,7 +40,7 @@ async fn test_bad_from() { References: \n\ \n\ hello\x00"; - let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..], None).await; + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).await; assert!(mimeparser.is_err()); } @@ -2817,7 +2814,7 @@ References: Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no Message with references."#; - let mime_parser = MimeMessage::from_bytes(&t, &mime[..], None).await?; + let mime_parser = MimeMessage::from_bytes(&t, &mime[..]).await?; let parent = get_parent_message(&t, &mime_parser).await?.unwrap(); assert_eq!(parent.id, first.id); @@ -4360,37 +4357,6 @@ async fn test_adhoc_grp_name_no_prefix() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_download_later() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = tcm.alice().await; - alice.set_config(Config::DownloadLimit, Some("1")).await?; - assert_eq!(alice.download_limit().await?, Some(MIN_DOWNLOAD_LIMIT)); - - let bob = tcm.bob().await; - let bob_chat = bob.create_chat(&alice).await; - - // Generate a random string so OpenPGP does not compress it. - let text = - rand::distr::Alphanumeric.sample_string(&mut rand::rng(), MIN_DOWNLOAD_LIMIT as usize); - - let sent_msg = bob.send_text(bob_chat.id, &text).await; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Available); - assert_eq!(msg.state, MessageState::InFresh); - - let hi_msg = tcm.send_recv(&bob, &alice, "hi").await; - - alice.set_config(Config::DownloadLimit, None).await?; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Done); - assert_eq!(msg.state, MessageState::InFresh); - assert_eq!(alice.get_last_msg_in(msg.chat_id).await.id, hi_msg.id); - assert!(msg.timestamp_sort <= hi_msg.timestamp_sort); - - Ok(()) -} - /// Malice can pretend they have the same address as Alice and sends a message encrypted to Alice's /// key but signed with another one. Alice must detect that this message is wrongly signed and not /// treat it as Autocrypt-encrypted. @@ -4425,162 +4391,6 @@ async fn test_outgoing_msg_forgery() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_create_group_with_big_msg() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = tcm.alice().await; - let bob = tcm.bob().await; - let ba_contact = bob.add_or_lookup_contact_id(&alice).await; - let ab_chat_id = alice.create_chat(&bob).await.id; - - let file_bytes = include_bytes!("../../test-data/image/screenshot.png"); - - let bob_grp_id = create_group(&bob, "Group").await?; - add_contact_to_chat(&bob, bob_grp_id, ba_contact).await?; - let mut msg = Message::new(Viewtype::Image); - msg.set_file_from_bytes(&bob, "a.jpg", file_bytes, None)?; - let sent_msg = bob.send_msg(bob_grp_id, &mut msg).await; - assert!(msg.get_showpadlock()); - - alice.set_config(Config::DownloadLimit, Some("1")).await?; - assert_eq!(alice.download_limit().await?, Some(MIN_DOWNLOAD_LIMIT)); - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Available); - let alice_chat = Chat::load_from_db(&alice, msg.chat_id).await?; - // Incomplete message is assigned to 1:1 chat. - assert_eq!(alice_chat.typ, Chattype::Single); - - alice.set_config(Config::DownloadLimit, None).await?; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Done); - assert_eq!(msg.state, MessageState::InFresh); - assert_eq!(msg.viewtype, Viewtype::Image); - assert_ne!(msg.chat_id, alice_chat.id); - let alice_grp = Chat::load_from_db(&alice, msg.chat_id).await?; - assert_eq!(alice_grp.typ, Chattype::Group); - assert_eq!(alice_grp.name, "Group"); - assert_eq!( - chat::get_chat_contacts(&alice, alice_grp.id).await?.len(), - 2 - ); - - // Now Bob can send encrypted messages to Alice. - - let bob_grp_id = create_group(&bob, "Group1").await?; - add_contact_to_chat(&bob, bob_grp_id, ba_contact).await?; - let mut msg = Message::new(Viewtype::Image); - msg.set_file_from_bytes(&bob, "a.jpg", file_bytes, None)?; - let sent_msg = bob.send_msg(bob_grp_id, &mut msg).await; - assert!(msg.get_showpadlock()); - - alice.set_config(Config::DownloadLimit, Some("1")).await?; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Available); - // Until fully downloaded, an encrypted message must sit in the 1:1 chat. - assert_eq!(msg.chat_id, ab_chat_id); - - alice.set_config(Config::DownloadLimit, None).await?; - let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.download_state, DownloadState::Done); - assert_eq!(msg.state, MessageState::InFresh); - assert_eq!(msg.viewtype, Viewtype::Image); - assert_ne!(msg.chat_id, ab_chat_id); - let alice_grp = Chat::load_from_db(&alice, msg.chat_id).await?; - assert_eq!(alice_grp.typ, Chattype::Group); - assert_eq!(alice_grp.name, "Group1"); - assert_eq!( - chat::get_chat_contacts(&alice, alice_grp.id).await?.len(), - 2 - ); - - // The big message must go away from the 1:1 chat. - let msgs = chat::get_chat_msgs(&alice, ab_chat_id).await?; - assert_eq!(msgs.len(), E2EE_INFO_MSGS); - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_partial_group_consistency() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = tcm.alice().await; - let bob = tcm.bob().await; - let fiona = tcm.fiona().await; - let bob_id = alice.add_or_lookup_contact_id(&bob).await; - let alice_chat_id = create_group(&alice, "foos").await?; - add_contact_to_chat(&alice, alice_chat_id, bob_id).await?; - - send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?; - let add = alice.pop_sent_msg().await; - bob.recv_msg(&add).await; - let bob_chat_id = bob.get_last_msg().await.chat_id; - let contacts = get_chat_contacts(&bob, bob_chat_id).await?; - assert_eq!(contacts.len(), 2); - - // Bob receives partial message. - let msg_id = receive_imf_from_inbox( - &bob, - "first@example.org", - b"From: Alice \n\ -To: , \n\ -Chat-Version: 1.0\n\ -Subject: subject\n\ -Message-ID: \n\ -Date: Sun, 14 Nov 2021 00:10:00 +0000\ -Content-Type: text/plain -Chat-Group-Member-Added: charlie@example.com", - false, - Some(100000), - ) - .await? - .context("no received message")?; - - let msg = Message::load_from_db(&bob, msg_id.msg_ids[0]).await?; - - // Partial download does not change the member list. - assert_eq!(msg.download_state, DownloadState::Available); - assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?, contacts); - - // Alice sends normal message to bob, adding fiona. - add_contact_to_chat( - &alice, - alice_chat_id, - alice.add_or_lookup_contact_id(&fiona).await, - ) - .await?; - - bob.recv_msg(&alice.pop_sent_msg().await).await; - - let contacts = get_chat_contacts(&bob, bob_chat_id).await?; - assert_eq!(contacts.len(), 3); - - // Bob fully receives the partial message. - let msg_id = receive_imf_from_inbox( - &bob, - "first@example.org", - b"From: Alice \n\ -To: Bob \n\ -Chat-Version: 1.0\n\ -Subject: subject\n\ -Message-ID: \n\ -Date: Sun, 14 Nov 2021 00:10:00 +0000\ -Content-Type: text/plain -Chat-Group-Member-Added: charlie@example.com", - false, - None, - ) - .await? - .context("no received message")?; - - let msg = Message::load_from_db(&bob, msg_id.msg_ids[0]).await?; - - // After full download, the old message should not change group state. - assert_eq!(msg.download_state, DownloadState::Done); - assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?, contacts); - - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_protected_group_add_remove_member_missing_key() -> Result<()> { let mut tcm = TestContextManager::new(); @@ -4819,48 +4629,6 @@ async fn test_references() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_prefer_references_to_downloaded_msgs() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - let bob = &tcm.bob().await; - bob.set_config(Config::DownloadLimit, Some("1")).await?; - let fiona = &tcm.fiona().await; - let alice_bob_id = tcm.send_recv(bob, alice, "hi").await.from_id; - let alice_fiona_id = tcm.send_recv(fiona, alice, "hi").await.from_id; - let alice_chat_id = create_group(alice, "Group").await?; - add_contact_to_chat(alice, alice_chat_id, alice_bob_id).await?; - // W/o fiona the test doesn't work -- the last message is assigned to the 1:1 chat due to - // `is_probably_private_reply()`. - add_contact_to_chat(alice, alice_chat_id, alice_fiona_id).await?; - let sent = alice.send_text(alice_chat_id, "Hi").await; - let received = bob.recv_msg(&sent).await; - assert_eq!(received.download_state, DownloadState::Done); - let bob_chat_id = received.chat_id; - - let file_bytes = include_bytes!("../../test-data/image/screenshot.gif"); - let mut msg = Message::new(Viewtype::File); - msg.set_file_from_bytes(alice, "file", file_bytes, None)?; - let mut sent = alice.send_msg(alice_chat_id, &mut msg).await; - sent.payload = sent - .payload - .replace("References:", "X-Microsoft-Original-References:") - .replace("In-Reply-To:", "X-Microsoft-Original-In-Reply-To:"); - let received = bob.recv_msg(&sent).await; - assert_eq!(received.download_state, DownloadState::Available); - assert_ne!(received.chat_id, bob_chat_id); - assert_eq!(received.chat_id, bob.get_chat(alice).await.id); - - let mut msg = Message::new(Viewtype::File); - msg.set_file_from_bytes(alice, "file", file_bytes, None)?; - let sent = alice.send_msg(alice_chat_id, &mut msg).await; - let received = bob.recv_msg(&sent).await; - assert_eq!(received.download_state, DownloadState::Available); - assert_eq!(received.chat_id, bob_chat_id); - - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_list_from() -> Result<()> { let t = &TestContext::new_alice().await; @@ -5313,41 +5081,6 @@ async fn test_outgoing_plaintext_two_member_group() -> Result<()> { Ok(()) } -/// Tests that large messages are assigned -/// to non-key-contacts if the type is not `multipart/encrypted`. -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_partial_download_key_contact_lookup() -> Result<()> { - let mut tcm = TestContextManager::new(); - let alice = &tcm.alice().await; - let bob = &tcm.bob().await; - - // Create two chats with Alice, both with key-contact and email address contact. - let encrypted_chat = bob.create_chat(alice).await; - let unencrypted_chat = bob.create_email_chat(alice).await; - - let seen = false; - let is_partial_download = Some(9999); - let received = receive_imf_from_inbox( - bob, - "3333@example.org", - b"From: alice@example.org\n\ - To: bob@example.net\n\ - Message-ID: <3333@example.org>\n\ - Date: Sun, 22 Mar 2020 22:37:57 +0000\n\ - \n\ - hello\n", - seen, - is_partial_download, - ) - .await? - .unwrap(); - - assert_ne!(received.chat_id, encrypted_chat.id); - assert_eq!(received.chat_id, unencrypted_chat.id); - - Ok(()) -} - /// Tests that outgoing unencrypted message /// is assigned to a chat with email-contact. /// diff --git a/src/stock_str.rs b/src/stock_str.rs index ad6e9b4b07..776c6fdfc6 100644 --- a/src/stock_str.rs +++ b/src/stock_str.rs @@ -4,7 +4,6 @@ use std::collections::HashMap; use std::sync::Arc; use anyhow::{Result, bail}; -use humansize::{BINARY, format_size}; use strum::EnumProperty as EnumPropertyTrait; use strum_macros::EnumProperty; use tokio::sync::RwLock; @@ -1182,14 +1181,6 @@ pub(crate) async fn quota_exceeding(context: &Context, highest_usage: u64) -> St .replace("%%", "%") } -/// Stock string: `%1$s message` with placeholder replaced by human-readable size. -pub(crate) async fn partial_download_msg_body(context: &Context, org_bytes: u32) -> String { - let size = &format_size(org_bytes, BINARY); - translated(context, StockMessage::PartialDownloadMsgBody) - .await - .replace1(size) -} - /// Stock string: `Incoming Messages`. pub(crate) async fn incoming_messages(context: &Context) -> String { translated(context, StockMessage::IncomingMessages).await diff --git a/src/stock_str/stock_str_tests.rs b/src/stock_str/stock_str_tests.rs index 509724721e..da4685368d 100644 --- a/src/stock_str/stock_str_tests.rs +++ b/src/stock_str/stock_str_tests.rs @@ -126,14 +126,6 @@ async fn test_quota_exceeding_stock_str() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_partial_download_msg_body() -> Result<()> { - let t = TestContext::new().await; - let str = partial_download_msg_body(&t, 1024 * 1024).await; - assert_eq!(str, "1 MiB message"); - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_update_device_chats() { let t = TestContext::new_alice().await; diff --git a/src/test_utils.rs b/src/test_utils.rs index 683e39e582..73e9875f13 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -745,7 +745,7 @@ impl TestContext { /// unlikely to be affected as the message would be processed again in exactly the /// same way. pub(crate) async fn parse_msg(&self, msg: &SentMessage<'_>) -> MimeMessage { - MimeMessage::from_bytes(&self.ctx, msg.payload().as_bytes(), None) + MimeMessage::from_bytes(&self.ctx, msg.payload().as_bytes()) .await .unwrap() } diff --git a/src/webxdc/webxdc_tests.rs b/src/webxdc/webxdc_tests.rs index cc205f3fc9..acd15cb480 100644 --- a/src/webxdc/webxdc_tests.rs +++ b/src/webxdc/webxdc_tests.rs @@ -10,9 +10,8 @@ use crate::chat::{ }; use crate::chatlist::Chatlist; use crate::config::Config; -use crate::download::DownloadState; use crate::ephemeral; -use crate::receive_imf::{receive_imf, receive_imf_from_inbox}; +use crate::receive_imf::receive_imf; use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager}; use crate::tools::{self, SystemTime}; use crate::{message, sql}; @@ -329,69 +328,6 @@ async fn test_webxdc_contact_request() -> Result<()> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_webxdc_update_for_not_downloaded_instance() -> Result<()> { - // Alice sends a larger instance and an update - let alice = TestContext::new_alice().await; - let bob = TestContext::new_bob().await; - let chat = alice.create_chat(&bob).await; - bob.set_config(Config::DownloadLimit, Some("40000")).await?; - let mut alice_instance = create_webxdc_instance( - &alice, - "chess.xdc", - include_bytes!("../../test-data/webxdc/chess.xdc"), - )?; - let sent1 = alice.send_msg(chat.id, &mut alice_instance).await; - let alice_instance = sent1.load_from_db().await; - alice - .send_webxdc_status_update( - alice_instance.id, - r#"{"payload": 7, "summary":"sum", "document":"doc"}"#, - ) - .await?; - alice.flush_status_updates().await?; - let sent2 = alice.pop_sent_msg().await; - - // Bob does not download instance but already receives update - receive_imf_from_inbox( - &bob, - &alice_instance.rfc724_mid, - sent1.payload().as_bytes(), - false, - Some(70790), - ) - .await?; - let bob_instance = bob.get_last_msg().await; - bob_instance.chat_id.accept(&bob).await?; - bob.recv_msg_trash(&sent2).await; - assert_eq!(bob_instance.download_state, DownloadState::Available); - - // Bob downloads instance, updates should be assigned correctly - let received_msg = receive_imf_from_inbox( - &bob, - &alice_instance.rfc724_mid, - sent1.payload().as_bytes(), - false, - None, - ) - .await? - .unwrap(); - assert_eq!(*received_msg.msg_ids.first().unwrap(), bob_instance.id); - let bob_instance = bob.get_last_msg().await; - assert_eq!(bob_instance.viewtype, Viewtype::Webxdc); - assert_eq!(bob_instance.download_state, DownloadState::Done); - assert_eq!( - bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) - .await?, - r#"[{"payload":7,"document":"doc","summary":"sum","serial":1,"max_serial":1}]"# - ); - let info = bob_instance.get_webxdc_info(&bob).await?; - assert_eq!(info.document, "doc"); - assert_eq!(info.summary, "sum"); - - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_delete_webxdc_instance() -> Result<()> { let t = TestContext::new_alice().await;