From 32318cc174459c5dca57100c1f0297cd97e29d22 Mon Sep 17 00:00:00 2001 From: link2xt Date: Thu, 7 May 2026 07:10:17 +0200 Subject: [PATCH] WIP: feat: add option to process unencrypted messages --- src/calls/calls_tests.rs | 58 ++++--- src/chat/chat_tests.rs | 183 +++++++++++--------- src/chatlist.rs | 3 + src/config.rs | 5 + src/contact/contact_tests.rs | 38 ++--- src/context.rs | 6 + src/context/context_tests.rs | 30 ++-- src/decrypt.rs | 5 + src/e2ee.rs | 2 + src/ephemeral/ephemeral_tests.rs | 80 +++++---- src/events/chatlist_events.rs | 3 + src/html.rs | 39 +++-- src/imap.rs | 11 +- src/imap/session.rs | 1 + src/location.rs | 18 +- src/message/message_tests.rs | 42 +++-- src/mimefactory/mimefactory_tests.rs | 247 ++++++++++++++------------- src/mimeparser/mimeparser_tests.rs | 59 +++---- src/receive_imf.rs | 8 + src/receive_imf/receive_imf_tests.rs | 182 ++++++++++++++++++-- src/securejoin/securejoin_tests.rs | 6 + src/tests/verified_chats.rs | 3 + src/webxdc/webxdc_tests.rs | 4 +- test-data/message/image_4k.eml | 55 +++--- 24 files changed, 673 insertions(+), 415 deletions(-) diff --git a/src/calls/calls_tests.rs b/src/calls/calls_tests.rs index 946cd6aeb..c84b21f78 100644 --- a/src/calls/calls_tests.rs +++ b/src/calls/calls_tests.rs @@ -4,6 +4,7 @@ use crate::config::Config; use crate::constants::DC_CHAT_ID_TRASH; use crate::message::MessageState; use crate::receive_imf::receive_imf; +use crate::test_utils; use crate::test_utils::{TestContext, TestContextManager}; struct CallSetup { @@ -634,20 +635,23 @@ async fn test_forward_call() -> Result<()> { async fn test_end_text_call() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; + let bob = &tcm.bob().await; - let received1 = receive_imf( - alice, - b"From: bob@example.net\n\ - To: alice@example.org\n\ - Message-ID: \n\ - Date: Sun, 22 Mar 2020 22:37:57 +0000\n\ - Chat-Version: 1.0\n\ - \n\ - Hello\n", - false, + let encrypted_message = test_utils::encrypt_raw_message( + bob, + &[alice], + b"From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Message-ID: \r\n\ + Date: Sun, 22 Mar 2020 22:37:57 +0000\r\n\ + Chat-Version: 1.0\r\n\ + \r\n\ + Hello\r\n", ) - .await? - .unwrap(); + .await?; + let received1 = receive_imf(alice, encrypted_message.as_bytes(), false) + .await? + .unwrap(); assert_eq!(received1.msg_ids.len(), 1); let msg = Message::load_from_db(alice, received1.msg_ids[0]) .await @@ -656,21 +660,23 @@ async fn test_end_text_call() -> Result<()> { // Receiving "Call ended" message that refers // to the text message does not result in an error. - let received2 = receive_imf( - alice, - b"From: bob@example.net\n\ - To: alice@example.org\n\ - Message-ID: \n\ - Date: Sun, 22 Mar 2020 23:37:57 +0000\n\ - In-Reply-To: \n\ - Chat-Version: 1.0\n\ - Chat-Content: call-ended\n\ - \n\ - Call ended\n", - false, + let encrypted_message2 = test_utils::encrypt_raw_message( + bob, + &[alice], + b"From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Message-ID: \r\n\ + Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\ + In-Reply-To: \r\n\ + Chat-Version: 1.0\r\n\ + Chat-Content: call-ended\r\n\ + \r\n\ + Call ended\r\n", ) - .await? - .unwrap(); + .await?; + let received2 = receive_imf(alice, encrypted_message2.as_bytes(), false) + .await? + .unwrap(); assert_eq!(received2.msg_ids.len(), 1); assert_eq!(received2.chat_id, DC_CHAT_ID_TRASH); diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index 162768059..3aeba82d2 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -11,6 +11,7 @@ use crate::message::{Message, MessengerMessage, delete_msgs}; use crate::mimeparser::{self, MimeMessage}; use crate::receive_imf::receive_imf; use crate::securejoin::{get_securejoin_qr, join_securejoin}; +use crate::test_utils; use crate::test_utils::{ AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, E2EE_INFO_MSGS, TestContext, TestContextManager, TimeShiftFalsePositiveNote, sync, @@ -1049,6 +1050,7 @@ async fn chatlist_len(ctx: &Context, listflags: usize) -> usize { async fn test_archive() { // create two chats let t = TestContext::new_alice().await; + let mut msg = Message::new_text("foo".to_string()); let msg_id = add_device_msg(&t, None, Some(&mut msg)).await.unwrap(); let chat_id1 = message::Message::load_from_db(&t, msg_id) @@ -1159,42 +1161,47 @@ async fn test_archive() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_unarchive_if_muted() -> Result<()> { - let t = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let t = &tcm.alice().await; + let bob = &tcm.bob().await; - async fn msg_from_bob(t: &TestContext, num: u32) -> Result<()> { - receive_imf( - t, + let msg_from_bob = async |num: u32| { + let encrypted_message = test_utils::encrypt_raw_message( + bob, + &[t], format!( - "From: bob@example.net\n\ - To: alice@example.org\n\ - Message-ID: <{num}@example.org>\n\ - Chat-Version: 1.0\n\ - Date: Sun, 22 Mar 2022 19:37:57 +0000\n\ - \n\ - hello\n" + "From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Message-ID: <{num}@example.org>\r\n\ + Chat-Version: 1.0\r\n\ + Date: Sun, 22 Mar 2022 19:37:57 +0000\r\n\ + \r\n\ + hello\r\n" ) .as_bytes(), - false, ) - .await?; - Ok(()) - } + .await + .unwrap(); + receive_imf(t, encrypted_message.as_bytes(), false) + .await + .unwrap(); + }; - msg_from_bob(&t, 1).await?; + msg_from_bob(1).await; let chat_id = t.get_last_msg().await.get_chat_id(); - chat_id.accept(&t).await?; - chat_id.set_visibility(&t, ChatVisibility::Archived).await?; - assert_eq!(get_archived_cnt(&t).await?, 1); + chat_id.accept(t).await?; + chat_id.set_visibility(t, ChatVisibility::Archived).await?; + assert_eq!(get_archived_cnt(t).await?, 1); // not muted chat is unarchived on receiving a message - msg_from_bob(&t, 2).await?; - assert_eq!(get_archived_cnt(&t).await?, 0); + msg_from_bob(2).await; + assert_eq!(get_archived_cnt(t).await?, 0); // forever muted chat is not unarchived on receiving a message chat_id.set_visibility(&t, ChatVisibility::Archived).await?; - set_muted(&t, chat_id, MuteDuration::Forever).await?; - msg_from_bob(&t, 3).await?; - assert_eq!(get_archived_cnt(&t).await?, 1); + set_muted(t, chat_id, MuteDuration::Forever).await?; + msg_from_bob(3).await; + assert_eq!(get_archived_cnt(t).await?, 1); // otherwise muted chat is not unarchived on receiving a message set_muted( @@ -1207,7 +1214,7 @@ async fn test_unarchive_if_muted() -> Result<()> { ), ) .await?; - msg_from_bob(&t, 4).await?; + msg_from_bob(4).await; assert_eq!(get_archived_cnt(&t).await?, 1); // expired mute will unarchive the chat @@ -1221,20 +1228,20 @@ async fn test_unarchive_if_muted() -> Result<()> { ), ) .await?; - msg_from_bob(&t, 5).await?; - assert_eq!(get_archived_cnt(&t).await?, 0); + msg_from_bob(5).await; + assert_eq!(get_archived_cnt(t).await?, 0); // no unarchiving on sending to muted chat or on adding info messages to muted chat - chat_id.set_visibility(&t, ChatVisibility::Archived).await?; - set_muted(&t, chat_id, MuteDuration::Forever).await?; - send_text_msg(&t, chat_id, "out".to_string()).await?; - add_info_msg(&t, chat_id, "info").await?; - assert_eq!(get_archived_cnt(&t).await?, 1); + chat_id.set_visibility(t, ChatVisibility::Archived).await?; + set_muted(t, chat_id, MuteDuration::Forever).await?; + send_text_msg(t, chat_id, "out".to_string()).await?; + add_info_msg(t, chat_id, "info").await?; + assert_eq!(get_archived_cnt(t).await?, 1); // finally, unarchive on sending to not muted chat - set_muted(&t, chat_id, MuteDuration::NotMuted).await?; - send_text_msg(&t, chat_id, "out2".to_string()).await?; - assert_eq!(get_archived_cnt(&t).await?, 0); + set_muted(t, chat_id, MuteDuration::NotMuted).await?; + send_text_msg(t, chat_id, "out2".to_string()).await?; + assert_eq!(get_archived_cnt(t).await?, 0); Ok(()) } @@ -1381,6 +1388,9 @@ async fn test_markfresh_chat() -> Result<()> { async fn test_archive_fresh_msgs() -> Result<()> { let t = TestContext::new_alice().await; + // FIXME: use encrypted messages + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; + async fn msg_from(t: &TestContext, name: &str, num: u32) -> Result<()> { receive_imf( t, @@ -1873,45 +1883,38 @@ async fn test_lookup_self_by_contact_id() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_marknoticed_chat() -> Result<()> { - let t = TestContext::new_alice().await; - let chat = t.create_chat_with_contact("bob", "bob@example.org").await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + let chat = alice.create_chat(bob).await; - receive_imf( - &t, - b"From: bob@example.org\n\ - To: alice@example.org\n\ - Message-ID: <1@example.org>\n\ - Chat-Version: 1.0\n\ - Date: Fri, 23 Apr 2021 10:00:57 +0000\n\ - \n\ - hello\n", - false, - ) - .await?; + let bob_chat_id = bob.create_chat_id(alice).await; + let sent = bob.send_text(bob_chat_id, "hello").await; + alice.recv_msg(&sent).await; - let chats = Chatlist::try_load(&t, 0, None, None).await?; + let chats = Chatlist::try_load(alice, 0, None, None).await?; assert_eq!(chats.len(), 1); assert_eq!(chats.get_chat_id(0)?, chat.id); - assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 1); - assert_eq!(t.get_fresh_msgs().await?.len(), 1); + assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 1); + assert_eq!(alice.get_fresh_msgs().await?.len(), 1); - let msgs = get_chat_msgs(&t, chat.id).await?; - assert_eq!(msgs.len(), 1); - let msg_id = match msgs.first().unwrap() { + let msgs = get_chat_msgs(alice, chat.id).await?; + assert_eq!(msgs.len(), 2); + let msg_id = match msgs.last().unwrap() { ChatItem::Message { msg_id } => *msg_id, _ => MsgId::new_unset(), }; - let msg = message::Message::load_from_db(&t, msg_id).await?; + let msg = message::Message::load_from_db(alice, msg_id).await?; assert_eq!(msg.state, MessageState::InFresh); - marknoticed_chat(&t, chat.id).await?; + marknoticed_chat(alice, chat.id).await?; - let chats = Chatlist::try_load(&t, 0, None, None).await?; + let chats = Chatlist::try_load(alice, 0, None, None).await?; assert_eq!(chats.len(), 1); - let msg = message::Message::load_from_db(&t, msg_id).await?; + let msg = message::Message::load_from_db(alice, msg_id).await?; assert_eq!(msg.state, MessageState::InNoticed); - assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 0); - assert_eq!(t.get_fresh_msgs().await?.len(), 0); + assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 0); + assert_eq!(alice.get_fresh_msgs().await?.len(), 0); Ok(()) } @@ -1919,6 +1922,7 @@ async fn test_marknoticed_chat() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_contact_request_fresh_messages() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; let chats = Chatlist::try_load(&t, 0, None, None).await?; assert_eq!(chats.len(), 0); @@ -1970,40 +1974,43 @@ async fn test_contact_request_fresh_messages() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_contact_request_archive() -> Result<()> { - let t = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; - receive_imf( - &t, - b"From: bob@example.org\n\ - To: alice@example.org\n\ - Message-ID: <2@example.org>\n\ - Chat-Version: 1.0\n\ - Date: Sun, 22 Mar 2021 19:37:57 +0000\n\ - \n\ - hello\n", - false, - ) - .await?; + let bob_chat_id = bob.create_chat_id(alice).await; + let bob_sent_text = bob.send_text(bob_chat_id, "hello").await; + alice.recv_msg(&bob_sent_text).await; - let chats = Chatlist::try_load(&t, 0, None, None).await?; + let chats = Chatlist::try_load(alice, 0, None, None).await?; assert_eq!(chats.len(), 1); let chat_id = chats.get_chat_id(0)?; - assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request()); - assert_eq!(get_archived_cnt(&t).await?, 0); + assert!( + Chat::load_from_db(alice, chat_id) + .await? + .is_contact_request() + ); + assert_eq!(get_archived_cnt(alice).await?, 0); // archive request without accepting or blocking - chat_id.set_visibility(&t, ChatVisibility::Archived).await?; + chat_id + .set_visibility(alice, ChatVisibility::Archived) + .await?; - let chats = Chatlist::try_load(&t, 0, None, None).await?; + let chats = Chatlist::try_load(alice, 0, None, None).await?; assert_eq!(chats.len(), 1); let chat_id = chats.get_chat_id(0)?; assert!(chat_id.is_archived_link()); - assert_eq!(get_archived_cnt(&t).await?, 1); + assert_eq!(get_archived_cnt(alice).await?, 1); - let chats = Chatlist::try_load(&t, DC_GCL_ARCHIVED_ONLY, None, None).await?; + let chats = Chatlist::try_load(alice, DC_GCL_ARCHIVED_ONLY, None, None).await?; assert_eq!(chats.len(), 1); let chat_id = chats.get_chat_id(0)?; - assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request()); + assert!( + Chat::load_from_db(alice, chat_id) + .await? + .is_contact_request() + ); Ok(()) } @@ -2011,6 +2018,9 @@ async fn test_contact_request_archive() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_classic_email_chat() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; // Alice receives a classic (non-chat) message from Bob. receive_imf( @@ -4695,10 +4705,12 @@ async fn test_sync_delete_chat() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_sync_adhoc_grp() -> Result<()> { - let alice0 = &TestContext::new_alice().await; - let alice1 = &TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice0 = &tcm.alice().await; + let alice1 = &tcm.alice().await; for a in [alice0, alice1] { a.set_config_bool(Config::SyncMsgs, true).await?; + a.set_config_bool(Config::ProcessUnencrypted, true).await?; } let mut chat_ids = Vec::new(); @@ -6070,6 +6082,9 @@ async fn test_no_key_contacts_in_adhoc_chats() -> Result<()> { let alice = &tcm.alice().await; let bob = &tcm.bob().await; let charlie = &tcm.charlie().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let chat_id = receive_imf( alice, diff --git a/src/chatlist.rs b/src/chatlist.rs index 534dccf91..ccb36d40c 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -473,6 +473,7 @@ mod tests { add_contact_to_chat, create_broadcast, create_group, get_chat_contacts, remove_contact_from_chat, send_text_msg, set_chat_name, }; + use crate::config::Config; use crate::receive_imf::receive_imf; use crate::securejoin::get_securejoin_qr; use crate::stock_str::StockMessage; @@ -665,6 +666,7 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_search_single_chat() -> anyhow::Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; // receive a one-to-one-message receive_imf( @@ -725,6 +727,7 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_search_single_chat_without_authname() -> anyhow::Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; // receive a one-to-one-message without authname set receive_imf( diff --git a/src/config.rs b/src/config.rs index f0ca2fafb..447b47f92 100644 --- a/src/config.rs +++ b/src/config.rs @@ -486,6 +486,11 @@ pub enum Config { /// Experimental option denoting that the current profile is shared between multiple team members. /// For now, the only effect of this option is that seen flags are not synchronized. TeamProfile, + + /// Process unencrypted messages. + /// + /// Unencrypted messages are fetched and processed only if this setting is explicitly enabled. + ProcessUnencrypted, } impl Config { diff --git a/src/contact/contact_tests.rs b/src/contact/contact_tests.rs index b821301ce..68057144c 100644 --- a/src/contact/contact_tests.rs +++ b/src/contact/contact_tests.rs @@ -335,6 +335,7 @@ async fn test_add_or_lookup() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_contact_name_changes() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; // first message creates contact and one-to-one-chat without name set receive_imf( @@ -927,9 +928,17 @@ async fn test_synchronize_status() -> Result<()> { // Alice has two devices. let alice1 = &tcm.alice().await; let alice2 = &tcm.alice().await; + alice1 + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; + alice2 + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; // Bob has one device. let bob = &tcm.bob().await; + bob.set_config_bool(Config::ProcessUnencrypted, true) + .await?; let default_status = alice1.get_config(Config::Selfstatus).await?; @@ -998,33 +1007,18 @@ async fn test_selfavatar_changed_event() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_last_seen() -> Result<()> { - let alice = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; - let (contact_id, _) = Contact::add_or_lookup( - &alice, - "Bob", - &ContactAddress::new("bob@example.net")?, - Origin::ManuallyCreated, - ) - .await?; - let contact = Contact::get_by_id(&alice, contact_id).await?; + let contact = alice.add_or_lookup_contact(bob).await; assert_eq!(contact.last_seen(), 0); - let mime = br#"Subject: Hello -Message-ID: message@example.net -To: Alice -From: Bob -Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no -Chat-Version: 1.0 -Date: Sun, 22 Mar 2020 22:37:55 +0000 - -Hi."#; - receive_imf(&alice, mime, false).await?; - let msg = alice.get_last_msg().await; + let msg = tcm.send_recv(bob, alice, "Hi.").await; let timestamp = msg.get_timestamp(); assert!(timestamp > 0); - let contact = Contact::get_by_id(&alice, contact_id).await?; + let contact = Contact::get_by_id(alice, contact.id).await?; assert_eq!(contact.last_seen(), timestamp); Ok(()) @@ -1098,6 +1092,8 @@ async fn test_was_seen_recently_event() -> Result<()> { async fn test_lookup_id_by_addr_recent_ex(accept_unencrypted_chat: bool) -> Result<()> { let mut tcm = TestContextManager::new(); let bob = &tcm.bob().await; + bob.set_config_bool(Config::ProcessUnencrypted, true) + .await?; let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml"); assert!(std::str::from_utf8(raw)?.contains("Date: Thu, 24 Nov 2022 20:05:57 +0100")); diff --git a/src/context.rs b/src/context.rs index 95ce3c777..ff559fb86 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1056,6 +1056,12 @@ impl Context { "team_profile", self.get_config_bool(Config::TeamProfile).await?.to_string(), ); + res.insert( + "process_unencrypted", + self.get_config_bool(Config::ProcessUnencrypted) + .await? + .to_string(), + ); let elapsed = time_elapsed(&self.creation_time); res.insert("uptime", duration_to_str(elapsed)); diff --git a/src/context/context_tests.rs b/src/context/context_tests.rs index b2de005a7..107c1ae39 100644 --- a/src/context/context_tests.rs +++ b/src/context/context_tests.rs @@ -8,7 +8,7 @@ use crate::chatlist::Chatlist; use crate::constants::Chattype; use crate::message::Message; use crate::receive_imf::receive_imf; -use crate::test_utils::{E2EE_INFO_MSGS, TestContext}; +use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager}; use crate::tools::{SystemTime, create_outgoing_rfc724_mid}; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -54,6 +54,9 @@ async fn receive_msg(t: &TestContext, chat: &Chat) { async fn test_get_fresh_msgs_and_muted_chats() { // receive various mails in 3 chats let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); let bob = t.create_chat_with_contact("", "bob@g.it").await; let claire = t.create_chat_with_contact("", "claire@g.it").await; let dave = t.create_chat_with_contact("", "dave@g.it").await; @@ -103,6 +106,9 @@ async fn test_get_fresh_msgs_and_muted_chats() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_get_fresh_msgs_and_muted_until() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); let bob = t.create_chat_with_contact("", "bob@g.it").await; receive_msg(&t, &bob).await; assert_eq!(get_chat_msgs(&t, bob.id).await.unwrap().len(), 1); @@ -158,6 +164,7 @@ async fn test_get_fresh_msgs_and_muted_until() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_muted_context() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 0); t.set_config(Config::IsMuted, Some("1")).await?; let chat = t.create_chat_with_contact("", "bob@g.it").await; @@ -385,21 +392,12 @@ async fn test_search_msgs() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_search_unaccepted_requests() -> Result<()> { - let t = TestContext::new_alice().await; - receive_imf( - &t, - b"From: BobBar \n\ - To: alice@example.org\n\ - Subject: foo\n\ - Message-ID: \n\ - Chat-Version: 1.0\n\ - Date: Tue, 25 Oct 2022 13:37:00 +0000\n\ - \n\ - hello bob, foobar test!\n", - false, - ) - .await?; - let chat_id = t.get_last_msg().await.get_chat_id(); + let mut tcm = TestContextManager::new(); + let t = &tcm.alice().await; + let bob = &tcm.bob().await; + bob.set_config(Config::Displayname, Some("BobBar")).await?; + let msg = tcm.send_recv(bob, t, "hello bob, foobar test!").await; + let chat_id = msg.get_chat_id(); let chat = Chat::load_from_db(&t, chat_id).await?; assert_eq!(chat.get_type(), Chattype::Single); assert!(chat.is_contact_request()); diff --git a/src/decrypt.rs b/src/decrypt.rs index 21fd3d3cf..5658da7db 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -368,6 +368,7 @@ pub(crate) fn validate_detached_signature<'a, 'b>( #[cfg(test)] mod tests { use super::*; + use crate::config::Config; use crate::receive_imf::receive_imf; use crate::test_utils::TestContext; @@ -402,6 +403,8 @@ mod tests { assert!(get_attachment_mime(&mail).is_some()); let bob = TestContext::new_bob().await; + bob.set_config(Config::ProcessUnencrypted, Some("1")) + .await?; receive_imf(&bob, attachment_mime, false).await?; let msg = bob.get_last_msg().await; // Subject should be prepended because the attachment doesn't have "Chat-Version". @@ -416,6 +419,8 @@ mod tests { // Desktop via MS Exchange (actually made with TB though). let mixed_up_mime = include_bytes!("../test-data/message/mixed-up-long.eml"); let bob = TestContext::new_bob().await; + bob.set_config(Config::ProcessUnencrypted, Some("1")) + .await?; receive_imf(&bob, mixed_up_mime, false).await?; let msg = bob.get_last_msg().await; assert!(!msg.get_text().is_empty()); diff --git a/src/e2ee.rs b/src/e2ee.rs index 0ca08e1be..38ba32080 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -160,6 +160,8 @@ Sent with my Delta Chat Messenger: https://delta.chat"; let mut tcm = TestContextManager::new(); let bob = &tcm.bob().await; bob.set_config_bool(Config::IsChatmail, true).await?; + bob.set_config_bool(Config::ProcessUnencrypted, true) + .await?; let bob_chat_id = receive_imf( bob, b"From: alice@example.org\n\ diff --git a/src/ephemeral/ephemeral_tests.rs b/src/ephemeral/ephemeral_tests.rs index 693c1b872..1a8aa19ad 100644 --- a/src/ephemeral/ephemeral_tests.rs +++ b/src/ephemeral/ephemeral_tests.rs @@ -9,6 +9,7 @@ use crate::download::DownloadState; use crate::location; use crate::message::markseen_msgs; use crate::receive_imf::receive_imf; +use crate::test_utils; use crate::test_utils::{TestContext, TestContextManager}; use crate::timesmearing::MAX_SECONDS_TO_LEND_FROM_FUTURE; use crate::{ @@ -557,42 +558,46 @@ async fn test_delete_expired_imap_messages() -> Result<()> { // Regression test for a bug in the timer rollback protection. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_ephemeral_timer_references() -> Result<()> { - let alice = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; // Message with Message-ID and no timer is received. - receive_imf( - &alice, - b"From: Bob \n\ - To: Alice \n\ - Chat-Version: 1.0\n\ - Subject: Subject\n\ - Message-ID: \n\ - Date: Sun, 22 Mar 2020 00:10:00 +0000\n\ - \n\ - hello\n", - false, + let encrypted_msg = test_utils::encrypt_raw_message( + bob, + &[alice], + b"From: Bob \r\n\ + To: Alice \r\n\ + Chat-Version: 1.0\r\n\ + Subject: Subject\r\n\ + Message-ID: \r\n\ + Date: Sun, 22 Mar 2020 00:10:00 +0000\r\n\ + \r\n\ + hello\r\n", ) .await?; + receive_imf(&alice, encrypted_msg.as_bytes(), false).await?; let msg = alice.get_last_msg().await; let chat_id = msg.chat_id; assert_eq!(chat_id.get_ephemeral_timer(&alice).await?, Timer::Disabled); // Message with Message-ID is received. - receive_imf( - &alice, - b"From: Bob \n\ - To: Alice \n\ - Chat-Version: 1.0\n\ - Subject: Subject\n\ - Message-ID: \n\ - Date: Sun, 22 Mar 2020 00:11:00 +0000\n\ - Ephemeral-Timer: 60\n\ - \n\ - second message\n", - false, + let encrypted_msg = test_utils::encrypt_raw_message( + bob, + &[alice], + b"From: Bob \r\n\ + To: Alice \r\n\ + Chat-Version: 1.0\r\n\ + Subject: Subject\r\n\ + Message-ID: \r\n\ + Date: Sun, 22 Mar 2020 00:11:00 +0000\r\n\ + Ephemeral-Timer: 60\r\n\ + \r\n\ + second message\r\n", ) .await?; + receive_imf(&alice, encrypted_msg.as_bytes(), false).await?; assert_eq!( chat_id.get_ephemeral_timer(&alice).await?, Timer::Enabled { duration: 60 } @@ -614,21 +619,22 @@ async fn test_ephemeral_timer_references() -> Result<()> { // // The message also contains a quote of the first message to test that only References: // header and not In-Reply-To: is consulted by the rollback protection. - receive_imf( - &alice, - b"From: Bob \n\ - To: Alice \n\ - Chat-Version: 1.0\n\ - Subject: Subject\n\ - Message-ID: \n\ - Date: Sun, 22 Mar 2020 00:12:00 +0000\n\ - References: \n\ - In-Reply-To: \n\ - \n\ - > hello\n", - false, + let encrypted_msg = test_utils::encrypt_raw_message( + bob, + &[alice], + b"From: Bob \r\n\ + To: Alice \r\n\ + Chat-Version: 1.0\r\n\ + Subject: Subject\r\n\ + Message-ID: \r\n\ + Date: Sun, 22 Mar 2020 00:12:00 +0000\r\n\ + References: \r\n\ + In-Reply-To: \r\n\ + \r\n\ + > hello\r\n", ) .await?; + receive_imf(&alice, encrypted_msg.as_bytes(), false).await?; let msg = alice.get_last_msg().await; assert_eq!( diff --git a/src/events/chatlist_events.rs b/src/events/chatlist_events.rs index 540e6a6e0..a1a36db6d 100644 --- a/src/events/chatlist_events.rs +++ b/src/events/chatlist_events.rs @@ -521,6 +521,9 @@ mod test_chatlist_events { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_adhoc_group() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let mime = br#"Subject: First thread Message-ID: first@example.org To: Alice , Bob diff --git a/src/html.rs b/src/html.rs index bc4e4bfbc..185438c12 100644 --- a/src/html.rs +++ b/src/html.rs @@ -287,6 +287,7 @@ impl MsgId { mod tests { use super::*; use crate::chat::{self, Chat, forward_msgs, save_msgs}; + use crate::config::Config; use crate::constants; use crate::contact::ContactId; use crate::message::{MessengerMessage, Viewtype}; @@ -450,6 +451,9 @@ test some special html-characters as < > and & but also " and &#x // alice receives a non-delta html-message let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; let chat = alice .create_chat_with_contact("", "sender@testrun.org") .await; @@ -483,6 +487,8 @@ test some special html-characters as < > and & but also " and &#x // bob: check that bob also got the html-part of the forwarded message let bob = &tcm.bob().await; + bob.set_config(Config::ProcessUnencrypted, Some("1")) + .await?; let chat_bob = bob.create_chat_with_contact("", "alice@example.org").await; async fn check_receiver(ctx: &TestContext, chat: &Chat, sender: &TestContext) { let msg = ctx.recv_msg(&sender.pop_sent_msg().await).await; @@ -520,31 +526,32 @@ test some special html-characters as < > and & but also " and &#x #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_html_save_msg() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; // Alice receives a non-delta html-message - let alice = TestContext::new_alice().await; let chat = alice .create_chat_with_contact("", "sender@testrun.org") .await; let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml"); - receive_imf(&alice, raw, false).await?; + receive_imf(alice, raw, false).await?; let msg = alice.get_last_msg_in(chat.get_id()).await; // Alice saves the message let self_chat = alice.get_self_chat().await; - save_msgs(&alice, &[msg.id]).await?; + save_msgs(alice, &[msg.id]).await?; let saved_msg = alice.get_last_msg_in(self_chat.get_id()).await; assert_ne!(saved_msg.id, msg.id); - assert_eq!( - saved_msg.get_original_msg_id(&alice).await?.unwrap(), - msg.id - ); + assert_eq!(saved_msg.get_original_msg_id(alice).await?.unwrap(), msg.id); assert!(!saved_msg.is_forwarded()); // UI should not flag "saved messages" as "forwarded" assert_ne!(saved_msg.get_from_id(), ContactId::SELF); assert_eq!(saved_msg.get_from_id(), msg.get_from_id()); assert_eq!(saved_msg.is_dc_message, MessengerMessage::No); assert!(saved_msg.get_text().contains("this is plain")); assert!(saved_msg.has_html()); - let html = saved_msg.get_id().get_html(&alice).await?.unwrap(); + let html = saved_msg.get_id().get_html(alice).await?.unwrap(); assert!(html.contains("this is html")); Ok(()) @@ -555,6 +562,10 @@ test some special html-characters as < > and & but also " and &#x let mut tcm = TestContextManager::new(); // Alice receives a non-delta html-message let alice = &tcm.alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); let chat = alice .create_chat_with_contact("", "sender@testrun.org") .await; @@ -618,18 +629,22 @@ test some special html-characters as < > and & but also " and &#x #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_cp1252_html() -> Result<()> { - let t = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; receive_imf( - &t, + alice, include_bytes!("../test-data/message/cp1252-html.eml"), false, ) .await?; - let msg = t.get_last_msg().await; + let msg = alice.get_last_msg().await; assert_eq!(msg.viewtype, Viewtype::Text); assert!(msg.text.contains("foo bar ä ö ü ß")); assert!(msg.has_html()); - let html = msg.get_id().get_html(&t).await?.unwrap(); + let html = msg.get_id().get_html(alice).await?.unwrap(); println!("{html}"); assert!(html.contains("foo bar ä ö ü ß")); Ok(()) diff --git a/src/imap.rs b/src/imap.rs index 892253bc2..09c89eda3 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -1996,12 +1996,21 @@ pub(crate) async fn prefetch_should_download( // prevent_rename=true as this might be a mailing list message and in this case it would be bad if we rename the contact. // (prevent_rename is the last argument of from_field_to_contact_id()) + let is_encrypted = if let Some(content_type) = headers.get_header_value(HeaderDef::ContentType) + { + mailparse::parse_content_type(&content_type).mimetype == "multipart/encrypted" + } else { + false + }; + if flags.any(|f| f == Flag::Draft) { info!(context, "Ignoring draft message"); return Ok(false); } - let should_download = !blocked_contact || maybe_ndn; + let should_download = maybe_ndn + || (!blocked_contact + && (is_encrypted || context.get_config_bool(Config::ProcessUnencrypted).await?)); Ok(should_download) } diff --git a/src/imap/session.rs b/src/imap/session.rs index 8d4e7e087..e1e397574 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -21,6 +21,7 @@ const PREFETCH_FLAGS: &str = "(UID RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\ DATE \ X-MICROSOFT-ORIGINAL-MESSAGE-ID \ FROM \ + CONTENT-TYPE \ CHAT-VERSION \ CHAT-IS-POST-MESSAGE \ AUTOCRYPT-SETUP-MESSAGE\ diff --git a/src/location.rs b/src/location.rs index 51d67d65e..7b200e726 100644 --- a/src/location.rs +++ b/src/location.rs @@ -998,7 +998,7 @@ Content-Disposition: attachment; filename="location.kml" let received_msg2 = alice.get_last_msg().await; assert_eq!(received_msg2.id, received_msg.id); - let locations = get_range(&alice, None, None, 0, 0).await?; + let locations = get_range(alice, None, None, 0, 0).await?; assert_eq!(locations.len(), 1); Ok(()) } @@ -1006,10 +1006,13 @@ Content-Disposition: attachment; filename="location.kml" /// Tests that `location.kml` is not hidden and not seen if it contains a message. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn receive_visible_location_kml() -> Result<()> { - let alice = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; - receive_imf( - &alice, + let encrypted_message = test_utils::encrypt_raw_message( + bob, + &[alice], br#"Subject: locations MIME-Version: 1.0 To: @@ -1037,10 +1040,9 @@ Content-Disposition: attachment; filename="location.kml" ---U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#, - false, - ) - .await?; +--U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#).await?; + + receive_imf(&alice, encrypted_message.as_bytes(), false).await?; let received_msg = alice.get_last_msg().await; assert_eq!(received_msg.text, "Text message."); diff --git a/src/message/message_tests.rs b/src/message/message_tests.rs index dc63f86a1..c63049e3b 100644 --- a/src/message/message_tests.rs +++ b/src/message/message_tests.rs @@ -111,6 +111,9 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> { let alice = &tcm.alice().await; let bob = &tcm.bob().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; tcm.section("Bob sends encrypted message to Alice"); let alice_chat = alice.create_chat(bob).await; @@ -139,23 +142,14 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_get_chat_id() { // Alice receives a message that pops up as a contact request - let alice = TestContext::new_alice().await; - receive_imf( - &alice, - b"From: Bob \n\ - To: alice@example.org\n\ - Chat-Version: 1.0\n\ - Message-ID: <123@example.com>\n\ - Date: Fri, 29 Jan 2021 21:37:55 +0000\n\ - \n\ - hello\n", - false, - ) - .await - .unwrap(); + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + let chat_id = bob.create_chat_id(alice).await; + let sent = bob.send_text(chat_id, "hello").await; + let msg = alice.recv_msg(&sent).await; // check chat-id of this message - let msg = alice.get_last_msg().await; assert!(!msg.get_chat_id().is_special()); assert_eq!(msg.get_text(), "hello".to_string()); } @@ -465,7 +459,11 @@ async fn test_get_state() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_is_bot() -> Result<()> { - let alice = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; // Alice receives an auto-generated non-chat message. // @@ -473,7 +471,7 @@ async fn test_is_bot() -> Result<()> { // in which case the message should be marked as bot-generated, // but the contact should not. receive_imf( - &alice, + alice, b"From: Claire \n\ To: alice@example.org\n\ Message-ID: <789@example.com>\n\ @@ -487,12 +485,12 @@ async fn test_is_bot() -> Result<()> { let msg = alice.get_last_msg().await; assert_eq!(msg.get_text(), "hello".to_string()); assert!(msg.is_bot()); - let contact = Contact::get_by_id(&alice, msg.from_id).await?; + let contact = Contact::get_by_id(alice, msg.from_id).await?; assert!(!contact.is_bot()); // Alice receives a message from Bob the bot. receive_imf( - &alice, + alice, b"From: Bob \n\ To: alice@example.org\n\ Chat-Version: 1.0\n\ @@ -507,12 +505,12 @@ async fn test_is_bot() -> Result<()> { let msg = alice.get_last_msg().await; assert_eq!(msg.get_text(), "hello".to_string()); assert!(msg.is_bot()); - let contact = Contact::get_by_id(&alice, msg.from_id).await?; + let contact = Contact::get_by_id(alice, msg.from_id).await?; assert!(contact.is_bot()); // Alice receives a message from Bob who is not the bot anymore. receive_imf( - &alice, + alice, b"From: Bob \n\ To: alice@example.org\n\ Chat-Version: 1.0\n\ @@ -526,7 +524,7 @@ async fn test_is_bot() -> Result<()> { let msg = alice.get_last_msg().await; assert_eq!(msg.get_text(), "hello again".to_string()); assert!(!msg.is_bot()); - let contact = Contact::get_by_id(&alice, msg.from_id).await?; + let contact = Contact::get_by_id(alice, msg.from_id).await?; assert!(!contact.is_bot()); Ok(()) diff --git a/src/mimefactory/mimefactory_tests.rs b/src/mimefactory/mimefactory_tests.rs index f47dfc086..86b6256e6 100644 --- a/src/mimefactory/mimefactory_tests.rs +++ b/src/mimefactory/mimefactory_tests.rs @@ -19,6 +19,7 @@ use crate::headerdef::HeaderDef; use crate::message; use crate::mimeparser::MimeMessage; use crate::receive_imf::receive_imf; +use crate::test_utils; use crate::test_utils::{TestContext, TestContextManager, get_chat_msg}; use crate::tools::SystemTime; @@ -132,14 +133,13 @@ async fn test_subject_from_mua() { // 1.: Receive a mail from an MUA assert_eq!( msg_to_subject_str( - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: Bob \n\ - To: alice@example.org\n\ - Subject: Antw: Chat: hello\n\ - Message-ID: <2222@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n" + b"From: Bob \r\n\ + To: alice@example.org\r\n\ + Subject: Antw: Chat: hello\r\n\ + Message-ID: <2222@example.net>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n" ) .await, "Re: Chat: hello" @@ -147,14 +147,13 @@ async fn test_subject_from_mua() { assert_eq!( msg_to_subject_str( - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: Bob \n\ - To: alice@example.org\n\ - Subject: Infos: 42\n\ - Message-ID: <2222@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n" + b"From: Bob \r\n\ + To: alice@example.org\r\n\ + Subject: Infos: 42\r\n\ + Message-ID: <2222@example.net>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n" ) .await, "Re: Infos: 42" @@ -166,15 +165,14 @@ async fn test_subject_from_dc() { // 2. Receive a message from Delta Chat assert_eq!( msg_to_subject_str( - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: bob@example.com\n\ - To: alice@example.org\n\ - Subject: Chat: hello\n\ - Chat-Version: 1.0\n\ - Message-ID: <2223@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n" + b"From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Subject: Chat: hello\r\n\ + Chat-Version: 1.0\r\n\ + Message-ID: <2223@example.net>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n" ) .await, "Re: Chat: hello" @@ -199,29 +197,27 @@ async fn test_subject_outgoing() { async fn test_subject_unicode() { // 4. Receive messages with unicode characters and make sure that we do not panic (we do not care about the result) msg_to_subject_str( - "Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: bob@example.com\n\ - To: alice@example.org\n\ - Subject: äääää\n\ - Chat-Version: 1.0\n\ - Message-ID: <2893@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n" + "From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Subject: äääää\r\n\ + Chat-Version: 1.0\r\n\ + Message-ID: <2893@example.com>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n" .as_bytes(), ) .await; msg_to_subject_str( - "Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: bob@example.com\n\ - To: alice@example.org\n\ - Subject: aäääää\n\ - Chat-Version: 1.0\n\ - Message-ID: <2893@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n" + "From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Subject: aäääää\r\n\ + Chat-Version: 1.0\r\n\ + Message-ID: <2893@example.com>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n" .as_bytes(), ) .await; @@ -230,48 +226,51 @@ async fn test_subject_unicode() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_subject_mdn() { // 5. Receive an mdn (read receipt) and make sure the mdn's subject is not used - let t = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let t = &tcm.alice().await; + let bob = &tcm.bob().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); receive_imf( &t, - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: alice@example.org\n\ - To: bob@example.com\n\ - Subject: Hello, Bob\n\ - Chat-Version: 1.0\n\ - Message-ID: <2893@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n", + b"From: alice@example.org\r\n\ + To: bob@example.net\r\n\ + Subject: Hello, Bob\r\n\ + Chat-Version: 1.0\r\n\ + Message-ID: <2893@example.com>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n", false, ) .await .unwrap(); let mut new_msg = incoming_msg_to_reply_msg( - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: bob@example.com\n\ - To: alice@example.org\n\ - Subject: message opened\n\ - Date: Sun, 22 Mar 2020 23:37:57 +0000\n\ - Chat-Version: 1.0\n\ - Message-ID: \n\ - Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\n\ - \n\ - \n\ - --SNIPP\n\ - Content-Type: text/plain; charset=utf-8\n\ - \n\ - Read receipts do not guarantee sth. was read.\n\ - \n\ - \n\ - --SNIPP\n\ - Content-Type: message/disposition-notification\n\ - \n\ - Reporting-UA: Delta Chat 1.28.0\n\ - Original-Recipient: rfc822;bob@example.com\n\ - Final-Recipient: rfc822;bob@example.com\n\ - Original-Message-ID: <2893@example.com>\n\ - Disposition: manual-action/MDN-sent-automatically; displayed\n\ - \n", &t).await; + b"From: bob@example.net\r\n\ + To: alice@example.org\r\n\ + Subject: message opened\r\n\ + Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\ + Chat-Version: 1.0\r\n\ + Message-ID: \r\n\ + Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\r\n\ + \r\n\ + \r\n\ + --SNIPP\r\n\ + Content-Type: text/plain; charset=utf-8\r\n\ + \r\n\ + Read receipts do not guarantee sth. was read.\r\n\ + \r\n\ + \r\n\ + --SNIPP\r\n\ + Content-Type: message/disposition-notification\r\n\ + \r\n\ + Reporting-UA: Delta Chat 1.28.0\r\n\ + Original-Recipient: rfc822;bob@example.com\r\n\ + Final-Recipient: rfc822;bob@example.com\r\n\ + Original-Message-ID: <2893@example.com>\r\n\ + Disposition: manual-action/MDN-sent-automatically; displayed\r\n\ + \r\n", &t, bob).await; chat::send_msg(&t, new_msg.chat_id, &mut new_msg) .await .unwrap(); @@ -284,19 +283,24 @@ async fn test_subject_mdn() { async fn test_mdn_create_encrypted() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = tcm.alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; alice .set_config(Config::Displayname, Some("Alice Exampleorg")) .await?; let bob = tcm.bob().await; + bob.set_config_bool(Config::ProcessUnencrypted, true) + .await?; bob.set_config(Config::Displayname, Some("Bob Examplenet")) .await?; bob.set_config(Config::Selfstatus, Some("Bob Examplenet")) .await?; bob.set_config_bool(Config::MdnsEnabled, true).await?; + // MDN for unencrypted message is not encrypted. let mut msg = Message::new(Viewtype::Text); - msg.param.set_int(Param::SkipAutocrypt, 1); - let chat_alice = alice.create_chat(&bob).await.id; + let chat_alice = alice.create_email_chat(&bob).await.id; let sent = alice.send_msg(chat_alice, &mut msg).await; let rcvd = bob.recv_msg(&sent).await; @@ -311,13 +315,13 @@ async fn test_mdn_create_encrypted() -> Result<()> { let bob_alice_contact = bob.add_or_lookup_contact(&alice).await; assert_eq!(bob_alice_contact.get_authname(), "Alice Exampleorg"); + // MDN for encrypted message is encrypted. let rcvd = tcm.send_recv(&alice, &bob, "Heyho").await; message::markseen_msgs(&bob, vec![rcvd.id]).await?; let mimefactory = MimeFactory::from_mdn(&bob, rcvd.from_id, rcvd.rfc724_mid, vec![]).await?; let rendered_msg = mimefactory.render(&bob).await?; - // When encrypted, the MDN should be encrypted as well assert!(rendered_msg.is_encrypted); assert!(!rendered_msg.message.contains("Bob Examplenet")); assert!(!rendered_msg.message.contains("Alice Exampleorg")); @@ -417,7 +421,7 @@ async fn first_subject_str(t: TestContext) -> String { mf.subject_str(&t).await.unwrap() } -// In `imf_raw`, From has to be bob@example.com, To has to be alice@example.org +// In `imf_raw`, From has to be bob@example.net, To has to be alice@example.org async fn msg_to_subject_str(imf_raw: &[u8]) -> String { let subject_str = msg_to_subject_str_inner(imf_raw, false, false, false).await; @@ -465,29 +469,33 @@ async fn msg_to_subject_str_inner( reply: bool, message_arrives_inbetween: bool, ) -> String { - let t = TestContext::new_alice().await; - let mut new_msg = incoming_msg_to_reply_msg(imf_raw, &t).await; - let incoming_msg = get_chat_msg(&t, new_msg.chat_id, 0, 1).await; + let mut tcm = TestContextManager::new(); + let t = &tcm.alice().await; + let bob = &tcm.bob().await; + let mut new_msg = incoming_msg_to_reply_msg(imf_raw, t, bob).await; + let incoming_msg = get_chat_msg(t, new_msg.chat_id, 1, 2).await; if delete_original_msg { incoming_msg.id.trash(&t, false).await.unwrap(); } if message_arrives_inbetween { - receive_imf( - &t, - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: Bob \n\ - To: alice@example.org\n\ - Subject: Some other, completely unrelated subject\n\ - Message-ID: <3cl4@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - Some other, completely unrelated content\n", - false, + let encrypted_msg = test_utils::encrypt_raw_message( + bob, + &[t], + b"From: Bob \r\n\ + To: alice@example.org\r\n\ + Subject: Some other, completely unrelated subject\r\n\ + Message-ID: <3cl4@example.com>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + Some other, completely unrelated content\r\n", ) .await .unwrap(); + receive_imf(t, encrypted_msg.as_bytes(), false) + .await + .unwrap(); let arrived_msg = t.get_last_msg().await; assert_eq!(arrived_msg.chat_id, incoming_msg.chat_id); @@ -505,8 +513,18 @@ async fn msg_to_subject_str_inner( } // Creates a `Message` that replies "Hi" to the incoming email in `imf_raw`. -async fn incoming_msg_to_reply_msg(imf_raw: &[u8], context: &Context) -> Message { - receive_imf(context, imf_raw, false).await.unwrap(); +async fn incoming_msg_to_reply_msg( + imf_raw: &[u8], + context: &TestContext, + from: &TestContext, +) -> Message { + let encrypted_msg = test_utils::encrypt_raw_message(from, &[context], imf_raw) + .await + .unwrap(); + + receive_imf(context, encrypted_msg.as_bytes(), false) + .await + .unwrap(); let chats = Chatlist::try_load(context, 0, None, None).await.unwrap(); @@ -522,30 +540,31 @@ async fn incoming_msg_to_reply_msg(imf_raw: &[u8], context: &Context) -> Message #[tokio::test(flavor = "multi_thread", worker_threads = 2)] // This test could still be extended async fn test_render_reply() { - let t = TestContext::new_alice().await; - let context = &t; + let mut tcm = TestContextManager::new(); + let t = &tcm.alice().await; + let charlie = &tcm.charlie().await; let mut msg = incoming_msg_to_reply_msg( - b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ - From: Charlie \n\ - To: alice@example.org\n\ - Subject: Chat: hello\n\ - Chat-Version: 1.0\n\ - Message-ID: <2223@example.com>\n\ - Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ - \n\ - hello\n", - context, + b"From: Charlie \r\n\ + To: alice@example.org\r\n\ + Subject: Chat: hello\r\n\ + Chat-Version: 1.0\r\n\ + Message-ID: <2223@example.com>\r\n\ + Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\ + \r\n\ + hello\r\n", + t, + charlie, ) .await; - chat::send_msg(&t, msg.chat_id, &mut msg).await.unwrap(); + chat::send_msg(t, msg.chat_id, &mut msg).await.unwrap(); let mimefactory = MimeFactory::from_msg(&t, msg).await.unwrap(); let recipients = mimefactory.recipients(); - assert_eq!(recipients, vec!["charlie@example.com"]); + assert_eq!(recipients, vec!["charlie@example.net"]); - let rendered_msg = mimefactory.render(context).await.unwrap(); + let rendered_msg = mimefactory.render(t).await.unwrap(); let mail = mailparse::parse_mail(rendered_msg.message.as_bytes()).unwrap(); assert_eq!( @@ -557,7 +576,7 @@ async fn test_render_reply() { "1.0" ); - let _mime_msg = MimeMessage::from_bytes(context, rendered_msg.message.as_bytes()) + let _mime_msg = MimeMessage::from_bytes(t, rendered_msg.message.as_bytes()) .await .unwrap(); } diff --git a/src/mimeparser/mimeparser_tests.rs b/src/mimeparser/mimeparser_tests.rs index ee67f48eb..7ac76317f 100644 --- a/src/mimeparser/mimeparser_tests.rs +++ b/src/mimeparser/mimeparser_tests.rs @@ -12,7 +12,7 @@ use crate::{ message::{MessageState, MessengerMessage}, receive_imf::receive_imf, securejoin::QrInvite, - test_utils::{TestContext, TestContextManager}, + test_utils::{self, TestContext, TestContextManager}, tools::time, }; @@ -1503,31 +1503,23 @@ Some reply // Test that WantsMdn parameter is not set on outgoing messages. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_outgoing_wants_mdn() -> Result<()> { - let alice = TestContext::new_alice().await; - let bob = TestContext::new_bob().await; + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let alice2 = &tcm.alice().await; + let bob = &tcm.bob().await; - let raw = br"Date: Thu, 28 Jan 2021 00:26:57 +0000 -Chat-Version: 1.0\n\ -Message-ID: -To: Bob -From: Alice -Subject: subject -Chat-Disposition-Notification-To: alice@example.org - -Message. -"; + let chat_id = alice.create_chat(bob).await.id; + let sent = alice.send_text(chat_id, "Message.").await; // Bob receives message. - receive_imf(&bob, raw, false).await?; - let msg = bob.get_last_msg().await; + let bob_msg = bob.recv_msg(&sent).await; // Message is incoming. - assert!(msg.param.get_bool(Param::WantsMdn).unwrap()); + assert!(bob_msg.param.get_bool(Param::WantsMdn).unwrap()); // Alice receives copy-to-self. - receive_imf(&alice, raw, false).await?; - let msg = alice.get_last_msg().await; + let alice2_msg = alice2.recv_msg(&sent).await; // Message is outgoing, don't send read receipt to self. - assert!(msg.param.get_bool(Param::WantsMdn).is_none()); + assert!(alice2_msg.param.get_bool(Param::WantsMdn).is_none()); Ok(()) } @@ -1604,7 +1596,9 @@ async fn test_ignore_read_receipt_to_self() -> Result<()> { /// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_ms_exchange_mdn() -> Result<()> { - let t = TestContext::new_alice().await; + let mut tcm = TestContextManager::new(); + let t = tcm.alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; let original = include_bytes!("../../test-data/message/ms_exchange_report_original_message.eml"); @@ -2048,6 +2042,8 @@ async fn test_multiple_autocrypt_hdrs() -> Result<()> { async fn test_receive_signed_only() -> Result<()> { let mut tcm = TestContextManager::new(); let bob = &tcm.bob().await; + bob.set_config(Config::ProcessUnencrypted, Some("1")) + .await?; let imf_raw = include_bytes!("../../test-data/message/unencrypted_signed_simple.eml"); let msg = receive_imf(bob, imf_raw, false).await?.unwrap(); @@ -2088,19 +2084,24 @@ async fn test_huge_image_becomes_file() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_4k_image_stays_image() -> Result<()> { - let t = TestContext::new_alice().await; - let msg_id = receive_imf( - &t, + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + let encrypted_msg = test_utils::encrypt_raw_message( + bob, + &[alice], include_bytes!("../../test-data/message/image_4k.eml"), - false, ) - .await? - .unwrap() - .msg_ids[0]; - let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap(); + .await?; + + let msg_id = receive_imf(alice, encrypted_msg.as_bytes(), false) + .await? + .unwrap() + .msg_ids[0]; + let msg = Message::load_from_db(alice, msg_id).await.unwrap(); // 4K image should be treated as image: assert_eq!(msg.viewtype, Viewtype::Image); - assert!(msg.get_file(&t).is_some()); + assert!(msg.get_file(alice).is_some()); assert_eq!(msg.get_filename().unwrap(), "4k_image.png"); assert_eq!(msg.get_filemime().unwrap(), "image/png"); assert_eq!(msg.param.get_int(Param::Width).unwrap_or_default(), 3840); diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 6feebccf2..95dec29b3 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -505,6 +505,14 @@ pub(crate) async fn receive_imf_inner( Ok(mime_parser) => mime_parser, }; + if !mime_parser.was_encrypted() + && mime_parser.get_header(HeaderDef::SecureJoin).is_none() + && !context.get_config_bool(Config::ProcessUnencrypted).await? + { + warn!(context, "Fetched unencrypted message, ignoring"); + return trash().await; + } + let rfc724_mid_orig = &mime_parser .get_rfc724_mid() .unwrap_or(rfc724_mid.to_string()); diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index f36295717..0e55870d7 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -100,6 +100,9 @@ async fn test_adhoc_group_is_shown() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_adhoc_group_show_accepted_contact_accepted() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); // accept Bob by accepting a delta-message from Bob receive_imf(&t, MSGRMSG, false).await.unwrap(); @@ -154,6 +157,9 @@ async fn test_adhoc_group_show_all() { async fn test_adhoc_groups_merge() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; receive_imf( alice, b"From: bob@example.net\n\ @@ -354,6 +360,9 @@ async fn test_no_message_id_header() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_escaped_from() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); let contact_id = Contact::create(&t, "foobar", "foobar@example.com") .await .unwrap(); @@ -387,6 +396,9 @@ async fn test_escaped_from() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_escaped_recipients() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); Contact::create(&t, "foobar", "foobar@example.com") .await .unwrap(); @@ -434,6 +446,9 @@ async fn test_escaped_recipients() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_cc_to_contact() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); Contact::create(&t, "foobar", "foobar@example.com") .await .unwrap(); @@ -590,6 +605,9 @@ async fn test_parse_ndn( ) -> (TestContext, MsgId) { let t = TestContext::new().await; t.configure_addr(self_addr).await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf( &t, @@ -674,6 +692,7 @@ async fn test_resend_after_ndn() -> Result<()> { async fn test_parse_ndn_group_msg() -> Result<()> { let t = TestContext::new().await; t.configure_addr("alice@gmail.com").await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; receive_imf( &t, @@ -715,6 +734,7 @@ async fn test_parse_ndn_group_msg() -> Result<()> { async fn test_concat_multiple_ndns() -> Result<()> { let t = TestContext::new().await; t.configure_addr("alice@posteo.org").await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; let mid = "1234@mail.gmail.com"; receive_imf( &t, @@ -772,6 +792,9 @@ async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_html_only_mail() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); let msg = load_imf_email(&t, include_bytes!("../../test-data/message/wrong-html.eml")).await; assert_eq!( msg.text, @@ -807,6 +830,7 @@ static GH_MAILINGLIST2: &str = "Received: (Postfix, from userid 1000); Mon, 4 De #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_github_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; receive_imf(&t.ctx, GH_MAILINGLIST, false).await?; @@ -880,6 +904,8 @@ static DC_MAILINGLIST2: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4 #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_classic_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; + receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); let chat_id = chats.get_chat_id(0).unwrap(); @@ -921,6 +947,8 @@ Hello mailinglist!\r\n" #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_other_device_writes_to_mailinglist() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; + receive_imf(&t, DC_MAILINGLIST, false).await.unwrap(); let first_msg = t.get_last_msg().await; let first_chat = Chat::load_from_db(&t, first_msg.chat_id).await?; @@ -971,6 +999,9 @@ async fn test_other_device_writes_to_mailinglist() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_block_mailing_list() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); t.evtracker.wait_next_incoming_message().await; @@ -1005,6 +1036,9 @@ async fn test_block_mailing_list() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_decide_block_then_unblock() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf(&t, DC_MAILINGLIST, false).await.unwrap(); let blocked = Contact::get_all_blocked(&t).await.unwrap(); @@ -1035,6 +1069,9 @@ async fn test_mailing_list_decide_block_then_unblock() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_decide_not_now() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); @@ -1062,6 +1099,9 @@ async fn test_mailing_list_decide_not_now() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_decide_accept() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); @@ -1084,6 +1124,8 @@ async fn test_mailing_list_decide_accept() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_multiple_names_in_subject() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; + receive_imf( &t, b"From: Foo Bar \n\ @@ -1108,6 +1150,7 @@ async fn test_mailing_list_multiple_names_in_subject() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_majordomo_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; // test mailing lists not having a `ListId:`-header receive_imf( @@ -1160,6 +1203,7 @@ async fn test_majordomo_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailchimp_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; receive_imf( &t, @@ -1193,6 +1237,7 @@ async fn test_mailchimp_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_dhl_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; receive_imf( &t, @@ -1218,6 +1263,7 @@ async fn test_dhl_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_dpd_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; receive_imf( &t, @@ -1243,6 +1289,7 @@ async fn test_dpd_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_xt_local_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; receive_imf( &t, @@ -1276,6 +1323,7 @@ async fn test_xt_local_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_xing_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; receive_imf( &t, @@ -1298,6 +1346,7 @@ async fn test_xing_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_ttline_mailing_list() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; receive_imf( &t, @@ -1318,6 +1367,9 @@ async fn test_ttline_mailing_list() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_with_mimepart_footer() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); // the mailing list message contains two top-level texts. // the second text is a footer that is added by some mailing list software @@ -1345,6 +1397,9 @@ async fn test_mailing_list_with_mimepart_footer() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_with_mimepart_footer_signed() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf( &t, @@ -1369,6 +1424,9 @@ async fn test_mailing_list_with_mimepart_footer_signed() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_apply_mailinglist_changes_assigned_by_reply() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf(&t, GH_MAILINGLIST, false).await.unwrap(); @@ -1407,6 +1465,9 @@ async fn test_apply_mailinglist_changes_assigned_by_reply() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_chat_message() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf( &t, @@ -1429,6 +1490,9 @@ async fn test_mailing_list_chat_message() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mailing_list_bot() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); t.set_config(Config::Bot, Some("1")).await.unwrap(); receive_imf( @@ -1461,6 +1525,10 @@ async fn test_dont_show_noreply_in_contacts_list() { async fn check_dont_show_in_contacts_list(addr: &str) { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); + receive_imf( &t, format!( @@ -1490,6 +1558,9 @@ YEAAAAAA!. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_pdf_filename_simple() { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); let msg = load_imf_email( &t, include_bytes!("../../test-data/message/pdf_filename_simple.eml"), @@ -1510,6 +1581,9 @@ async fn test_pdf_filename_simple() { async fn test_pdf_filename_continuation() { // test filenames split across multiple header lines, see rfc 2231 let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); let msg = load_imf_email( &t, include_bytes!("../../test-data/message/pdf_filename_continuation.eml"), @@ -1535,6 +1609,9 @@ async fn test_pdf_filename_continuation() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_many_images() { let t = TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); receive_imf( &t, @@ -1555,6 +1632,9 @@ async fn test_many_images() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_in_reply_to() { let t = TestContext::new().await; + t.set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); t.configure_addr("bob@example.com").await; // Receive message from Alice about group "foo". @@ -1632,6 +1712,10 @@ async fn test_save_mime_headers_off() -> anyhow::Result<()> { async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: bool) { let mut tcm = TestContextManager::new(); let alice = tcm.alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); // Claire, a customer, sends a support request // to the alias address . @@ -1698,6 +1782,11 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo let claire = tcm.unconfigured().await; claire.configure_addr("claire@example.org").await; + claire + .set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); + receive_imf(&claire, claire_request.as_bytes(), false) .await .unwrap(); @@ -1912,6 +2001,7 @@ Message content", async fn test_unencrypted_doesnt_goto_self_chat() -> Result<()> { let mut tcm = TestContextManager::new(); let t = &tcm.alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; let mut chat_id = None; for (i, to) in [ @@ -1993,6 +2083,10 @@ async fn test_no_smtp_job_for_self_chat() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_outgoing_classic_mail_creates_chat() { let alice = TestContext::new_alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); // Alice downloads outgoing classic email. receive_imf( @@ -2018,6 +2112,9 @@ Message content", async fn test_duplicate_message() -> Result<()> { // Test that duplicate messages are ignored based on the Message-ID let alice = TestContext::new_alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let bob_contact_id = Contact::add_or_lookup( &alice, @@ -2076,6 +2173,8 @@ Second signature"; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_ignore_footer_status_from_mailinglist() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; + let bob_id = Contact::add_or_lookup( &t, "", @@ -2155,6 +2254,8 @@ Original signature updated", #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_ignore_old_status_updates() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; + let bob_id = Contact::add_or_lookup( &t, "", @@ -2224,11 +2325,15 @@ sig thursday", #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_chat_assignment_private_classical_reply() { + let mut tcm = TestContextManager::new(); for outgoing_is_classical in &[true, false] { - let t = TestContext::new_alice().await; + let t = &tcm.alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); receive_imf( - &t, + t, format!( r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) Subject: =?utf-8?q?single_reply-to?= @@ -2265,12 +2370,12 @@ Message-ID: " "Hello, I've just created the group \"single reply-to\" for us." } ); - let group_chat = Chat::load_from_db(&t, group_msg.chat_id).await.unwrap(); + let group_chat = Chat::load_from_db(t, group_msg.chat_id).await.unwrap(); assert_eq!(group_chat.typ, Chattype::Group); assert_eq!(group_chat.name, "single reply-to"); receive_imf( - &t, + t, format!( r#"Subject: Re: single reply-to To: "Alice" @@ -2299,7 +2404,7 @@ Private reply"#, let private_msg = t.get_last_msg().await; assert_eq!(private_msg.text, "Private reply"); - let private_chat = Chat::load_from_db(&t, private_msg.chat_id).await.unwrap(); + let private_chat = Chat::load_from_db(t, private_msg.chat_id).await.unwrap(); assert_eq!(private_chat.typ, Chattype::Single); assert_ne!(private_msg.chat_id, group_msg.chat_id); } @@ -2402,11 +2507,15 @@ Sent with my Delta Chat Messenger: https://delta.chat #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_chat_assignment_nonprivate_classical_reply() { + let mut tcm = TestContextManager::new(); for outgoing_is_classical in &[true, false] { - let t = TestContext::new_alice().await; + let t = &tcm.alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true) + .await + .unwrap(); receive_imf( - &t, + t, format!( r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) Subject: =?utf-8?q?single_reply-to?= @@ -2442,13 +2551,13 @@ Message-ID: " "Hello, I've just created the group \"single reply-to\" for us." } ); - let group_chat = Chat::load_from_db(&t, group_msg.chat_id).await.unwrap(); + let group_chat = Chat::load_from_db(t, group_msg.chat_id).await.unwrap(); assert_eq!(group_chat.typ, Chattype::Group); assert_eq!(group_chat.name, "single reply-to"); // =============== Receive another outgoing message and check that it is put into the same chat =============== receive_imf( - &t, + t, format!( r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) Subject: Out subj @@ -2473,13 +2582,13 @@ Outgoing reply to all"#, let reply = t.get_last_msg().await; assert_eq!(reply.text, "Out subj – Outgoing reply to all"); - let reply_chat = Chat::load_from_db(&t, reply.chat_id).await.unwrap(); + let reply_chat = Chat::load_from_db(t, reply.chat_id).await.unwrap(); assert_eq!(reply_chat.typ, Chattype::Group); assert_eq!(reply.chat_id, group_msg.chat_id); // =============== Receive an incoming message and check that it is put into the same chat =============== receive_imf( - &t, + t, br#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) Subject: In subj To: "Bob" , "Claire" @@ -2496,7 +2605,7 @@ Reply to all"#, let reply = t.get_last_msg().await; assert_eq!(reply.text, "In subj – Reply to all"); - let reply_chat = Chat::load_from_db(&t, reply.chat_id).await.unwrap(); + let reply_chat = Chat::load_from_db(t, reply.chat_id).await.unwrap(); assert_eq!(reply_chat.typ, Chattype::Group); assert_eq!(reply.chat_id, group_msg.chat_id); } @@ -2658,6 +2767,7 @@ async fn test_read_receipts_dont_unmark_bots() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_gmx_forwarded_msg() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; receive_imf( &t, @@ -3253,6 +3363,9 @@ async fn test_blocked_contact_creates_group() -> Result<()> { async fn test_outgoing_undecryptable() -> Result<()> { let alice = &TestContext::new().await; alice.configure_addr("alice@example.org").await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml"); receive_imf(alice, raw, false).await?; @@ -3289,6 +3402,7 @@ async fn test_outgoing_undecryptable() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_thunderbird_autocrypt() -> Result<()> { let t = TestContext::new_bob().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml"); let received_msg = receive_imf(&t, raw, false).await?.unwrap(); @@ -3336,6 +3450,7 @@ async fn test_issuer_fingerprint() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> { let t = TestContext::new_bob().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; // The message has public key attached *and* Autocrypt header. // @@ -3407,6 +3522,9 @@ async fn test_forged_from_and_no_valid_signatures() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> { let t = &TestContext::new_bob().await; + // TODO: same test, but with ProcessUnencrypted, should trash the message + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; + let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml"); let raw = String::from_utf8(raw.to_vec())?.replace("From: Alice", "From: A"); receive_imf(t, raw.as_bytes(), false).await?.unwrap(); @@ -3421,6 +3539,8 @@ async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> { let bob = &TestContext::new_bob().await; + bob.set_config(Config::ProcessUnencrypted, Some("1")) + .await?; // Thunderbird message with Autocrypt header and a signature, // but not encrypted. @@ -3459,6 +3579,11 @@ async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_thunderbird_unsigned() -> Result<()> { let alice = TestContext::new_alice().await; + // TODO: same test without process unencrypted should trash the message + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await + .unwrap(); // Alice receives an unsigned message from Bob. let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_unsigned.eml"); @@ -3568,6 +3693,7 @@ async fn test_big_forwarded_with_big_attachment() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mua_user_adds_member() -> Result<()> { let t = TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; receive_imf( &t, @@ -3619,6 +3745,9 @@ async fn test_mua_user_adds_member() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mua_user_adds_recipient_to_single_chat() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; // Alice sends a 1:1 message to Bob, creating a 1:1 chat. let msg = receive_imf( @@ -4051,6 +4180,9 @@ async fn test_dont_readd_with_normal_msg() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mua_cant_remove() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let now = time(); @@ -4143,6 +4275,9 @@ async fn test_mua_cant_remove() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mua_can_add() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; let now = time(); @@ -4202,6 +4337,9 @@ async fn test_mua_can_add() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_mua_can_readd() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; // Alice creates chat with 3 contacts. let msg = receive_imf( @@ -4365,6 +4503,10 @@ async fn test_keep_member_list_if_possibly_nomember() -> Result<()> { async fn test_adhoc_grp_name_no_prefix() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; + let chat_id = receive_imf( alice, b"Subject: Re: Once upon a time this was with the only Re: here\n\ @@ -4400,12 +4542,12 @@ async fn test_outgoing_msg_forgery() -> Result<()> { imex(alice, ImexMode::ExportSelfKeys, export_dir.path(), None).await?; // We need Bob only to encrypt the forged message to Alice's key, actually Bob doesn't // participate in the scenario. - let bob = &TestContext::new().await; + let bob = &tcm.unconfigured().await; assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 0); bob.configure_addr("bob@example.net").await; imex(bob, ImexMode::ImportSelfKeys, export_dir.path(), None).await?; assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1); - let malice = &TestContext::new().await; + let malice = &tcm.unconfigured().await; malice.configure_addr(alice_addr).await; let malice_chat_id = tcm @@ -4647,6 +4789,7 @@ async fn test_forged_from() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_multiline_iso_8859_1_subject() -> Result<()> { let t = &TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; let mail = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ From: bob@example.com\n\ To: alice@example.org, claire@example.com\n\ @@ -4711,6 +4854,7 @@ async fn test_references() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_list_from() -> Result<()> { let t = &TestContext::new_alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; let raw = include_bytes!("../../test-data/message/list-from.eml"); let received = receive_imf(t, raw, false).await?.unwrap(); @@ -4840,6 +4984,7 @@ async fn test_make_n_send_vcard() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_unencrypted_group_id_no_recipients() -> Result<()> { let t = &TestContext::new_alice().await; + t.set_config(Config::ProcessUnencrypted, Some("1")).await?; let raw = "From: alice@example.org Subject: Group Chat-Version: 1.0 @@ -5313,6 +5458,9 @@ async fn test_outgoing_unencrypted_chat_assignment() { async fn test_incoming_reply_with_date_in_past() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let msg0 = receive_imf( alice, @@ -5454,6 +5602,11 @@ async fn test_small_unencrypted_group() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; let bob = &tcm.bob().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; + bob.set_config(Config::ProcessUnencrypted, Some("1")) + .await?; let alice_chat_id = chat::create_group_unencrypted(alice, "Unencrypted group").await?; let alice_bob_id = alice.add_or_lookup_address_contact_id(bob).await; @@ -5533,6 +5686,7 @@ async fn test_lookup_key_contact_by_address_self() -> Result<()> { async fn test_calendar_alternative() -> Result<()> { let mut tcm = TestContextManager::new(); let t = &tcm.alice().await; + t.set_config_bool(Config::ProcessUnencrypted, true).await?; let raw = include_bytes!("../../test-data/message/calendar-alternative.eml"); let msg = receive_imf(t, raw, false).await?.unwrap(); assert_eq!(msg.msg_ids.len(), 1); diff --git a/src/securejoin/securejoin_tests.rs b/src/securejoin/securejoin_tests.rs index 330d6716e..ef73bd6b4 100644 --- a/src/securejoin/securejoin_tests.rs +++ b/src/securejoin/securejoin_tests.rs @@ -772,6 +772,9 @@ fn manipulate_qr(v3: bool, remove_invite: bool, qr: &mut String) { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_adhoc_group_no_qr() -> Result<()> { let alice = TestContext::new_alice().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let mime = br#"Subject: First thread Message-ID: first@example.org @@ -1415,6 +1418,9 @@ async fn test_vc_request_encrypted_at_rest() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = &tcm.alice().await; let bob = &tcm.bob().await; + alice + .set_config_bool(Config::ProcessUnencrypted, true) + .await?; let qr = get_securejoin_qr(alice, None).await?; diff --git a/src/tests/verified_chats.rs b/src/tests/verified_chats.rs index 055fe7787..30a9e171c 100644 --- a/src/tests/verified_chats.rs +++ b/src/tests/verified_chats.rs @@ -332,6 +332,9 @@ async fn test_reply() -> Result<()> { let mut tcm = TestContextManager::new(); let alice = tcm.alice().await; let bob = tcm.bob().await; + alice + .set_config(Config::ProcessUnencrypted, Some("1")) + .await?; if verified { mark_as_verified(&alice, &bob).await; diff --git a/src/webxdc/webxdc_tests.rs b/src/webxdc/webxdc_tests.rs index 85c38c851..b1fa17042 100644 --- a/src/webxdc/webxdc_tests.rs +++ b/src/webxdc/webxdc_tests.rs @@ -983,7 +983,7 @@ async fn test_pop_status_update() -> Result<()> { async fn test_draft_and_send_webxdc_status_update() -> Result<()> { let alice = TestContext::new_alice().await; let bob = TestContext::new_bob().await; - let alice_chat_id = alice.create_email_chat(&bob).await.id; + let alice_chat_id = alice.create_chat(&bob).await.id; // prepare webxdc instance, // status updates are not sent for drafts, therefore send_webxdc_status_update() returns Ok(None) @@ -1030,8 +1030,6 @@ async fn test_draft_and_send_webxdc_status_update() -> Result<()> { let bob_instance = bob.recv_msg(&sent1).await; assert_eq!(bob_instance.viewtype, Viewtype::Webxdc); assert_eq!(bob_instance.get_filename().unwrap(), "minimal.xdc"); - assert!(sent1.payload().contains("Content-Type: application/json")); - assert!(sent1.payload().contains("status-update.json")); assert_eq!( bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) .await?, diff --git a/test-data/message/image_4k.eml b/test-data/message/image_4k.eml index 7b2e3e777..3f7d00adc 100644 --- a/test-data/message/image_4k.eml +++ b/test-data/message/image_4k.eml @@ -1,28 +1,27 @@ -Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET) -From: Test Sender -Subject: Large image test -To: alice@example.org -Message-ID: -Date: Thu, 17 Dec 2020 15:38:45 +0100 -User-Agent: Test-Agent/1.0 -MIME-Version: 1.0 -Content-Type: multipart/mixed; - boundary="------------BIG_IMAGE_BOUNDARY" -Content-Language: en-US - -This is a multi-part message in MIME format. ---------------BIG_IMAGE_BOUNDARY -Content-Type: text/plain; charset=utf-8 -Content-Transfer-Encoding: 7bit - -Here is a 4k image - ---------------BIG_IMAGE_BOUNDARY -Content-Type: image/png; - name="4k_image.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="4k_image.png" - -iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg== ---------------BIG_IMAGE_BOUNDARY-- +From: Test Sender +Subject: Large image test +To: alice@example.org +Message-ID: +Date: Thu, 17 Dec 2020 15:38:45 +0100 +User-Agent: Test-Agent/1.0 +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------BIG_IMAGE_BOUNDARY" +Content-Language: en-US + +This is a multi-part message in MIME format. +--------------BIG_IMAGE_BOUNDARY +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit + +Here is a 4k image + +--------------BIG_IMAGE_BOUNDARY +Content-Type: image/png; + name="4k_image.png" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="4k_image.png" + +iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg== +--------------BIG_IMAGE_BOUNDARY--