feat: add option to force encryption

This commit is contained in:
link2xt
2026-05-07 07:10:17 +02:00
parent ceadf60a7a
commit c67dc519cd
33 changed files with 1082 additions and 881 deletions

View File

@@ -487,6 +487,7 @@ char* dc_get_blobdir (const dc_context_t* context);
* 0 = Everybody (except explicitly blocked contacts), * 0 = Everybody (except explicitly blocked contacts),
* 1 = Contacts (default, does not include contact requests), * 1 = Contacts (default, does not include contact requests),
* 2 = Nobody (calls never result in a notification). * 2 = Nobody (calls never result in a notification).
* - `force_encryption` = 1 (default) to force encryption, 0 to allow receiving unencrypted messages.
* *
* Also, there are configs that are only needed * Also, there are configs that are only needed
* if you want to use the deprecated dc_configure() API, such as: * if you want to use the deprecated dc_configure() API, such as:

View File

@@ -4,6 +4,7 @@ use crate::config::Config;
use crate::constants::DC_CHAT_ID_TRASH; use crate::constants::DC_CHAT_ID_TRASH;
use crate::message::MessageState; use crate::message::MessageState;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::test_utils;
use crate::test_utils::{TestContext, TestContextManager}; use crate::test_utils::{TestContext, TestContextManager};
struct CallSetup { struct CallSetup {
@@ -634,20 +635,23 @@ async fn test_forward_call() -> Result<()> {
async fn test_end_text_call() -> Result<()> { async fn test_end_text_call() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let received1 = receive_imf( let encrypted_message = test_utils::encrypt_raw_message(
alice, bob,
b"From: bob@example.net\n\ &[alice],
To: alice@example.org\n\ b"From: bob@example.net\r\n\
Message-ID: <first@example.net>\n\ To: alice@example.org\r\n\
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\ Message-ID: <first@example.net>\r\n\
Chat-Version: 1.0\n\ Date: Sun, 22 Mar 2020 22:37:57 +0000\r\n\
\n\ Chat-Version: 1.0\r\n\
Hello\n", \r\n\
false, Hello\r\n",
) )
.await? .await?;
.unwrap(); let received1 = receive_imf(alice, encrypted_message.as_bytes(), false)
.await?
.unwrap();
assert_eq!(received1.msg_ids.len(), 1); assert_eq!(received1.msg_ids.len(), 1);
let msg = Message::load_from_db(alice, received1.msg_ids[0]) let msg = Message::load_from_db(alice, received1.msg_ids[0])
.await .await
@@ -656,21 +660,23 @@ async fn test_end_text_call() -> Result<()> {
// Receiving "Call ended" message that refers // Receiving "Call ended" message that refers
// to the text message does not result in an error. // to the text message does not result in an error.
let received2 = receive_imf( let encrypted_message2 = test_utils::encrypt_raw_message(
alice, bob,
b"From: bob@example.net\n\ &[alice],
To: alice@example.org\n\ b"From: bob@example.net\r\n\
Message-ID: <second@example.net>\n\ To: alice@example.org\r\n\
Date: Sun, 22 Mar 2020 23:37:57 +0000\n\ Message-ID: <second@example.net>\r\n\
In-Reply-To: <first@example.net>\n\ Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\
Chat-Version: 1.0\n\ In-Reply-To: <first@example.net>\r\n\
Chat-Content: call-ended\n\ Chat-Version: 1.0\r\n\
\n\ Chat-Content: call-ended\r\n\
Call ended\n", \r\n\
false, Call ended\r\n",
) )
.await? .await?;
.unwrap(); let received2 = receive_imf(alice, encrypted_message2.as_bytes(), false)
.await?
.unwrap();
assert_eq!(received2.msg_ids.len(), 1); assert_eq!(received2.msg_ids.len(), 1);
assert_eq!(received2.chat_id, DC_CHAT_ID_TRASH); assert_eq!(received2.chat_id, DC_CHAT_ID_TRASH);

View File

@@ -11,6 +11,7 @@ use crate::message::{Message, MessengerMessage, delete_msgs};
use crate::mimeparser::{self, MimeMessage}; use crate::mimeparser::{self, MimeMessage};
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::securejoin::{get_securejoin_qr, join_securejoin}; use crate::securejoin::{get_securejoin_qr, join_securejoin};
use crate::test_utils;
use crate::test_utils::{ use crate::test_utils::{
AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, E2EE_INFO_MSGS, TestContext, TestContextManager, AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, E2EE_INFO_MSGS, TestContext, TestContextManager,
TimeShiftFalsePositiveNote, sync, TimeShiftFalsePositiveNote, sync,
@@ -1049,6 +1050,7 @@ async fn chatlist_len(ctx: &Context, listflags: usize) -> usize {
async fn test_archive() { async fn test_archive() {
// create two chats // create two chats
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let mut msg = Message::new_text("foo".to_string()); let mut msg = Message::new_text("foo".to_string());
let msg_id = add_device_msg(&t, None, Some(&mut msg)).await.unwrap(); let msg_id = add_device_msg(&t, None, Some(&mut msg)).await.unwrap();
let chat_id1 = message::Message::load_from_db(&t, msg_id) let chat_id1 = message::Message::load_from_db(&t, msg_id)
@@ -1159,46 +1161,51 @@ async fn test_archive() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_unarchive_if_muted() -> Result<()> { 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<()> { let msg_from_bob = async |num: u32| {
receive_imf( let encrypted_message = test_utils::encrypt_raw_message(
t, bob,
&[t],
format!( format!(
"From: bob@example.net\n\ "From: bob@example.net\r\n\
To: alice@example.org\n\ To: alice@example.org\r\n\
Message-ID: <{num}@example.org>\n\ Message-ID: <{num}@example.org>\r\n\
Chat-Version: 1.0\n\ Chat-Version: 1.0\r\n\
Date: Sun, 22 Mar 2022 19:37:57 +0000\n\ Date: Sun, 22 Mar 2022 19:37:57 +0000\r\n\
\n\ \r\n\
hello\n" hello\r\n"
) )
.as_bytes(), .as_bytes(),
false,
) )
.await?; .await
Ok(()) .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(); let chat_id = t.get_last_msg().await.get_chat_id();
chat_id.accept(&t).await?; chat_id.accept(t).await?;
chat_id.set_visibility(&t, ChatVisibility::Archived).await?; chat_id.set_visibility(t, ChatVisibility::Archived).await?;
assert_eq!(get_archived_cnt(&t).await?, 1); assert_eq!(get_archived_cnt(t).await?, 1);
// not muted chat is unarchived on receiving a message // not muted chat is unarchived on receiving a message
msg_from_bob(&t, 2).await?; msg_from_bob(2).await;
assert_eq!(get_archived_cnt(&t).await?, 0); assert_eq!(get_archived_cnt(t).await?, 0);
// forever muted chat is not unarchived on receiving a message // forever muted chat is not unarchived on receiving a message
chat_id.set_visibility(&t, ChatVisibility::Archived).await?; chat_id.set_visibility(t, ChatVisibility::Archived).await?;
set_muted(&t, chat_id, MuteDuration::Forever).await?; set_muted(t, chat_id, MuteDuration::Forever).await?;
msg_from_bob(&t, 3).await?; msg_from_bob(3).await;
assert_eq!(get_archived_cnt(&t).await?, 1); assert_eq!(get_archived_cnt(t).await?, 1);
// otherwise muted chat is not unarchived on receiving a message // otherwise muted chat is not unarchived on receiving a message
set_muted( set_muted(
&t, t,
chat_id, chat_id,
MuteDuration::Until( MuteDuration::Until(
SystemTime::now() SystemTime::now()
@@ -1207,12 +1214,12 @@ async fn test_unarchive_if_muted() -> Result<()> {
), ),
) )
.await?; .await?;
msg_from_bob(&t, 4).await?; msg_from_bob(4).await;
assert_eq!(get_archived_cnt(&t).await?, 1); assert_eq!(get_archived_cnt(t).await?, 1);
// expired mute will unarchive the chat // expired mute will unarchive the chat
set_muted( set_muted(
&t, t,
chat_id, chat_id,
MuteDuration::Until( MuteDuration::Until(
SystemTime::now() SystemTime::now()
@@ -1221,20 +1228,20 @@ async fn test_unarchive_if_muted() -> Result<()> {
), ),
) )
.await?; .await?;
msg_from_bob(&t, 5).await?; msg_from_bob(5).await;
assert_eq!(get_archived_cnt(&t).await?, 0); assert_eq!(get_archived_cnt(t).await?, 0);
// no unarchiving on sending to muted chat or on adding info messages to muted chat // no unarchiving on sending to muted chat or on adding info messages to muted chat
chat_id.set_visibility(&t, ChatVisibility::Archived).await?; chat_id.set_visibility(t, ChatVisibility::Archived).await?;
set_muted(&t, chat_id, MuteDuration::Forever).await?; set_muted(t, chat_id, MuteDuration::Forever).await?;
send_text_msg(&t, chat_id, "out".to_string()).await?; send_text_msg(t, chat_id, "out".to_string()).await?;
add_info_msg(&t, chat_id, "info").await?; add_info_msg(t, chat_id, "info").await?;
assert_eq!(get_archived_cnt(&t).await?, 1); assert_eq!(get_archived_cnt(t).await?, 1);
// finally, unarchive on sending to not muted chat // finally, unarchive on sending to not muted chat
set_muted(&t, chat_id, MuteDuration::NotMuted).await?; set_muted(t, chat_id, MuteDuration::NotMuted).await?;
send_text_msg(&t, chat_id, "out2".to_string()).await?; send_text_msg(t, chat_id, "out2".to_string()).await?;
assert_eq!(get_archived_cnt(&t).await?, 0); assert_eq!(get_archived_cnt(t).await?, 0);
Ok(()) Ok(())
} }
@@ -1381,6 +1388,9 @@ async fn test_markfresh_chat() -> Result<()> {
async fn test_archive_fresh_msgs() -> Result<()> { async fn test_archive_fresh_msgs() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
// FIXME: use encrypted messages
t.allow_unencrypted().await?;
async fn msg_from(t: &TestContext, name: &str, num: u32) -> Result<()> { async fn msg_from(t: &TestContext, name: &str, num: u32) -> Result<()> {
receive_imf( receive_imf(
t, t,
@@ -1873,45 +1883,38 @@ async fn test_lookup_self_by_contact_id() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_marknoticed_chat() -> Result<()> { async fn test_marknoticed_chat() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let chat = t.create_chat_with_contact("bob", "bob@example.org").await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let chat = alice.create_chat(bob).await;
receive_imf( let bob_chat_id = bob.create_chat_id(alice).await;
&t, let sent = bob.send_text(bob_chat_id, "hello").await;
b"From: bob@example.org\n\ alice.recv_msg(&sent).await;
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 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.len(), 1);
assert_eq!(chats.get_chat_id(0)?, chat.id); assert_eq!(chats.get_chat_id(0)?, chat.id);
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 1); assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 1);
assert_eq!(t.get_fresh_msgs().await?.len(), 1); assert_eq!(alice.get_fresh_msgs().await?.len(), 1);
let msgs = get_chat_msgs(&t, chat.id).await?; let msgs = get_chat_msgs(alice, chat.id).await?;
assert_eq!(msgs.len(), 1); assert_eq!(msgs.len(), 2);
let msg_id = match msgs.first().unwrap() { let msg_id = match msgs.last().unwrap() {
ChatItem::Message { msg_id } => *msg_id, ChatItem::Message { msg_id } => *msg_id,
_ => MsgId::new_unset(), _ => 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); 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); 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!(msg.state, MessageState::InNoticed);
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 0); assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 0);
assert_eq!(t.get_fresh_msgs().await?.len(), 0); assert_eq!(alice.get_fresh_msgs().await?.len(), 0);
Ok(()) Ok(())
} }
@@ -1919,6 +1922,7 @@ async fn test_marknoticed_chat() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_request_fresh_messages() -> Result<()> { async fn test_contact_request_fresh_messages() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
let chats = Chatlist::try_load(&t, 0, None, None).await?; let chats = Chatlist::try_load(&t, 0, None, None).await?;
assert_eq!(chats.len(), 0); 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_request_archive() -> Result<()> { 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( let bob_chat_id = bob.create_chat_id(alice).await;
&t, let bob_sent_text = bob.send_text(bob_chat_id, "hello").await;
b"From: bob@example.org\n\ alice.recv_msg(&bob_sent_text).await;
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 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.len(), 1);
let chat_id = chats.get_chat_id(0)?; let chat_id = chats.get_chat_id(0)?;
assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request()); assert!(
assert_eq!(get_archived_cnt(&t).await?, 0); 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 // 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); assert_eq!(chats.len(), 1);
let chat_id = chats.get_chat_id(0)?; let chat_id = chats.get_chat_id(0)?;
assert!(chat_id.is_archived_link()); 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); assert_eq!(chats.len(), 1);
let chat_id = chats.get_chat_id(0)?; 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(()) Ok(())
} }
@@ -2011,6 +2018,7 @@ async fn test_contact_request_archive() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_classic_email_chat() -> Result<()> { async fn test_classic_email_chat() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
// Alice receives a classic (non-chat) message from Bob. // Alice receives a classic (non-chat) message from Bob.
receive_imf( receive_imf(
@@ -4695,10 +4703,12 @@ async fn test_sync_delete_chat() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sync_adhoc_grp() -> Result<()> { async fn test_sync_adhoc_grp() -> Result<()> {
let alice0 = &TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice1 = &TestContext::new_alice().await; let alice0 = &tcm.alice().await;
let alice1 = &tcm.alice().await;
for a in [alice0, alice1] { for a in [alice0, alice1] {
a.set_config_bool(Config::SyncMsgs, true).await?; a.set_config_bool(Config::SyncMsgs, true).await?;
a.allow_unencrypted().await?;
} }
let mut chat_ids = Vec::new(); let mut chat_ids = Vec::new();
@@ -6070,6 +6080,7 @@ async fn test_no_key_contacts_in_adhoc_chats() -> Result<()> {
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
let charlie = &tcm.charlie().await; let charlie = &tcm.charlie().await;
alice.allow_unencrypted().await?;
let chat_id = receive_imf( let chat_id = receive_imf(
alice, alice,

View File

@@ -665,6 +665,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_single_chat() -> anyhow::Result<()> { async fn test_search_single_chat() -> anyhow::Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
// receive a one-to-one-message // receive a one-to-one-message
receive_imf( receive_imf(
@@ -725,6 +726,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_single_chat_without_authname() -> anyhow::Result<()> { async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
// receive a one-to-one-message without authname set // receive a one-to-one-message without authname set
receive_imf( receive_imf(

View File

@@ -486,6 +486,12 @@ pub enum Config {
/// Experimental option denoting that the current profile is shared between multiple team members. /// 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. /// For now, the only effect of this option is that seen flags are not synchronized.
TeamProfile, TeamProfile,
/// Process unencrypted messages.
///
/// Unencrypted messages are fetched and processed only if this setting is explicitly enabled.
#[strum(props(default = "1"))]
ForceEncryption,
} }
impl Config { impl Config {

View File

@@ -335,6 +335,7 @@ async fn test_add_or_lookup() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_name_changes() -> Result<()> { async fn test_contact_name_changes() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
// first message creates contact and one-to-one-chat without name set // first message creates contact and one-to-one-chat without name set
receive_imf( receive_imf(
@@ -927,9 +928,12 @@ async fn test_synchronize_status() -> Result<()> {
// Alice has two devices. // Alice has two devices.
let alice1 = &tcm.alice().await; let alice1 = &tcm.alice().await;
let alice2 = &tcm.alice().await; let alice2 = &tcm.alice().await;
alice1.allow_unencrypted().await?;
alice2.allow_unencrypted().await?;
// Bob has one device. // Bob has one device.
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
bob.allow_unencrypted().await?;
let default_status = alice1.get_config(Config::Selfstatus).await?; let default_status = alice1.get_config(Config::Selfstatus).await?;
@@ -998,33 +1002,18 @@ async fn test_selfavatar_changed_event() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_last_seen() -> Result<()> { 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( let contact = alice.add_or_lookup_contact(bob).await;
&alice,
"Bob",
&ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?;
let contact = Contact::get_by_id(&alice, contact_id).await?;
assert_eq!(contact.last_seen(), 0); assert_eq!(contact.last_seen(), 0);
let mime = br#"Subject: Hello let msg = tcm.send_recv(bob, alice, "Hi.").await;
Message-ID: message@example.net
To: Alice <alice@example.org>
From: Bob <bob@example.net>
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 timestamp = msg.get_timestamp(); let timestamp = msg.get_timestamp();
assert!(timestamp > 0); 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); assert_eq!(contact.last_seen(), timestamp);
Ok(()) Ok(())
@@ -1098,6 +1087,7 @@ async fn test_was_seen_recently_event() -> Result<()> {
async fn test_lookup_id_by_addr_recent_ex(accept_unencrypted_chat: bool) -> Result<()> { async fn test_lookup_id_by_addr_recent_ex(accept_unencrypted_chat: bool) -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
bob.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml"); 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")); assert!(std::str::from_utf8(raw)?.contains("Date: Thu, 24 Nov 2022 20:05:57 +0100"));

View File

@@ -1056,6 +1056,12 @@ impl Context {
"team_profile", "team_profile",
self.get_config_bool(Config::TeamProfile).await?.to_string(), self.get_config_bool(Config::TeamProfile).await?.to_string(),
); );
res.insert(
"force_encryption",
self.get_config_bool(Config::ForceEncryption)
.await?
.to_string(),
);
let elapsed = time_elapsed(&self.creation_time); let elapsed = time_elapsed(&self.creation_time);
res.insert("uptime", duration_to_str(elapsed)); res.insert("uptime", duration_to_str(elapsed));

View File

@@ -8,7 +8,7 @@ use crate::chatlist::Chatlist;
use crate::constants::Chattype; use crate::constants::Chattype;
use crate::message::Message; use crate::message::Message;
use crate::receive_imf::receive_imf; 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}; use crate::tools::{SystemTime, create_outgoing_rfc724_mid};
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -54,6 +54,7 @@ async fn receive_msg(t: &TestContext, chat: &Chat) {
async fn test_get_fresh_msgs_and_muted_chats() { async fn test_get_fresh_msgs_and_muted_chats() {
// receive various mails in 3 chats // receive various mails in 3 chats
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let bob = t.create_chat_with_contact("", "bob@g.it").await; let bob = t.create_chat_with_contact("", "bob@g.it").await;
let claire = t.create_chat_with_contact("", "claire@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; let dave = t.create_chat_with_contact("", "dave@g.it").await;
@@ -103,6 +104,7 @@ async fn test_get_fresh_msgs_and_muted_chats() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_fresh_msgs_and_muted_until() { async fn test_get_fresh_msgs_and_muted_until() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let bob = t.create_chat_with_contact("", "bob@g.it").await; let bob = t.create_chat_with_contact("", "bob@g.it").await;
receive_msg(&t, &bob).await; receive_msg(&t, &bob).await;
assert_eq!(get_chat_msgs(&t, bob.id).await.unwrap().len(), 1); assert_eq!(get_chat_msgs(&t, bob.id).await.unwrap().len(), 1);
@@ -158,6 +160,7 @@ async fn test_get_fresh_msgs_and_muted_until() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_muted_context() -> Result<()> { async fn test_muted_context() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 0); assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 0);
t.set_config(Config::IsMuted, Some("1")).await?; t.set_config(Config::IsMuted, Some("1")).await?;
let chat = t.create_chat_with_contact("", "bob@g.it").await; let chat = t.create_chat_with_contact("", "bob@g.it").await;
@@ -385,49 +388,40 @@ async fn test_search_msgs() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_unaccepted_requests() -> Result<()> { async fn test_search_unaccepted_requests() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
receive_imf( let t = &tcm.alice().await;
&t, let bob = &tcm.bob().await;
b"From: BobBar <bob@example.org>\n\ bob.set_config(Config::Displayname, Some("BobBar")).await?;
To: alice@example.org\n\ let msg = tcm.send_recv(bob, t, "hello bob, foobar test!").await;
Subject: foo\n\ let chat_id = msg.get_chat_id();
Message-ID: <msg1234@example.org>\n\ let chat = Chat::load_from_db(t, chat_id).await?;
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 chat = Chat::load_from_db(&t, chat_id).await?;
assert_eq!(chat.get_type(), Chattype::Single); assert_eq!(chat.get_type(), Chattype::Single);
assert!(chat.is_contact_request()); assert!(chat.is_contact_request());
assert_eq!(Chatlist::try_load(t, 0, None, None).await?.len(), 1);
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 1);
assert_eq!( assert_eq!(
Chatlist::try_load(&t, 0, Some("BobBar"), None).await?.len(), Chatlist::try_load(t, 0, Some("BobBar"), None).await?.len(),
1 1
); );
assert_eq!(t.search_msgs(None, "foobar").await?.len(), 1); assert_eq!(t.search_msgs(None, "foobar").await?.len(), 1);
assert_eq!(t.search_msgs(Some(chat_id), "foobar").await?.len(), 1); assert_eq!(t.search_msgs(Some(chat_id), "foobar").await?.len(), 1);
chat_id.block(&t).await?; chat_id.block(t).await?;
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 0); assert_eq!(Chatlist::try_load(t, 0, None, None).await?.len(), 0);
assert_eq!( assert_eq!(
Chatlist::try_load(&t, 0, Some("BobBar"), None).await?.len(), Chatlist::try_load(t, 0, Some("BobBar"), None).await?.len(),
0 0
); );
assert_eq!(t.search_msgs(None, "foobar").await?.len(), 0); assert_eq!(t.search_msgs(None, "foobar").await?.len(), 0);
assert_eq!(t.search_msgs(Some(chat_id), "foobar").await?.len(), 0); assert_eq!(t.search_msgs(Some(chat_id), "foobar").await?.len(), 0);
let contact_ids = get_chat_contacts(&t, chat_id).await?; let contact_ids = get_chat_contacts(t, chat_id).await?;
Contact::unblock(&t, *contact_ids.first().unwrap()).await?; Contact::unblock(t, *contact_ids.first().unwrap()).await?;
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 1); assert_eq!(Chatlist::try_load(t, 0, None, None).await?.len(), 1);
assert_eq!( assert_eq!(
Chatlist::try_load(&t, 0, Some("BobBar"), None).await?.len(), Chatlist::try_load(t, 0, Some("BobBar"), None).await?.len(),
1 1
); );
assert_eq!(t.search_msgs(None, "foobar").await?.len(), 1); assert_eq!(t.search_msgs(None, "foobar").await?.len(), 1);

View File

@@ -402,6 +402,7 @@ mod tests {
assert!(get_attachment_mime(&mail).is_some()); assert!(get_attachment_mime(&mail).is_some());
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
bob.allow_unencrypted().await?;
receive_imf(&bob, attachment_mime, false).await?; receive_imf(&bob, attachment_mime, false).await?;
let msg = bob.get_last_msg().await; let msg = bob.get_last_msg().await;
// Subject should be prepended because the attachment doesn't have "Chat-Version". // Subject should be prepended because the attachment doesn't have "Chat-Version".
@@ -416,6 +417,7 @@ mod tests {
// Desktop via MS Exchange (actually made with TB though). // Desktop via MS Exchange (actually made with TB though).
let mixed_up_mime = include_bytes!("../test-data/message/mixed-up-long.eml"); let mixed_up_mime = include_bytes!("../test-data/message/mixed-up-long.eml");
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
bob.allow_unencrypted().await?;
receive_imf(&bob, mixed_up_mime, false).await?; receive_imf(&bob, mixed_up_mime, false).await?;
let msg = bob.get_last_msg().await; let msg = bob.get_last_msg().await;
assert!(!msg.get_text().is_empty()); assert!(!msg.get_text().is_empty());

View File

@@ -160,6 +160,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
bob.set_config_bool(Config::IsChatmail, true).await?; bob.set_config_bool(Config::IsChatmail, true).await?;
bob.allow_unencrypted().await?;
let bob_chat_id = receive_imf( let bob_chat_id = receive_imf(
bob, bob,
b"From: alice@example.org\n\ b"From: alice@example.org\n\

View File

@@ -9,6 +9,7 @@ use crate::download::DownloadState;
use crate::location; use crate::location;
use crate::message::markseen_msgs; use crate::message::markseen_msgs;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::test_utils;
use crate::test_utils::{TestContext, TestContextManager}; use crate::test_utils::{TestContext, TestContextManager};
use crate::timesmearing::MAX_SECONDS_TO_LEND_FROM_FUTURE; use crate::timesmearing::MAX_SECONDS_TO_LEND_FROM_FUTURE;
use crate::{ use crate::{
@@ -557,50 +558,54 @@ async fn test_delete_expired_imap_messages() -> Result<()> {
// Regression test for a bug in the timer rollback protection. // Regression test for a bug in the timer rollback protection.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ephemeral_timer_references() -> Result<()> { 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 <first@example.com> and no timer is received. // Message with Message-ID <first@example.com> and no timer is received.
receive_imf( let encrypted_msg = test_utils::encrypt_raw_message(
&alice, bob,
b"From: Bob <bob@example.com>\n\ &[alice],
To: Alice <alice@example.org>\n\ b"From: Bob <bob@example.net>\r\n\
Chat-Version: 1.0\n\ To: Alice <alice@example.org>\r\n\
Subject: Subject\n\ Chat-Version: 1.0\r\n\
Message-ID: <first@example.com>\n\ Subject: Subject\r\n\
Date: Sun, 22 Mar 2020 00:10:00 +0000\n\ Message-ID: <first@example.com>\r\n\
\n\ Date: Sun, 22 Mar 2020 00:10:00 +0000\r\n\
hello\n", \r\n\
false, hello\r\n",
) )
.await?; .await?;
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
let chat_id = msg.chat_id; let chat_id = msg.chat_id;
assert_eq!(chat_id.get_ephemeral_timer(&alice).await?, Timer::Disabled); assert_eq!(chat_id.get_ephemeral_timer(alice).await?, Timer::Disabled);
// Message with Message-ID <second@example.com> is received. // Message with Message-ID <second@example.com> is received.
receive_imf( let encrypted_msg = test_utils::encrypt_raw_message(
&alice, bob,
b"From: Bob <bob@example.com>\n\ &[alice],
To: Alice <alice@example.org>\n\ b"From: Bob <bob@example.net>\r\n\
Chat-Version: 1.0\n\ To: Alice <alice@example.org>\r\n\
Subject: Subject\n\ Chat-Version: 1.0\r\n\
Message-ID: <second@example.com>\n\ Subject: Subject\r\n\
Date: Sun, 22 Mar 2020 00:11:00 +0000\n\ Message-ID: <second@example.com>\r\n\
Ephemeral-Timer: 60\n\ Date: Sun, 22 Mar 2020 00:11:00 +0000\r\n\
\n\ Ephemeral-Timer: 60\r\n\
second message\n", \r\n\
false, second message\r\n",
) )
.await?; .await?;
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
assert_eq!( assert_eq!(
chat_id.get_ephemeral_timer(&alice).await?, chat_id.get_ephemeral_timer(alice).await?,
Timer::Enabled { duration: 60 } Timer::Enabled { duration: 60 }
); );
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
// Message is deleted when its timer expires. // Message is deleted when its timer expires.
msg.id.trash(&alice, false).await?; msg.id.trash(alice, false).await?;
// Message with Message-ID <third@example.com>, referencing <first@example.com> and // Message with Message-ID <third@example.com>, referencing <first@example.com> and
// <second@example.com>, is received. The message <second@example.come> is not in the // <second@example.com>, is received. The message <second@example.come> is not in the
@@ -614,25 +619,26 @@ async fn test_ephemeral_timer_references() -> Result<()> {
// //
// The message also contains a quote of the first message to test that only References: // 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. // header and not In-Reply-To: is consulted by the rollback protection.
receive_imf( let encrypted_msg = test_utils::encrypt_raw_message(
&alice, bob,
b"From: Bob <bob@example.com>\n\ &[alice],
To: Alice <alice@example.org>\n\ b"From: Bob <bob@example.net>\r\n\
Chat-Version: 1.0\n\ To: Alice <alice@example.org>\r\n\
Subject: Subject\n\ Chat-Version: 1.0\r\n\
Message-ID: <third@example.com>\n\ Subject: Subject\r\n\
Date: Sun, 22 Mar 2020 00:12:00 +0000\n\ Message-ID: <third@example.com>\r\n\
References: <first@example.com> <second@example.com>\n\ Date: Sun, 22 Mar 2020 00:12:00 +0000\r\n\
In-Reply-To: <first@example.com>\n\ References: <first@example.com> <second@example.com>\r\n\
\n\ In-Reply-To: <first@example.com>\r\n\
> hello\n", \r\n\
false, > hello\r\n",
) )
.await?; .await?;
receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!( assert_eq!(
msg.chat_id.get_ephemeral_timer(&alice).await?, msg.chat_id.get_ephemeral_timer(alice).await?,
Timer::Disabled Timer::Disabled
); );

View File

@@ -521,6 +521,7 @@ mod test_chatlist_events {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group() -> Result<()> { async fn test_adhoc_group() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
let mime = br#"Subject: First thread let mime = br#"Subject: First thread
Message-ID: first@example.org Message-ID: first@example.org
To: Alice <alice@example.org>, Bob <bob@example.net> To: Alice <alice@example.org>, Bob <bob@example.net>

View File

@@ -287,6 +287,7 @@ impl MsgId {
mod tests { mod tests {
use super::*; use super::*;
use crate::chat::{self, Chat, forward_msgs, save_msgs}; use crate::chat::{self, Chat, forward_msgs, save_msgs};
use crate::constants; use crate::constants;
use crate::contact::ContactId; use crate::contact::ContactId;
use crate::message::{MessengerMessage, Viewtype}; use crate::message::{MessengerMessage, Viewtype};
@@ -450,6 +451,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
// alice receives a non-delta html-message // alice receives a non-delta html-message
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
let chat = alice let chat = alice
.create_chat_with_contact("", "sender@testrun.org") .create_chat_with_contact("", "sender@testrun.org")
.await; .await;
@@ -483,6 +485,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
// bob: check that bob also got the html-part of the forwarded message // bob: check that bob also got the html-part of the forwarded message
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
bob.allow_unencrypted().await?;
let chat_bob = bob.create_chat_with_contact("", "alice@example.org").await; let chat_bob = bob.create_chat_with_contact("", "alice@example.org").await;
async fn check_receiver(ctx: &TestContext, chat: &Chat, sender: &TestContext) { async fn check_receiver(ctx: &TestContext, chat: &Chat, sender: &TestContext) {
let msg = ctx.recv_msg(&sender.pop_sent_msg().await).await; let msg = ctx.recv_msg(&sender.pop_sent_msg().await).await;
@@ -520,31 +523,30 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_save_msg() -> Result<()> { async fn test_html_save_msg() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
// Alice receives a non-delta html-message // Alice receives a non-delta html-message
let alice = TestContext::new_alice().await;
let chat = alice let chat = alice
.create_chat_with_contact("", "sender@testrun.org") .create_chat_with_contact("", "sender@testrun.org")
.await; .await;
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml"); 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; let msg = alice.get_last_msg_in(chat.get_id()).await;
// Alice saves the message // Alice saves the message
let self_chat = alice.get_self_chat().await; 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; let saved_msg = alice.get_last_msg_in(self_chat.get_id()).await;
assert_ne!(saved_msg.id, msg.id); assert_ne!(saved_msg.id, msg.id);
assert_eq!( assert_eq!(saved_msg.get_original_msg_id(alice).await?.unwrap(), msg.id);
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!(!saved_msg.is_forwarded()); // UI should not flag "saved messages" as "forwarded"
assert_ne!(saved_msg.get_from_id(), ContactId::SELF); assert_ne!(saved_msg.get_from_id(), ContactId::SELF);
assert_eq!(saved_msg.get_from_id(), msg.get_from_id()); assert_eq!(saved_msg.get_from_id(), msg.get_from_id());
assert_eq!(saved_msg.is_dc_message, MessengerMessage::No); assert_eq!(saved_msg.is_dc_message, MessengerMessage::No);
assert!(saved_msg.get_text().contains("this is plain")); assert!(saved_msg.get_text().contains("this is plain"));
assert!(saved_msg.has_html()); 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 <b>html</b>")); assert!(html.contains("this is <b>html</b>"));
Ok(()) Ok(())
@@ -555,6 +557,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
// Alice receives a non-delta html-message // Alice receives a non-delta html-message
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await.unwrap();
let chat = alice let chat = alice
.create_chat_with_contact("", "sender@testrun.org") .create_chat_with_contact("", "sender@testrun.org")
.await; .await;
@@ -618,18 +621,20 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cp1252_html() -> Result<()> { async fn test_cp1252_html() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, alice,
include_bytes!("../test-data/message/cp1252-html.eml"), include_bytes!("../test-data/message/cp1252-html.eml"),
false, false,
) )
.await?; .await?;
let msg = t.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.viewtype, Viewtype::Text); assert_eq!(msg.viewtype, Viewtype::Text);
assert!(msg.text.contains("foo bar ä ö ü ß")); assert!(msg.text.contains("foo bar ä ö ü ß"));
assert!(msg.has_html()); 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}"); println!("{html}");
assert!(html.contains("foo bar ä ö ü ß")); assert!(html.contains("foo bar ä ö ü ß"));
Ok(()) Ok(())

View File

@@ -1994,12 +1994,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=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()) // (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) { if flags.any(|f| f == Flag::Draft) {
info!(context, "Ignoring draft message"); info!(context, "Ignoring draft message");
return Ok(false); return Ok(false);
} }
let should_download = !blocked_contact || maybe_ndn; let should_download = maybe_ndn
|| (!blocked_contact
&& (is_encrypted || !context.get_config_bool(Config::ForceEncryption).await?));
Ok(should_download) Ok(should_download)
} }

View File

@@ -21,6 +21,7 @@ const PREFETCH_FLAGS: &str = "(UID RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\
DATE \ DATE \
X-MICROSOFT-ORIGINAL-MESSAGE-ID \ X-MICROSOFT-ORIGINAL-MESSAGE-ID \
FROM \ FROM \
CONTENT-TYPE \
CHAT-VERSION \ CHAT-VERSION \
CHAT-IS-POST-MESSAGE \ CHAT-IS-POST-MESSAGE \
AUTOCRYPT-SETUP-MESSAGE\ AUTOCRYPT-SETUP-MESSAGE\

View File

@@ -998,7 +998,7 @@ Content-Disposition: attachment; filename="location.kml"
let received_msg2 = alice.get_last_msg().await; let received_msg2 = alice.get_last_msg().await;
assert_eq!(received_msg2.id, received_msg.id); 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); assert_eq!(locations.len(), 1);
Ok(()) 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. /// Tests that `location.kml` is not hidden and not seen if it contains a message.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn receive_visible_location_kml() -> Result<()> { 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( let encrypted_message = test_utils::encrypt_raw_message(
&alice, bob,
&[alice],
br#"Subject: locations br#"Subject: locations
MIME-Version: 1.0 MIME-Version: 1.0
To: <alice@example.org> To: <alice@example.org>
@@ -1037,16 +1040,15 @@ Content-Disposition: attachment; filename="location.kml"
</Document> </Document>
</kml> </kml>
--U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#, --U8BOG8qNXfB0GgLiQ3PKUjlvdIuLRF--"#).await?;
false,
) receive_imf(alice, encrypted_message.as_bytes(), false).await?;
.await?;
let received_msg = alice.get_last_msg().await; let received_msg = alice.get_last_msg().await;
assert_eq!(received_msg.text, "Text message."); assert_eq!(received_msg.text, "Text message.");
assert_eq!(received_msg.state, MessageState::InFresh); assert_eq!(received_msg.state, MessageState::InFresh);
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); assert_eq!(locations.len(), 1);
Ok(()) Ok(())
} }

View File

@@ -111,6 +111,7 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> {
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await?;
tcm.section("Bob sends encrypted message to Alice"); tcm.section("Bob sends encrypted message to Alice");
let alice_chat = alice.create_chat(bob).await; let alice_chat = alice.create_chat(bob).await;
@@ -139,23 +140,14 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_chat_id() { async fn test_get_chat_id() {
// Alice receives a message that pops up as a contact request // Alice receives a message that pops up as a contact request
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
receive_imf( let alice = &tcm.alice().await;
&alice, let bob = &tcm.bob().await;
b"From: Bob <bob@example.com>\n\ let chat_id = bob.create_chat_id(alice).await;
To: alice@example.org\n\ let sent = bob.send_text(chat_id, "hello").await;
Chat-Version: 1.0\n\ let msg = alice.recv_msg(&sent).await;
Message-ID: <123@example.com>\n\
Date: Fri, 29 Jan 2021 21:37:55 +0000\n\
\n\
hello\n",
false,
)
.await
.unwrap();
// check chat-id of this message // check chat-id of this message
let msg = alice.get_last_msg().await;
assert!(!msg.get_chat_id().is_special()); assert!(!msg.get_chat_id().is_special());
assert_eq!(msg.get_text(), "hello".to_string()); assert_eq!(msg.get_text(), "hello".to_string());
} }
@@ -465,7 +457,9 @@ async fn test_get_state() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_is_bot() -> Result<()> { async fn test_is_bot() -> Result<()> {
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
// Alice receives an auto-generated non-chat message. // Alice receives an auto-generated non-chat message.
// //
@@ -473,7 +467,7 @@ async fn test_is_bot() -> Result<()> {
// in which case the message should be marked as bot-generated, // in which case the message should be marked as bot-generated,
// but the contact should not. // but the contact should not.
receive_imf( receive_imf(
&alice, alice,
b"From: Claire <claire@example.com>\n\ b"From: Claire <claire@example.com>\n\
To: alice@example.org\n\ To: alice@example.org\n\
Message-ID: <789@example.com>\n\ Message-ID: <789@example.com>\n\
@@ -487,12 +481,12 @@ async fn test_is_bot() -> Result<()> {
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.get_text(), "hello".to_string()); assert_eq!(msg.get_text(), "hello".to_string());
assert!(msg.is_bot()); 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()); assert!(!contact.is_bot());
// Alice receives a message from Bob the bot. // Alice receives a message from Bob the bot.
receive_imf( receive_imf(
&alice, alice,
b"From: Bob <bob@example.com>\n\ b"From: Bob <bob@example.com>\n\
To: alice@example.org\n\ To: alice@example.org\n\
Chat-Version: 1.0\n\ Chat-Version: 1.0\n\
@@ -507,12 +501,12 @@ async fn test_is_bot() -> Result<()> {
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.get_text(), "hello".to_string()); assert_eq!(msg.get_text(), "hello".to_string());
assert!(msg.is_bot()); 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()); assert!(contact.is_bot());
// Alice receives a message from Bob who is not the bot anymore. // Alice receives a message from Bob who is not the bot anymore.
receive_imf( receive_imf(
&alice, alice,
b"From: Bob <bob@example.com>\n\ b"From: Bob <bob@example.com>\n\
To: alice@example.org\n\ To: alice@example.org\n\
Chat-Version: 1.0\n\ Chat-Version: 1.0\n\
@@ -526,7 +520,7 @@ async fn test_is_bot() -> Result<()> {
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.get_text(), "hello again".to_string()); assert_eq!(msg.get_text(), "hello again".to_string());
assert!(!msg.is_bot()); 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()); assert!(!contact.is_bot());
Ok(()) Ok(())

View File

@@ -19,6 +19,7 @@ use crate::headerdef::HeaderDef;
use crate::message; use crate::message;
use crate::mimeparser::MimeMessage; use crate::mimeparser::MimeMessage;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::test_utils;
use crate::test_utils::{TestContext, TestContextManager, get_chat_msg}; use crate::test_utils::{TestContext, TestContextManager, get_chat_msg};
use crate::tools::SystemTime; use crate::tools::SystemTime;
@@ -132,14 +133,13 @@ async fn test_subject_from_mua() {
// 1.: Receive a mail from an MUA // 1.: Receive a mail from an MUA
assert_eq!( assert_eq!(
msg_to_subject_str( msg_to_subject_str(
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ b"From: Bob <bob@example.net>\r\n\
From: Bob <bob@example.com>\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: Antw: Chat: hello\r\n\
Subject: Antw: Chat: hello\n\ Message-ID: <2222@example.net>\r\n\
Message-ID: <2222@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n"
hello\n"
) )
.await, .await,
"Re: Chat: hello" "Re: Chat: hello"
@@ -147,14 +147,13 @@ async fn test_subject_from_mua() {
assert_eq!( assert_eq!(
msg_to_subject_str( msg_to_subject_str(
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ b"From: Bob <bob@example.net>\r\n\
From: Bob <bob@example.com>\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: Infos: 42\r\n\
Subject: Infos: 42\n\ Message-ID: <2222@example.net>\r\n\
Message-ID: <2222@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n"
hello\n"
) )
.await, .await,
"Re: Infos: 42" "Re: Infos: 42"
@@ -166,15 +165,14 @@ async fn test_subject_from_dc() {
// 2. Receive a message from Delta Chat // 2. Receive a message from Delta Chat
assert_eq!( assert_eq!(
msg_to_subject_str( msg_to_subject_str(
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ b"From: bob@example.net\r\n\
From: bob@example.com\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: Chat: hello\r\n\
Subject: Chat: hello\n\ Chat-Version: 1.0\r\n\
Chat-Version: 1.0\n\ Message-ID: <2223@example.net>\r\n\
Message-ID: <2223@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n"
hello\n"
) )
.await, .await,
"Re: Chat: hello" "Re: Chat: hello"
@@ -199,29 +197,27 @@ async fn test_subject_outgoing() {
async fn test_subject_unicode() { 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) // 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( msg_to_subject_str(
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ "From: bob@example.net\r\n\
From: bob@example.com\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: äääää\r\n\
Subject: äääää\n\ Chat-Version: 1.0\r\n\
Chat-Version: 1.0\n\ Message-ID: <2893@example.com>\r\n\
Message-ID: <2893@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n"
hello\n"
.as_bytes(), .as_bytes(),
) )
.await; .await;
msg_to_subject_str( msg_to_subject_str(
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ "From: bob@example.net\r\n\
From: bob@example.com\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: aäääää\r\n\
Subject: aäääää\n\ Chat-Version: 1.0\r\n\
Chat-Version: 1.0\n\ Message-ID: <2893@example.com>\r\n\
Message-ID: <2893@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n"
hello\n"
.as_bytes(), .as_bytes(),
) )
.await; .await;
@@ -230,73 +226,76 @@ async fn test_subject_unicode() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_mdn() { async fn test_subject_mdn() {
// 5. Receive an mdn (read receipt) and make sure the mdn's subject is not used // 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.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, t,
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ b"From: alice@example.org\r\n\
From: alice@example.org\n\ To: bob@example.net\r\n\
To: bob@example.com\n\ Subject: Hello, Bob\r\n\
Subject: Hello, Bob\n\ Chat-Version: 1.0\r\n\
Chat-Version: 1.0\n\ Message-ID: <2893@example.com>\r\n\
Message-ID: <2893@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n",
hello\n",
false, false,
) )
.await .await
.unwrap(); .unwrap();
let mut new_msg = incoming_msg_to_reply_msg( 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\ b"From: bob@example.net\r\n\
From: bob@example.com\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: message opened\r\n\
Subject: message opened\n\ Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\
Date: Sun, 22 Mar 2020 23:37:57 +0000\n\ Chat-Version: 1.0\r\n\
Chat-Version: 1.0\n\ Message-ID: <Mr.12345678902@example.com>\r\n\
Message-ID: <Mr.12345678902@example.com>\n\ Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\r\n\
Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\n\ \r\n\
\n\ \r\n\
\n\ --SNIPP\r\n\
--SNIPP\n\ Content-Type: text/plain; charset=utf-8\r\n\
Content-Type: text/plain; charset=utf-8\n\ \r\n\
\n\ Read receipts do not guarantee sth. was read.\r\n\
Read receipts do not guarantee sth. was read.\n\ \r\n\
\n\ \r\n\
\n\ --SNIPP\r\n\
--SNIPP\n\ Content-Type: message/disposition-notification\r\n\
Content-Type: message/disposition-notification\n\ \r\n\
\n\ Reporting-UA: Delta Chat 1.28.0\r\n\
Reporting-UA: Delta Chat 1.28.0\n\ Original-Recipient: rfc822;bob@example.com\r\n\
Original-Recipient: rfc822;bob@example.com\n\ Final-Recipient: rfc822;bob@example.com\r\n\
Final-Recipient: rfc822;bob@example.com\n\ Original-Message-ID: <2893@example.com>\r\n\
Original-Message-ID: <2893@example.com>\n\ Disposition: manual-action/MDN-sent-automatically; displayed\r\n\
Disposition: manual-action/MDN-sent-automatically; displayed\n\ \r\n", t, bob).await;
\n", &t).await; chat::send_msg(t, new_msg.chat_id, &mut new_msg)
chat::send_msg(&t, new_msg.chat_id, &mut new_msg)
.await .await
.unwrap(); .unwrap();
let mf = MimeFactory::from_msg(&t, new_msg).await.unwrap(); let mf = MimeFactory::from_msg(t, new_msg).await.unwrap();
// The subject string should not be "Re: message opened" // The subject string should not be "Re: message opened"
assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap()); assert_eq!("Re: Hello, Bob", mf.subject_str(t).await.unwrap());
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mdn_create_encrypted() -> Result<()> { async fn test_mdn_create_encrypted() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = tcm.alice().await; let alice = tcm.alice().await;
alice.allow_unencrypted().await?;
alice alice
.set_config(Config::Displayname, Some("Alice Exampleorg")) .set_config(Config::Displayname, Some("Alice Exampleorg"))
.await?; .await?;
let bob = tcm.bob().await; let bob = tcm.bob().await;
bob.allow_unencrypted().await?;
bob.set_config(Config::Displayname, Some("Bob Examplenet")) bob.set_config(Config::Displayname, Some("Bob Examplenet"))
.await?; .await?;
bob.set_config(Config::Selfstatus, Some("Bob Examplenet")) bob.set_config(Config::Selfstatus, Some("Bob Examplenet"))
.await?; .await?;
bob.set_config_bool(Config::MdnsEnabled, true).await?; bob.set_config_bool(Config::MdnsEnabled, true).await?;
// MDN for unencrypted message is not encrypted.
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
msg.param.set_int(Param::SkipAutocrypt, 1); let chat_alice = alice.create_email_chat(&bob).await.id;
let chat_alice = alice.create_chat(&bob).await.id;
let sent = alice.send_msg(chat_alice, &mut msg).await; let sent = alice.send_msg(chat_alice, &mut msg).await;
let rcvd = bob.recv_msg(&sent).await; let rcvd = bob.recv_msg(&sent).await;
@@ -311,13 +310,13 @@ async fn test_mdn_create_encrypted() -> Result<()> {
let bob_alice_contact = bob.add_or_lookup_contact(&alice).await; let bob_alice_contact = bob.add_or_lookup_contact(&alice).await;
assert_eq!(bob_alice_contact.get_authname(), "Alice Exampleorg"); assert_eq!(bob_alice_contact.get_authname(), "Alice Exampleorg");
// MDN for encrypted message is encrypted.
let rcvd = tcm.send_recv(&alice, &bob, "Heyho").await; let rcvd = tcm.send_recv(&alice, &bob, "Heyho").await;
message::markseen_msgs(&bob, vec![rcvd.id]).await?; message::markseen_msgs(&bob, vec![rcvd.id]).await?;
let mimefactory = MimeFactory::from_mdn(&bob, rcvd.from_id, rcvd.rfc724_mid, vec![]).await?; let mimefactory = MimeFactory::from_mdn(&bob, rcvd.from_id, rcvd.rfc724_mid, vec![]).await?;
let rendered_msg = mimefactory.render(&bob).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.is_encrypted);
assert!(!rendered_msg.message.contains("Bob Examplenet")); assert!(!rendered_msg.message.contains("Bob Examplenet"));
assert!(!rendered_msg.message.contains("Alice Exampleorg")); assert!(!rendered_msg.message.contains("Alice Exampleorg"));
@@ -417,7 +416,7 @@ async fn first_subject_str(t: TestContext) -> String {
mf.subject_str(&t).await.unwrap() 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 { async fn msg_to_subject_str(imf_raw: &[u8]) -> String {
let subject_str = msg_to_subject_str_inner(imf_raw, false, false, false).await; let subject_str = msg_to_subject_str_inner(imf_raw, false, false, false).await;
@@ -465,48 +464,62 @@ async fn msg_to_subject_str_inner(
reply: bool, reply: bool,
message_arrives_inbetween: bool, message_arrives_inbetween: bool,
) -> String { ) -> String {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let mut new_msg = incoming_msg_to_reply_msg(imf_raw, &t).await; let t = &tcm.alice().await;
let incoming_msg = get_chat_msg(&t, new_msg.chat_id, 0, 1).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 { if delete_original_msg {
incoming_msg.id.trash(&t, false).await.unwrap(); incoming_msg.id.trash(t, false).await.unwrap();
} }
if message_arrives_inbetween { if message_arrives_inbetween {
receive_imf( let encrypted_msg = test_utils::encrypt_raw_message(
&t, bob,
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ &[t],
From: Bob <bob@example.com>\n\ b"From: Bob <bob@example.net>\r\n\
To: alice@example.org\n\ To: alice@example.org\r\n\
Subject: Some other, completely unrelated subject\n\ Subject: Some other, completely unrelated subject\r\n\
Message-ID: <3cl4@example.com>\n\ Message-ID: <3cl4@example.com>\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
\n\ \r\n\
Some other, completely unrelated content\n", Some other, completely unrelated content\r\n",
false,
) )
.await .await
.unwrap(); .unwrap();
receive_imf(t, encrypted_msg.as_bytes(), false)
.await
.unwrap();
let arrived_msg = t.get_last_msg().await; let arrived_msg = t.get_last_msg().await;
assert_eq!(arrived_msg.chat_id, incoming_msg.chat_id); assert_eq!(arrived_msg.chat_id, incoming_msg.chat_id);
} }
if reply { if reply {
new_msg.set_quote(&t, Some(&incoming_msg)).await.unwrap(); new_msg.set_quote(t, Some(&incoming_msg)).await.unwrap();
} }
chat::send_msg(&t, new_msg.chat_id, &mut new_msg) chat::send_msg(t, new_msg.chat_id, &mut new_msg)
.await .await
.unwrap(); .unwrap();
let mf = MimeFactory::from_msg(&t, new_msg).await.unwrap(); let mf = MimeFactory::from_msg(t, new_msg).await.unwrap();
mf.subject_str(&t).await.unwrap() mf.subject_str(t).await.unwrap()
} }
// Creates a `Message` that replies "Hi" to the incoming email in `imf_raw`. // 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 { async fn incoming_msg_to_reply_msg(
receive_imf(context, imf_raw, false).await.unwrap(); 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(); let chats = Chatlist::try_load(context, 0, None, None).await.unwrap();
@@ -522,30 +535,31 @@ async fn incoming_msg_to_reply_msg(imf_raw: &[u8], context: &Context) -> Message
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
// This test could still be extended // This test could still be extended
async fn test_render_reply() { async fn test_render_reply() {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let context = &t; let t = &tcm.alice().await;
let charlie = &tcm.charlie().await;
let mut msg = incoming_msg_to_reply_msg( let mut msg = incoming_msg_to_reply_msg(
b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ b"From: Charlie <charlie@example.net>\r\n\
From: Charlie <charlie@example.com>\n\ To: alice@example.org\r\n\
To: alice@example.org\n\ Subject: Chat: hello\r\n\
Subject: Chat: hello\n\ Chat-Version: 1.0\r\n\
Chat-Version: 1.0\n\ Message-ID: <2223@example.com>\r\n\
Message-ID: <2223@example.com>\n\ Date: Sun, 22 Mar 2020 22:37:56 +0000\r\n\
Date: Sun, 22 Mar 2020 22:37:56 +0000\n\ \r\n\
\n\ hello\r\n",
hello\n", t,
context, charlie,
) )
.await; .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 mimefactory = MimeFactory::from_msg(t, msg).await.unwrap();
let recipients = mimefactory.recipients(); 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(); let mail = mailparse::parse_mail(rendered_msg.message.as_bytes()).unwrap();
assert_eq!( assert_eq!(
@@ -557,7 +571,7 @@ async fn test_render_reply() {
"1.0" "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 .await
.unwrap(); .unwrap();
} }

View File

@@ -12,7 +12,7 @@ use crate::{
message::{MessageState, MessengerMessage}, message::{MessageState, MessengerMessage},
receive_imf::receive_imf, receive_imf::receive_imf,
securejoin::QrInvite, securejoin::QrInvite,
test_utils::{TestContext, TestContextManager}, test_utils::{self, TestContext, TestContextManager},
tools::time, tools::time,
}; };
@@ -1185,6 +1185,7 @@ async fn test_allinkl_blockquote() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_subj_to_multimedia_msg() { async fn test_add_subj_to_multimedia_msg() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t.ctx, &t.ctx,
include_bytes!("../../test-data/message/subj_with_multimedia_msg.eml"), include_bytes!("../../test-data/message/subj_with_multimedia_msg.eml"),
@@ -1461,6 +1462,7 @@ async fn test_intended_recipient_fingerprint() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_long_in_reply_to() -> Result<()> { async fn test_long_in_reply_to() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
// A message with a long Message-ID. // A message with a long Message-ID.
// Long message-IDs are generated by Mailjet. // Long message-IDs are generated by Mailjet.
@@ -1503,96 +1505,89 @@ Some reply
// Test that WantsMdn parameter is not set on outgoing messages. // Test that WantsMdn parameter is not set on outgoing messages.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_wants_mdn() -> Result<()> { async fn test_outgoing_wants_mdn() -> Result<()> {
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let bob = TestContext::new_bob().await; 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 let chat_id = alice.create_chat(bob).await.id;
Chat-Version: 1.0\n\ let sent = alice.send_text(chat_id, "Message.").await;
Message-ID: <foobarbaz@example.org>
To: Bob <bob@example.org>
From: Alice <alice@example.org>
Subject: subject
Chat-Disposition-Notification-To: alice@example.org
Message.
";
// Bob receives message. // Bob receives message.
receive_imf(&bob, raw, false).await?; let bob_msg = bob.recv_msg(&sent).await;
let msg = bob.get_last_msg().await;
// Message is incoming. // 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. // Alice receives copy-to-self.
receive_imf(&alice, raw, false).await?; let alice2_msg = alice2.recv_msg(&sent).await;
let msg = alice.get_last_msg().await;
// Message is outgoing, don't send read receipt to self. // 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(()) Ok(())
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ignore_read_receipt_to_self() -> Result<()> { async fn test_ignore_read_receipt_to_self() -> Result<()> {
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
// Alice receives BCC-self copy of a message sent to Bob. // Alice receives BCC-self copy of a message sent to Bob.
receive_imf( let encrypted_message = test_utils::encrypt_raw_message(
&alice, alice,
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ &[bob],
From: alice@example.org\n\ "From: alice@example.org\r\n\
To: bob@example.net\n\ To: bob@example.net\r\n\
Subject: foo\n\ Subject: foo\r\n\
Message-ID: first@example.com\n\ Message-ID: first@example.com\r\n\
Chat-Version: 1.0\n\ Chat-Version: 1.0\r\n\
Chat-Disposition-Notification-To: alice@example.org\n\ Chat-Disposition-Notification-To: alice@example.org\r\n\
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\ Date: Sun, 22 Mar 2020 22:37:57 +0000\r\n\
\n\ \r\n\
hello\n" hello\r\n"
.as_bytes(), .as_bytes(),
false,
) )
.await?; .await?;
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.state, MessageState::OutDelivered); assert_eq!(msg.state, MessageState::OutDelivered);
// Due to a bug in the old version running on the other device, Alice receives a read // Alice receives a read receipt from self.
// receipt from self. //
receive_imf( // This should mark the message as seen, i.e. not counted as fresh,
&alice, // but not "read" (having double checkmark in the UI).
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ let encrypted_message = test_utils::encrypt_raw_message(
From: alice@example.org\n\ alice,
To: alice@example.org\n\ &[alice],
Subject: message opened\n\ "From: alice@example.org\r\n\
Date: Sun, 22 Mar 2020 23:37:57 +0000\n\ To: alice@example.org\r\n\
Chat-Version: 1.0\n\ Subject: message opened\r\n\
Message-ID: second@example.com\n\ Date: Sun, 22 Mar 2020 23:37:57 +0000\r\n\
Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\n\ Chat-Version: 1.0\r\n\
\n\ Message-ID: second@example.com\r\n\
\n\ Content-Type: multipart/report; report-type=disposition-notification; boundary=\"SNIPP\"\r\n\
--SNIPP\n\ \r\n\
Content-Type: text/plain; charset=utf-8\n\ \r\n\
\n\ --SNIPP\r\n\
Read receipts do not guarantee sth. was read.\n\ Content-Type: text/plain; charset=utf-8\r\n\
\n\ \r\n\
\n\ Read receipts do not guarantee sth. was read.\r\n\
--SNIPP\n\ \r\n\
Content-Type: message/disposition-notification\n\ \r\n\
\n\ --SNIPP\r\n\
Original-Recipient: rfc822;bob@example.com\n\ Content-Type: message/disposition-notification\r\n\
Final-Recipient: rfc822;bob@example.com\n\ \r\n\
Original-Message-ID: <first@example.com>\n\ Original-Recipient: rfc822;bob@example.com\r\n\
Disposition: manual-action/MDN-sent-automatically; displayed\n\ Final-Recipient: rfc822;bob@example.com\r\n\
\n\ Original-Message-ID: <first@example.com>\r\n\
\n\ Disposition: manual-action/MDN-sent-automatically; displayed\r\n\
--SNIPP--" \r\n\
.as_bytes(), \r\n\
false, --SNIPP--".as_bytes()).await?;
) receive_imf(alice, encrypted_message.as_bytes(), false).await?;
.await?;
// Check that the state has not changed to `MessageState::OutMdnRcvd`. // Check that the state has not changed to `MessageState::OutMdnRcvd`.
let msg = Message::load_from_db(&alice, msg.id).await?; let msg = Message::load_from_db(alice, msg.id).await?;
assert_eq!(msg.state, MessageState::OutDelivered); assert_eq!(msg.state, MessageState::OutDelivered);
Ok(()) Ok(())
@@ -1604,7 +1599,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. /// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ms_exchange_mdn() -> Result<()> { async fn test_ms_exchange_mdn() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let t = tcm.alice().await;
t.allow_unencrypted().await?;
let original = let original =
include_bytes!("../../test-data/message/ms_exchange_report_original_message.eml"); include_bytes!("../../test-data/message/ms_exchange_report_original_message.eml");
@@ -1700,17 +1697,24 @@ Content-Disposition: reaction\n\
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_jpeg_as_application_octet_stream() -> Result<()> { async fn test_jpeg_as_application_octet_stream() -> Result<()> {
let context = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let raw = include_bytes!("../../test-data/message/jpeg-as-application-octet-stream.eml"); let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let encrypted_msg = test_utils::encrypt_raw_message(
alice,
&[bob],
include_bytes!("../../test-data/message/jpeg-as-application-octet-stream.eml"),
)
.await?;
let msg = MimeMessage::from_bytes(&context.ctx, &raw[..]) let msg = MimeMessage::from_bytes(bob, encrypted_msg.as_bytes())
.await .await
.unwrap(); .unwrap();
assert_eq!(msg.parts.len(), 1); assert_eq!(msg.parts.len(), 1);
assert_eq!(msg.parts[0].typ, Viewtype::Image); assert_eq!(msg.parts[0].typ, Viewtype::Image);
receive_imf(&context, &raw[..], false).await?; receive_imf(alice, encrypted_msg.as_bytes(), false).await?;
let msg = context.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.get_viewtype(), Viewtype::Image); assert_eq!(msg.get_viewtype(), Viewtype::Image);
Ok(()) Ok(())
@@ -1984,6 +1988,8 @@ async fn test_chat_edit_imf_header() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await?;
bob.allow_unencrypted().await?;
let alice_chat = alice.create_email_chat(bob).await; let alice_chat = alice.create_email_chat(bob).await;
// Alice sends a message, then sends an invalid edit request. // Alice sends a message, then sends an invalid edit request.
@@ -2048,6 +2054,7 @@ async fn test_multiple_autocrypt_hdrs() -> Result<()> {
async fn test_receive_signed_only() -> Result<()> { async fn test_receive_signed_only() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
bob.allow_unencrypted().await?;
let imf_raw = include_bytes!("../../test-data/message/unencrypted_signed_simple.eml"); let imf_raw = include_bytes!("../../test-data/message/unencrypted_signed_simple.eml");
let msg = receive_imf(bob, imf_raw, false).await?.unwrap(); let msg = receive_imf(bob, imf_raw, false).await?.unwrap();
@@ -2065,19 +2072,24 @@ async fn test_receive_signed_only() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_huge_image_becomes_file() -> Result<()> { async fn test_huge_image_becomes_file() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let msg_id = receive_imf( let alice = &tcm.alice().await;
&t, let bob = &tcm.bob().await;
let encrypted_msg = test_utils::encrypt_raw_message(
bob,
&[alice],
include_bytes!("../../test-data/message/image_huge_64M.eml"), include_bytes!("../../test-data/message/image_huge_64M.eml"),
false,
) )
.await? .await?;
.unwrap()
.msg_ids[0]; let msg_id = receive_imf(alice, encrypted_msg.as_bytes(), false)
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap(); .await?
.unwrap()
.msg_ids[0];
let msg = Message::load_from_db(alice, msg_id).await.unwrap();
// Huge image should be treated as file: // Huge image should be treated as file:
assert_eq!(msg.viewtype, Viewtype::File); assert_eq!(msg.viewtype, Viewtype::File);
assert!(msg.get_file(&t).is_some()); assert!(msg.get_file(alice).is_some());
assert_eq!(msg.get_filename().unwrap(), "huge_image.png"); assert_eq!(msg.get_filename().unwrap(), "huge_image.png");
assert_eq!(msg.get_filemime().unwrap(), "image/png"); assert_eq!(msg.get_filemime().unwrap(), "image/png");
// File has no width or height // File has no width or height
@@ -2088,19 +2100,24 @@ async fn test_huge_image_becomes_file() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_4k_image_stays_image() -> Result<()> { async fn test_4k_image_stays_image() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let msg_id = receive_imf( let alice = &tcm.alice().await;
&t, let bob = &tcm.bob().await;
let encrypted_msg = test_utils::encrypt_raw_message(
bob,
&[alice],
include_bytes!("../../test-data/message/image_4k.eml"), include_bytes!("../../test-data/message/image_4k.eml"),
false,
) )
.await? .await?;
.unwrap()
.msg_ids[0]; let msg_id = receive_imf(alice, encrypted_msg.as_bytes(), false)
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap(); .await?
.unwrap()
.msg_ids[0];
let msg = Message::load_from_db(alice, msg_id).await.unwrap();
// 4K image should be treated as image: // 4K image should be treated as image:
assert_eq!(msg.viewtype, Viewtype::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_filename().unwrap(), "4k_image.png");
assert_eq!(msg.get_filemime().unwrap(), "image/png"); assert_eq!(msg.get_filemime().unwrap(), "image/png");
assert_eq!(msg.param.get_int(Param::Width).unwrap_or_default(), 3840); assert_eq!(msg.param.get_int(Param::Width).unwrap_or_default(), 3840);

View File

@@ -335,18 +335,17 @@ impl Chat {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use deltachat_contact_tools::ContactAddress;
use super::*; use super::*;
use crate::chat::{forward_msgs, get_chat_msgs, marknoticed_chat, send_text_msg}; use crate::chat::{forward_msgs, get_chat_msgs, marknoticed_chat, send_text_msg};
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::config::Config; use crate::config::Config;
use crate::contact::{Contact, Origin}; use crate::contact::Contact;
use crate::key::{load_self_public_key, load_self_secret_key}; use crate::key::{load_self_public_key, load_self_secret_key};
use crate::message::{MessageState, Viewtype, delete_msgs, markseen_msgs}; use crate::message::{MessageState, Viewtype, delete_msgs, markseen_msgs};
use crate::pgp::{SeipdVersion, pk_encrypt}; use crate::pgp::{SeipdVersion, pk_encrypt};
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::sql::housekeeping; use crate::sql::housekeeping;
use crate::test_utils;
use crate::test_utils::E2EE_INFO_MSGS; use crate::test_utils::E2EE_INFO_MSGS;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
use crate::test_utils::TestContextManager; use crate::test_utils::TestContextManager;
@@ -386,59 +385,54 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_receive_reaction() -> Result<()> { async fn test_receive_reaction() -> Result<()> {
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
// Alice receives BCC-self copy of a message sent to Bob. // Alice receives BCC-self copy of a message sent to Bob.
receive_imf( let encrypted_message = test_utils::encrypt_raw_message(
&alice, alice,
"To: bob@example.net\n\ &[alice, bob],
From: alice@example.org\n\ b"To: bob@example.net\r\n\
Date: Today, 29 February 2021 00:00:00 -800\n\ From: alice@example.org\r\n\
Message-ID: 12345@example.org\n\ Date: Today, 29 February 2021 00:00:00 -800\r\n\
Subject: Meeting\n\ Message-ID: 12345@example.org\r\n\
\n\ Subject: Meeting\r\n\
Can we chat at 1pm pacific, today?" \r\n\
.as_bytes(), Can we chat at 1pm pacific, today?",
false,
) )
.await?; .await?;
receive_imf(alice, encrypted_message.as_bytes(), false).await?;
let msg = alice.get_last_msg().await; let msg = alice.get_last_msg().await;
assert_eq!(msg.state, MessageState::OutDelivered); assert_eq!(msg.state, MessageState::OutDelivered);
let reactions = get_msg_reactions(&alice, msg.id).await?; let reactions = get_msg_reactions(alice, msg.id).await?;
let contacts = reactions.contacts(); let contacts = reactions.contacts();
assert_eq!(contacts.len(), 0); assert_eq!(contacts.len(), 0);
let bob_id = Contact::add_or_lookup( let bob_id = alice.add_or_lookup_contact_id(bob).await;
&alice,
"",
&ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?
.0;
let bob_reaction = reactions.get(bob_id); let bob_reaction = reactions.get(bob_id);
assert!(bob_reaction.is_empty()); // Bob has not reacted to message yet. assert!(bob_reaction.is_empty()); // Bob has not reacted to message yet.
// Alice receives reaction to her message from Bob. // Alice receives reaction to her message from Bob.
receive_imf( test_utils::receive_encrypted_imf(
&alice, alice,
"To: alice@example.org\n\ bob,
From: bob@example.net\n\ "To: alice@example.org\r\n\
Date: Today, 29 February 2021 00:00:10 -800\n\ From: bob@example.net\r\n\
Message-ID: 56789@example.net\n\ Date: Today, 29 February 2021 00:00:10 -800\r\n\
In-Reply-To: 12345@example.org\n\ Message-ID: 56789@example.net\r\n\
Subject: Meeting\n\ In-Reply-To: 12345@example.org\r\n\
Mime-Version: 1.0 (1.0)\n\ Subject: Meeting\r\n\
Content-Type: text/plain; charset=utf-8\n\ Mime-Version: 1.0 (1.0)\r\n\
Content-Disposition: reaction\n\ Content-Type: text/plain; charset=utf-8\r\n\
\n\ Content-Disposition: reaction\r\n\
\r\n\
\u{1F44D}" \u{1F44D}"
.as_bytes(), .as_bytes(),
false,
) )
.await?; .await?;
let reactions = get_msg_reactions(&alice, msg.id).await?; let reactions = get_msg_reactions(alice, msg.id).await?;
assert_eq!(reactions.to_string(), "👍1"); assert_eq!(reactions.to_string(), "👍1");
let contacts = reactions.contacts(); let contacts = reactions.contacts();
@@ -451,8 +445,9 @@ Content-Disposition: reaction\n\
assert_eq!(bob_reaction.as_str(), "👍"); assert_eq!(bob_reaction.as_str(), "👍");
// Alice receives reaction to her message from Bob with a footer. // Alice receives reaction to her message from Bob with a footer.
receive_imf( test_utils::receive_encrypted_imf(
&alice, alice,
bob,
"To: alice@example.org\n\ "To: alice@example.org\n\
From: bob@example.net\n\ From: bob@example.net\n\
Date: Today, 29 February 2021 00:00:10 -800\n\ Date: Today, 29 February 2021 00:00:10 -800\n\
@@ -469,16 +464,16 @@ Content-Disposition: reaction\n\
_______________________________________________\n\ _______________________________________________\n\
Here's my footer -- bob@example.net" Here's my footer -- bob@example.net"
.as_bytes(), .as_bytes(),
false,
) )
.await?; .await?;
let reactions = get_msg_reactions(&alice, msg.id).await?; let reactions = get_msg_reactions(alice, msg.id).await?;
assert_eq!(reactions.to_string(), "😀1"); assert_eq!(reactions.to_string(), "😀1");
// Alice receives a message with reaction to her message from Bob. // Alice receives a message with reaction to her message from Bob.
let msg_bob = receive_imf( let msg_bob = test_utils::receive_encrypted_imf(
&alice, alice,
bob,
"To: alice@example.org\n\ "To: alice@example.org\n\
From: bob@example.net\n\ From: bob@example.net\n\
Date: Today, 29 February 2021 00:00:10 -800\n\ Date: Today, 29 February 2021 00:00:10 -800\n\
@@ -502,18 +497,16 @@ Content-Disposition: reaction\n\
\n\ \n\
--YiEDa0DAkWCtVeE4--" --YiEDa0DAkWCtVeE4--"
.as_bytes(), .as_bytes(),
false,
) )
.await? .await?;
.unwrap(); let msg_bob = Message::load_from_db(alice, msg_bob.msg_ids[0]).await?;
let msg_bob = Message::load_from_db(&alice, msg_bob.msg_ids[0]).await?;
assert_eq!(msg_bob.from_id, bob_id); assert_eq!(msg_bob.from_id, bob_id);
assert_eq!(msg_bob.chat_id, msg.chat_id); assert_eq!(msg_bob.chat_id, msg.chat_id);
assert_eq!(msg_bob.viewtype, Viewtype::Text); assert_eq!(msg_bob.viewtype, Viewtype::Text);
assert_eq!(msg_bob.state, MessageState::InFresh); assert_eq!(msg_bob.state, MessageState::InFresh);
assert_eq!(msg_bob.hidden, false); assert_eq!(msg_bob.hidden, false);
assert_eq!(msg_bob.text, "Reply + reaction"); assert_eq!(msg_bob.text, "Reply + reaction");
let reactions = get_msg_reactions(&alice, msg.id).await?; let reactions = get_msg_reactions(alice, msg.id).await?;
assert_eq!(reactions.to_string(), "👍1"); assert_eq!(reactions.to_string(), "👍1");
Ok(()) Ok(())

View File

@@ -505,6 +505,14 @@ pub(crate) async fn receive_imf_inner(
Ok(mime_parser) => mime_parser, Ok(mime_parser) => mime_parser,
}; };
if !mime_parser.was_encrypted()
&& mime_parser.get_header(HeaderDef::SecureJoin).is_none()
&& context.get_config_bool(Config::ForceEncryption).await?
{
warn!(context, "Fetched unencrypted message, ignoring");
return trash().await;
}
let rfc724_mid_orig = &mime_parser let rfc724_mid_orig = &mime_parser
.get_rfc724_mid() .get_rfc724_mid()
.unwrap_or(rfc724_mid.to_string()); .unwrap_or(rfc724_mid.to_string());

View File

@@ -15,6 +15,7 @@ use crate::imap::prefetch_should_download;
use crate::imex::{ImexMode, imex}; use crate::imex::{ImexMode, imex};
use crate::key; use crate::key;
use crate::securejoin::get_securejoin_qr; use crate::securejoin::get_securejoin_qr;
use crate::test_utils;
use crate::test_utils::{ use crate::test_utils::{
TestContext, TestContextManager, alice_keypair, get_chat_msg, mark_as_verified, TestContext, TestContextManager, alice_keypair, get_chat_msg, mark_as_verified,
}; };
@@ -80,6 +81,7 @@ static GRP_MAIL: &[u8] =
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_is_shown() { async fn test_adhoc_group_is_shown() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0); assert_eq!(chats.len(), 0);
@@ -100,6 +102,7 @@ async fn test_adhoc_group_is_shown() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_accepted_contact_accepted() { async fn test_adhoc_group_show_accepted_contact_accepted() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
// accept Bob by accepting a delta-message from Bob // accept Bob by accepting a delta-message from Bob
receive_imf(&t, MSGRMSG, false).await.unwrap(); receive_imf(&t, MSGRMSG, false).await.unwrap();
@@ -135,6 +138,7 @@ async fn test_adhoc_group_show_accepted_contact_accepted() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_all() { async fn test_adhoc_group_show_all() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf(&t, GRP_MAIL, false).await.unwrap(); receive_imf(&t, GRP_MAIL, false).await.unwrap();
// adhoc-group with unknown contacts with show_emails=all will show up in a single chat // adhoc-group with unknown contacts with show_emails=all will show up in a single chat
@@ -154,6 +158,7 @@ async fn test_adhoc_group_show_all() {
async fn test_adhoc_groups_merge() -> Result<()> { async fn test_adhoc_groups_merge() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
receive_imf( receive_imf(
alice, alice,
b"From: bob@example.net\n\ b"From: bob@example.net\n\
@@ -354,6 +359,7 @@ async fn test_no_message_id_header() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_escaped_from() { async fn test_escaped_from() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let contact_id = Contact::create(&t, "foobar", "foobar@example.com") let contact_id = Contact::create(&t, "foobar", "foobar@example.com")
.await .await
.unwrap(); .unwrap();
@@ -387,6 +393,7 @@ async fn test_escaped_from() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_escaped_recipients() { async fn test_escaped_recipients() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
Contact::create(&t, "foobar", "foobar@example.com") Contact::create(&t, "foobar", "foobar@example.com")
.await .await
.unwrap(); .unwrap();
@@ -434,6 +441,7 @@ async fn test_escaped_recipients() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cc_to_contact() { async fn test_cc_to_contact() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
Contact::create(&t, "foobar", "foobar@example.com") Contact::create(&t, "foobar", "foobar@example.com")
.await .await
.unwrap(); .unwrap();
@@ -590,6 +598,7 @@ async fn test_parse_ndn(
) -> (TestContext, MsgId) { ) -> (TestContext, MsgId) {
let t = TestContext::new().await; let t = TestContext::new().await;
t.configure_addr(self_addr).await; t.configure_addr(self_addr).await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, &t,
@@ -674,6 +683,7 @@ async fn test_resend_after_ndn() -> Result<()> {
async fn test_parse_ndn_group_msg() -> Result<()> { async fn test_parse_ndn_group_msg() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
t.configure_addr("alice@gmail.com").await; t.configure_addr("alice@gmail.com").await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -715,6 +725,7 @@ async fn test_parse_ndn_group_msg() -> Result<()> {
async fn test_concat_multiple_ndns() -> Result<()> { async fn test_concat_multiple_ndns() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
t.configure_addr("alice@posteo.org").await; t.configure_addr("alice@posteo.org").await;
t.allow_unencrypted().await?;
let mid = "1234@mail.gmail.com"; let mid = "1234@mail.gmail.com";
receive_imf( receive_imf(
&t, &t,
@@ -772,6 +783,7 @@ async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_only_mail() { async fn test_html_only_mail() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let msg = load_imf_email(&t, include_bytes!("../../test-data/message/wrong-html.eml")).await; let msg = load_imf_email(&t, include_bytes!("../../test-data/message/wrong-html.eml")).await;
assert_eq!( assert_eq!(
msg.text, msg.text,
@@ -807,6 +819,7 @@ static GH_MAILINGLIST2: &str = "Received: (Postfix, from userid 1000); Mon, 4 De
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_github_mailing_list() -> Result<()> { async fn test_github_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf(&t.ctx, GH_MAILINGLIST, false).await?; receive_imf(&t.ctx, GH_MAILINGLIST, false).await?;
@@ -880,6 +893,8 @@ static DC_MAILINGLIST2: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_classic_mailing_list() -> Result<()> { async fn test_classic_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
let chat_id = chats.get_chat_id(0).unwrap(); let chat_id = chats.get_chat_id(0).unwrap();
@@ -921,6 +936,8 @@ Hello mailinglist!\r\n"
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_other_device_writes_to_mailinglist() -> Result<()> { async fn test_other_device_writes_to_mailinglist() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap(); receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
let first_msg = t.get_last_msg().await; let first_msg = t.get_last_msg().await;
let first_chat = Chat::load_from_db(&t, first_msg.chat_id).await?; let first_chat = Chat::load_from_db(&t, first_msg.chat_id).await?;
@@ -971,6 +988,7 @@ async fn test_other_device_writes_to_mailinglist() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_block_mailing_list() { async fn test_block_mailing_list() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
t.evtracker.wait_next_incoming_message().await; t.evtracker.wait_next_incoming_message().await;
@@ -1005,6 +1023,7 @@ async fn test_block_mailing_list() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_block_then_unblock() { async fn test_mailing_list_decide_block_then_unblock() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap(); receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
let blocked = Contact::get_all_blocked(&t).await.unwrap(); let blocked = Contact::get_all_blocked(&t).await.unwrap();
@@ -1035,6 +1054,7 @@ async fn test_mailing_list_decide_block_then_unblock() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_not_now() { async fn test_mailing_list_decide_not_now() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
@@ -1062,6 +1082,7 @@ async fn test_mailing_list_decide_not_now() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_accept() { async fn test_mailing_list_decide_accept() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap(); receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
@@ -1084,6 +1105,8 @@ async fn test_mailing_list_decide_accept() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_multiple_names_in_subject() -> Result<()> { async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
b"From: Foo Bar <foo@bar.org>\n\ b"From: Foo Bar <foo@bar.org>\n\
@@ -1108,6 +1131,7 @@ async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_majordomo_mailing_list() -> Result<()> { async fn test_majordomo_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
// test mailing lists not having a `ListId:`-header // test mailing lists not having a `ListId:`-header
receive_imf( receive_imf(
@@ -1160,6 +1184,7 @@ async fn test_majordomo_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailchimp_mailing_list() -> Result<()> { async fn test_mailchimp_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -1193,6 +1218,7 @@ async fn test_mailchimp_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dhl_mailing_list() -> Result<()> { async fn test_dhl_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -1218,6 +1244,7 @@ async fn test_dhl_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dpd_mailing_list() -> Result<()> { async fn test_dpd_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -1243,6 +1270,7 @@ async fn test_dpd_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_xt_local_mailing_list() -> Result<()> { async fn test_xt_local_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -1276,6 +1304,7 @@ async fn test_xt_local_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_xing_mailing_list() -> Result<()> { async fn test_xing_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -1298,6 +1327,7 @@ async fn test_xing_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ttline_mailing_list() -> Result<()> { async fn test_ttline_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -1318,6 +1348,7 @@ async fn test_ttline_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_mimepart_footer() { async fn test_mailing_list_with_mimepart_footer() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
// the mailing list message contains two top-level texts. // the mailing list message contains two top-level texts.
// the second text is a footer that is added by some mailing list software // the second text is a footer that is added by some mailing list software
@@ -1345,6 +1376,7 @@ async fn test_mailing_list_with_mimepart_footer() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_mimepart_footer_signed() { async fn test_mailing_list_with_mimepart_footer_signed() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, &t,
@@ -1369,6 +1401,7 @@ async fn test_mailing_list_with_mimepart_footer_signed() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_apply_mailinglist_changes_assigned_by_reply() { async fn test_apply_mailinglist_changes_assigned_by_reply() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf(&t, GH_MAILINGLIST, false).await.unwrap(); receive_imf(&t, GH_MAILINGLIST, false).await.unwrap();
@@ -1407,6 +1440,7 @@ async fn test_apply_mailinglist_changes_assigned_by_reply() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_chat_message() { async fn test_mailing_list_chat_message() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, &t,
@@ -1429,6 +1463,7 @@ async fn test_mailing_list_chat_message() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_bot() { async fn test_mailing_list_bot() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
t.set_config(Config::Bot, Some("1")).await.unwrap(); t.set_config(Config::Bot, Some("1")).await.unwrap();
receive_imf( receive_imf(
@@ -1461,6 +1496,8 @@ async fn test_dont_show_noreply_in_contacts_list() {
async fn check_dont_show_in_contacts_list(addr: &str) { async fn check_dont_show_in_contacts_list(addr: &str) {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, &t,
format!( format!(
@@ -1490,6 +1527,7 @@ YEAAAAAA!.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pdf_filename_simple() { async fn test_pdf_filename_simple() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let msg = load_imf_email( let msg = load_imf_email(
&t, &t,
include_bytes!("../../test-data/message/pdf_filename_simple.eml"), include_bytes!("../../test-data/message/pdf_filename_simple.eml"),
@@ -1510,6 +1548,7 @@ async fn test_pdf_filename_simple() {
async fn test_pdf_filename_continuation() { async fn test_pdf_filename_continuation() {
// test filenames split across multiple header lines, see rfc 2231 // test filenames split across multiple header lines, see rfc 2231
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let msg = load_imf_email( let msg = load_imf_email(
&t, &t,
include_bytes!("../../test-data/message/pdf_filename_continuation.eml"), include_bytes!("../../test-data/message/pdf_filename_continuation.eml"),
@@ -1535,6 +1574,7 @@ async fn test_pdf_filename_continuation() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_many_images() { async fn test_many_images() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, &t,
@@ -1555,6 +1595,7 @@ async fn test_many_images() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_in_reply_to() { async fn test_in_reply_to() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.allow_unencrypted().await.unwrap();
t.configure_addr("bob@example.com").await; t.configure_addr("bob@example.com").await;
// Receive message from Alice about group "foo". // Receive message from Alice about group "foo".
@@ -1630,6 +1671,7 @@ async fn test_save_mime_headers_off() -> anyhow::Result<()> {
async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: bool) { async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: bool) {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = tcm.alice().await; let alice = tcm.alice().await;
alice.allow_unencrypted().await.unwrap();
// Claire, a customer, sends a support request // Claire, a customer, sends a support request
// to the alias address <support@example.org>. // to the alias address <support@example.org>.
@@ -1696,6 +1738,8 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
let claire = tcm.unconfigured().await; let claire = tcm.unconfigured().await;
claire.configure_addr("claire@example.org").await; claire.configure_addr("claire@example.org").await;
claire.allow_unencrypted().await.unwrap();
receive_imf(&claire, claire_request.as_bytes(), false) receive_imf(&claire, claire_request.as_bytes(), false)
.await .await
.unwrap(); .unwrap();
@@ -1832,47 +1876,57 @@ async fn test_alias_support_answer_from_dc_chat_group() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dont_assign_to_trash_by_parent() { async fn test_dont_assign_to_trash_by_parent() {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
println!("\n========= Receive a message =========="); let alice = &tcm.alice().await;
receive_imf( let bob = &tcm.bob().await;
&t, let charlie = &tcm.charlie().await;
b"From: Nu Bar <nu@bar.org>\n\
To: alice@example.org, bob@example.org\n\ tcm.section("Receive a message");
Subject: Hi\n\ let encrypted_message = test_utils::encrypt_raw_message(
Message-ID: <4444@example.org>\n\ charlie,
\n\ &[alice, bob],
hello\n", b"From: Charlie <charlie@example.net>\r\n\
false, To: alice@example.org, bob@example.org\r\n\
Subject: Hi\r\n\
Message-ID: <4444@example.org>\r\n\
\r\n\
hello\r\n",
) )
.await .await
.unwrap(); .unwrap();
let chat_id = t.get_last_msg().await.chat_id; receive_imf(alice, encrypted_message.as_bytes(), false)
chat_id.accept(&t).await.unwrap(); .await
let msg = get_chat_msg(&t, chat_id, 0, 1).await; // Make sure that the message is actually in the chat .unwrap();
let chat_id = alice.get_last_msg().await.chat_id;
chat_id.accept(alice).await.unwrap();
let msg = get_chat_msg(alice, chat_id, 0, 1).await; // Make sure that the message is actually in the chat
assert!(!msg.chat_id.is_special()); assert!(!msg.chat_id.is_special());
assert_eq!(msg.text, "Hi hello"); assert_eq!(msg.text, "Hi hello");
println!("\n========= Delete the message =========="); tcm.section("Delete the message");
msg.id.trash(&t, false).await.unwrap(); msg.id.trash(alice, false).await.unwrap();
let msgs = chat::get_chat_msgs(&t.ctx, chat_id).await.unwrap(); let msgs = chat::get_chat_msgs(alice, chat_id).await.unwrap();
assert_eq!(msgs.len(), 0); assert_eq!(msgs.len(), 0);
println!("\n========= Receive a message that is a reply to the deleted message =========="); tcm.section("Receive a message that is a reply to the deleted message");
receive_imf( let encrypted_message = test_utils::encrypt_raw_message(
&t, charlie,
b"From: Nu Bar <nu@bar.org>\n\ &[alice, bob],
To: alice@example.org, bob@example.org\n\ b"From: Charlie <charlie@example.net>\r\n\
Subject: Re: Hi\n\ To: alice@example.org, bob@example.org\r\n\
Message-ID: <5555@example.org>\n\ Subject: Re: Hi\r\n\
In-Reply-To: <4444@example.org\n\ Message-ID: <5555@example.org>\r\n\
\n\ In-Reply-To: <4444@example.org\r\n\
Reply\n", \r\n\
false, Reply\r\n",
) )
.await .await
.unwrap(); .unwrap();
let msg = t.get_last_msg().await; receive_imf(alice, encrypted_message.as_bytes(), false)
.await
.unwrap();
let msg = alice.get_last_msg().await;
assert!(!msg.chat_id.is_special()); // Esp. check that the chat_id is not TRASH assert!(!msg.chat_id.is_special()); // Esp. check that the chat_id is not TRASH
assert_eq!(msg.text, "Reply"); assert_eq!(msg.text, "Reply");
} }
@@ -1882,10 +1936,12 @@ async fn test_dont_show_all_outgoing_msgs_in_self_chat() {
// Regression test for <https://github.com/deltachat/deltachat-android/issues/1940>: // Regression test for <https://github.com/deltachat/deltachat-android/issues/1940>:
// Some servers add a `Bcc: <Self>` header, which caused all outgoing messages to // Some servers add a `Bcc: <Self>` header, which caused all outgoing messages to
// be shown in the self-chat. // be shown in the self-chat.
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let t = &tcm.alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, t,
b"Bcc: alice@example.org b"Bcc: alice@example.org
Received: from [127.0.0.1] Received: from [127.0.0.1]
Subject: s Subject: s
@@ -1910,6 +1966,7 @@ Message content",
async fn test_unencrypted_doesnt_goto_self_chat() -> Result<()> { async fn test_unencrypted_doesnt_goto_self_chat() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let t = &tcm.alice().await; let t = &tcm.alice().await;
t.allow_unencrypted().await?;
let mut chat_id = None; let mut chat_id = None;
for (i, to) in [ for (i, to) in [
@@ -1991,6 +2048,7 @@ async fn test_no_smtp_job_for_self_chat() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_classic_mail_creates_chat() { async fn test_outgoing_classic_mail_creates_chat() {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await.unwrap();
// Alice downloads outgoing classic email. // Alice downloads outgoing classic email.
receive_imf( receive_imf(
@@ -2016,6 +2074,7 @@ Message content",
async fn test_duplicate_message() -> Result<()> { async fn test_duplicate_message() -> Result<()> {
// Test that duplicate messages are ignored based on the Message-ID // Test that duplicate messages are ignored based on the Message-ID
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
let bob_contact_id = Contact::add_or_lookup( let bob_contact_id = Contact::add_or_lookup(
&alice, &alice,
@@ -2074,6 +2133,8 @@ Second signature";
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ignore_footer_status_from_mailinglist() -> Result<()> { async fn test_ignore_footer_status_from_mailinglist() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
let bob_id = Contact::add_or_lookup( let bob_id = Contact::add_or_lookup(
&t, &t,
"", "",
@@ -2153,6 +2214,8 @@ Original signature updated",
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ignore_old_status_updates() -> Result<()> { async fn test_ignore_old_status_updates() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
let bob_id = Contact::add_or_lookup( let bob_id = Contact::add_or_lookup(
&t, &t,
"", "",
@@ -2222,11 +2285,13 @@ sig thursday",
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_private_classical_reply() { async fn test_chat_assignment_private_classical_reply() {
let mut tcm = TestContextManager::new();
for outgoing_is_classical in &[true, false] { for outgoing_is_classical in &[true, false] {
let t = TestContext::new_alice().await; let t = &tcm.alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, t,
format!( format!(
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
Subject: =?utf-8?q?single_reply-to?= Subject: =?utf-8?q?single_reply-to?=
@@ -2263,12 +2328,12 @@ Message-ID: <Gr.eJ_llQIXf0K.buxmrnMmG0Y@gmx.de>"
"Hello, I've just created the group \"single reply-to\" for us." "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.typ, Chattype::Group);
assert_eq!(group_chat.name, "single reply-to"); assert_eq!(group_chat.name, "single reply-to");
receive_imf( receive_imf(
&t, t,
format!( format!(
r#"Subject: Re: single reply-to r#"Subject: Re: single reply-to
To: "Alice" <alice@example.org> To: "Alice" <alice@example.org>
@@ -2297,7 +2362,7 @@ Private reply"#,
let private_msg = t.get_last_msg().await; let private_msg = t.get_last_msg().await;
assert_eq!(private_msg.text, "Private reply"); 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_eq!(private_chat.typ, Chattype::Single);
assert_ne!(private_msg.chat_id, group_msg.chat_id); assert_ne!(private_msg.chat_id, group_msg.chat_id);
} }
@@ -2305,16 +2370,17 @@ Private reply"#,
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_private_chat_reply() { async fn test_chat_assignment_private_chat_reply() {
let mut tcm = TestContextManager::new();
for (outgoing_is_classical, outgoing_has_multiple_recipients) in for (outgoing_is_classical, outgoing_has_multiple_recipients) in
&[(true, true), (false, true), (false, false)] &[(true, true), (false, true), (false, false)]
{ {
let t = TestContext::new_alice().await; let t = &tcm.alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, t,
format!( format!(
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) r#"Subject: =?utf-8?q?single_reply-to?=
Subject: =?utf-8?q?single_reply-to?=
{} {}
Date: Fri, 28 May 2021 10:15:05 +0000 Date: Fri, 28 May 2021 10:15:05 +0000
To: Bob <bob@example.com>, Charlie <charlie@example.net>{} To: Bob <bob@example.com>, Charlie <charlie@example.net>{}
@@ -2352,12 +2418,12 @@ Message-ID: <Gr.iy1KCE2y65_.mH2TM52miv9@testrun.org>"
"Hello, I've just created the group \"single reply-to\" for us." "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.typ, Chattype::Group);
assert_eq!(group_chat.name, "single reply-to"); assert_eq!(group_chat.name, "single reply-to");
receive_imf( receive_imf(
&t, t,
format!( format!(
r#"Subject: =?utf-8?q?Re=3A_single_reply-to?= r#"Subject: =?utf-8?q?Re=3A_single_reply-to?=
MIME-Version: 1.0 MIME-Version: 1.0
@@ -2392,7 +2458,7 @@ Sent with my Delta Chat Messenger: https://delta.chat
let private_msg = t.get_last_msg().await; let private_msg = t.get_last_msg().await;
assert_eq!(private_msg.text, "Private reply"); 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_eq!(private_chat.typ, Chattype::Single);
assert_ne!(private_msg.chat_id, group_msg.chat_id); assert_ne!(private_msg.chat_id, group_msg.chat_id);
} }
@@ -2400,11 +2466,13 @@ Sent with my Delta Chat Messenger: https://delta.chat
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_nonprivate_classical_reply() { async fn test_chat_assignment_nonprivate_classical_reply() {
let mut tcm = TestContextManager::new();
for outgoing_is_classical in &[true, false] { for outgoing_is_classical in &[true, false] {
let t = TestContext::new_alice().await; let t = &tcm.alice().await;
t.allow_unencrypted().await.unwrap();
receive_imf( receive_imf(
&t, t,
format!( format!(
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
Subject: =?utf-8?q?single_reply-to?= Subject: =?utf-8?q?single_reply-to?=
@@ -2440,13 +2508,13 @@ Message-ID: <Gr.eJ_llQIXf0K.buxmrnMmG0Y@gmx.de>"
"Hello, I've just created the group \"single reply-to\" for us." "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.typ, Chattype::Group);
assert_eq!(group_chat.name, "single reply-to"); assert_eq!(group_chat.name, "single reply-to");
// =============== Receive another outgoing message and check that it is put into the same chat =============== // =============== Receive another outgoing message and check that it is put into the same chat ===============
receive_imf( receive_imf(
&t, t,
format!( format!(
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
Subject: Out subj Subject: Out subj
@@ -2471,13 +2539,13 @@ Outgoing reply to all"#,
let reply = t.get_last_msg().await; let reply = t.get_last_msg().await;
assert_eq!(reply.text, "Out subj Outgoing reply to all"); 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.typ, Chattype::Group);
assert_eq!(reply.chat_id, group_msg.chat_id); assert_eq!(reply.chat_id, group_msg.chat_id);
// =============== Receive an incoming message and check that it is put into the same chat =============== // =============== Receive an incoming message and check that it is put into the same chat ===============
receive_imf( receive_imf(
&t, t,
br#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) br#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
Subject: In subj Subject: In subj
To: "Bob" <bob@example.com>, "Claire" <claire@example.com> To: "Bob" <bob@example.com>, "Claire" <claire@example.com>
@@ -2494,7 +2562,7 @@ Reply to all"#,
let reply = t.get_last_msg().await; let reply = t.get_last_msg().await;
assert_eq!(reply.text, "In subj Reply to all"); 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.typ, Chattype::Group);
assert_eq!(reply.chat_id, group_msg.chat_id); assert_eq!(reply.chat_id, group_msg.chat_id);
} }
@@ -2512,6 +2580,9 @@ async fn test_chat_assignment_adhoc() -> Result<()> {
let alice = tcm.alice().await; let alice = tcm.alice().await;
let bob = tcm.bob().await; let bob = tcm.bob().await;
let fiona = tcm.fiona().await; let fiona = tcm.fiona().await;
alice.allow_unencrypted().await?;
bob.allow_unencrypted().await?;
fiona.allow_unencrypted().await?;
let first_thread_mime = br#"Subject: First thread let first_thread_mime = br#"Subject: First thread
Message-ID: first@example.org Message-ID: first@example.org
@@ -2656,6 +2727,7 @@ async fn test_read_receipts_dont_unmark_bots() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_gmx_forwarded_msg() -> Result<()> { async fn test_gmx_forwarded_msg() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -2743,7 +2815,9 @@ async fn get_parent_message(
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_parent_message() -> Result<()> { async fn test_get_parent_message() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let mime = br#"Subject: First let mime = br#"Subject: First
Message-ID: first@example.net Message-ID: first@example.net
@@ -2752,8 +2826,8 @@ From: Bob <bob@example.net>
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
First."#; First."#;
receive_imf(&t, mime, false).await?; test_utils::receive_encrypted_imf(alice, bob, mime).await?;
let first = t.get_last_msg().await; let first = alice.get_last_msg().await;
let mime = br#"Subject: Second let mime = br#"Subject: Second
Message-ID: second@example.net Message-ID: second@example.net
To: Alice <alice@example.org> To: Alice <alice@example.org>
@@ -2761,8 +2835,8 @@ From: Bob <bob@example.net>
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
First."#; First."#;
receive_imf(&t, mime, false).await?; test_utils::receive_encrypted_imf(alice, bob, mime).await?;
let second = t.get_last_msg().await; let second = alice.get_last_msg().await;
let mime = br#"Subject: Third let mime = br#"Subject: Third
Message-ID: third@example.net Message-ID: third@example.net
To: Alice <alice@example.org> To: Alice <alice@example.org>
@@ -2770,8 +2844,8 @@ From: Bob <bob@example.net>
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
First."#; First."#;
receive_imf(&t, mime, false).await?; test_utils::receive_encrypted_imf(alice, bob, mime).await?;
let third = t.get_last_msg().await; let third = alice.get_last_msg().await;
let mime = br#"Subject: Message with references. let mime = br#"Subject: Message with references.
Message-ID: second@example.net Message-ID: second@example.net
@@ -2782,21 +2856,22 @@ References: <second@example.net> <nonexistent@example.net> <first@example.net>
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
Message with references."#; Message with references."#;
let mime_parser = MimeMessage::from_bytes(&t, &mime[..]).await?; let encrypted_mime = test_utils::encrypt_raw_message(bob, &[alice], mime).await?;
let mime_parser = MimeMessage::from_bytes(alice, encrypted_mime.as_bytes()).await?;
let parent = get_parent_message(&t, &mime_parser).await?.unwrap(); let parent = get_parent_message(alice, &mime_parser).await?.unwrap();
assert_eq!(parent.id, first.id); assert_eq!(parent.id, first.id);
message::delete_msgs(&t, &[first.id]).await?; message::delete_msgs(alice, &[first.id]).await?;
let parent = get_parent_message(&t, &mime_parser).await?.unwrap(); let parent = get_parent_message(alice, &mime_parser).await?.unwrap();
assert_eq!(parent.id, second.id); assert_eq!(parent.id, second.id);
message::delete_msgs(&t, &[second.id]).await?; message::delete_msgs(alice, &[second.id]).await?;
let parent = get_parent_message(&t, &mime_parser).await?.unwrap(); let parent = get_parent_message(alice, &mime_parser).await?.unwrap();
assert_eq!(parent.id, third.id); assert_eq!(parent.id, third.id);
message::delete_msgs(&t, &[third.id]).await?; message::delete_msgs(alice, &[third.id]).await?;
let parent = get_parent_message(&t, &mime_parser).await?; let parent = get_parent_message(alice, &mime_parser).await?;
assert!(parent.is_none()); assert!(parent.is_none());
Ok(()) Ok(())
@@ -3022,7 +3097,8 @@ async fn test_auto_accept_for_bots() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_auto_accept_group_for_bots() -> Result<()> { async fn test_auto_accept_group_for_bots() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::Bot, Some("1")).await.unwrap(); t.set_config_bool(Config::Bot, true).await.unwrap();
t.allow_unencrypted().await.unwrap();
let msg = load_imf_email(&t, GRP_MAIL).await; let msg = load_imf_email(&t, GRP_MAIL).await;
let chat = chat::Chat::load_from_db(&t, msg.chat_id).await?; let chat = chat::Chat::load_from_db(&t, msg.chat_id).await?;
@@ -3251,6 +3327,7 @@ async fn test_blocked_contact_creates_group() -> Result<()> {
async fn test_outgoing_undecryptable() -> Result<()> { async fn test_outgoing_undecryptable() -> Result<()> {
let alice = &TestContext::new().await; let alice = &TestContext::new().await;
alice.configure_addr("alice@example.org").await; alice.configure_addr("alice@example.org").await;
alice.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml"); let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
receive_imf(alice, raw, false).await?; receive_imf(alice, raw, false).await?;
@@ -3287,6 +3364,7 @@ async fn test_outgoing_undecryptable() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_autocrypt() -> Result<()> { async fn test_thunderbird_autocrypt() -> Result<()> {
let t = TestContext::new_bob().await; let t = TestContext::new_bob().await;
t.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml"); let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
let received_msg = receive_imf(&t, raw, false).await?.unwrap(); let received_msg = receive_imf(&t, raw, false).await?.unwrap();
@@ -3334,6 +3412,7 @@ async fn test_issuer_fingerprint() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> { async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> {
let t = TestContext::new_bob().await; let t = TestContext::new_bob().await;
t.allow_unencrypted().await?;
// The message has public key attached *and* Autocrypt header. // The message has public key attached *and* Autocrypt header.
// //
@@ -3384,7 +3463,9 @@ async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forged_from_and_no_valid_signatures() -> Result<()> { async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
let t = &TestContext::new_bob().await; let mut tcm = TestContextManager::new();
let t = &tcm.bob().await;
t.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml"); let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml");
let received_msg = receive_imf(t, raw, false).await?.unwrap(); let received_msg = receive_imf(t, raw, false).await?.unwrap();
@@ -3394,7 +3475,8 @@ async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
assert!(!msg.chat_id.is_trash()); assert!(!msg.chat_id.is_trash());
assert!(!msg.get_showpadlock()); assert!(!msg.get_showpadlock());
let t = &TestContext::new_bob().await; let t = &tcm.bob().await;
t.allow_unencrypted().await?;
let raw = String::from_utf8(raw.to_vec())?.replace("alice@example.org", "clarice@example.org"); let raw = String::from_utf8(raw.to_vec())?.replace("alice@example.org", "clarice@example.org");
let received_msg = receive_imf(t, raw.as_bytes(), false).await?.unwrap(); let received_msg = receive_imf(t, raw.as_bytes(), false).await?.unwrap();
assert!(received_msg.chat_id.is_trash()); assert!(received_msg.chat_id.is_trash());
@@ -3405,6 +3487,8 @@ async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> { async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> {
let t = &TestContext::new_bob().await; let t = &TestContext::new_bob().await;
t.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml"); let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml");
let raw = String::from_utf8(raw.to_vec())?.replace("From: Alice", "From: A"); let raw = String::from_utf8(raw.to_vec())?.replace("From: Alice", "From: A");
receive_imf(t, raw.as_bytes(), false).await?.unwrap(); receive_imf(t, raw.as_bytes(), false).await?.unwrap();
@@ -3419,6 +3503,7 @@ async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> { async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
let bob = &TestContext::new_bob().await; let bob = &TestContext::new_bob().await;
bob.allow_unencrypted().await?;
// Thunderbird message with Autocrypt header and a signature, // Thunderbird message with Autocrypt header and a signature,
// but not encrypted. // but not encrypted.
@@ -3457,6 +3542,8 @@ async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_unsigned() -> Result<()> { async fn test_thunderbird_unsigned() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
// TODO: same test without process unencrypted should trash the message
alice.allow_unencrypted().await.unwrap();
// Alice receives an unsigned message from Bob. // Alice receives an unsigned message from Bob.
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_unsigned.eml"); let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_unsigned.eml");
@@ -3476,6 +3563,7 @@ async fn test_thunderbird_unsigned() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_unsigned_with_unencrypted_subject() -> Result<()> { async fn test_thunderbird_unsigned_with_unencrypted_subject() -> Result<()> {
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
bob.allow_unencrypted().await.unwrap();
let raw = include_bytes!( let raw = include_bytes!(
"../../test-data/message/thunderbird_encrypted_unsigned_with_unencrypted_subject.eml" "../../test-data/message/thunderbird_encrypted_unsigned_with_unencrypted_subject.eml"
@@ -3508,18 +3596,20 @@ async fn test_messed_up_message_id() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_big_forwarded_with_big_attachment() -> Result<()> { async fn test_big_forwarded_with_big_attachment() -> Result<()> {
let t = &TestContext::new_bob().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let raw = include_bytes!("../../test-data/message/big_forwarded_with_big_attachment.eml"); let raw = include_bytes!("../../test-data/message/big_forwarded_with_big_attachment.eml");
let rcvd = receive_imf(t, raw, false).await?.unwrap(); let rcvd = test_utils::receive_encrypted_imf(bob, alice, raw).await?;
assert_eq!(rcvd.msg_ids.len(), 3); assert_eq!(rcvd.msg_ids.len(), 3);
let msg = Message::load_from_db(t, rcvd.msg_ids[0]).await?; let msg = Message::load_from_db(bob, rcvd.msg_ids[0]).await?;
assert_eq!(msg.get_viewtype(), Viewtype::Text); assert_eq!(msg.get_viewtype(), Viewtype::Text);
assert_eq!(msg.get_text(), "Hello!"); assert_eq!(msg.get_text(), "Hello!");
assert!(!msg.has_html()); assert!(!msg.has_html());
let msg = Message::load_from_db(t, rcvd.msg_ids[1]).await?; let msg = Message::load_from_db(bob, rcvd.msg_ids[1]).await?;
assert_eq!(msg.get_viewtype(), Viewtype::Text); assert_eq!(msg.get_viewtype(), Viewtype::Text);
assert!( assert!(
msg.get_text() msg.get_text()
@@ -3528,10 +3618,10 @@ async fn test_big_forwarded_with_big_attachment() -> Result<()> {
assert!(msg.get_text().ends_with("[...]")); assert!(msg.get_text().ends_with("[...]"));
assert!(!msg.has_html()); assert!(!msg.has_html());
let msg = Message::load_from_db(t, rcvd.msg_ids[2]).await?; let msg = Message::load_from_db(bob, rcvd.msg_ids[2]).await?;
assert_eq!(msg.get_viewtype(), Viewtype::File); assert_eq!(msg.get_viewtype(), Viewtype::File);
assert!(msg.has_html()); assert!(msg.has_html());
let html = msg.id.get_html(t).await?.unwrap(); let html = msg.id.get_html(bob).await?.unwrap();
let tail = html let tail = html
.split_once("Hello!") .split_once("Hello!")
.unwrap() .unwrap()
@@ -3566,6 +3656,7 @@ async fn test_big_forwarded_with_big_attachment() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_user_adds_member() -> Result<()> { async fn test_mua_user_adds_member() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await?;
receive_imf( receive_imf(
&t, &t,
@@ -3617,6 +3708,7 @@ async fn test_mua_user_adds_member() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_user_adds_recipient_to_single_chat() -> Result<()> { async fn test_mua_user_adds_recipient_to_single_chat() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
// Alice sends a 1:1 message to Bob, creating a 1:1 chat. // Alice sends a 1:1 message to Bob, creating a 1:1 chat.
let msg = receive_imf( let msg = receive_imf(
@@ -4049,6 +4141,7 @@ async fn test_dont_readd_with_normal_msg() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_cant_remove() -> Result<()> { async fn test_mua_cant_remove() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
let now = time(); let now = time();
@@ -4141,6 +4234,7 @@ async fn test_mua_cant_remove() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_can_add() -> Result<()> { async fn test_mua_can_add() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
let now = time(); let now = time();
@@ -4200,6 +4294,7 @@ async fn test_mua_can_add() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_can_readd() -> Result<()> { async fn test_mua_can_readd() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
// Alice creates chat with 3 contacts. // Alice creates chat with 3 contacts.
let msg = receive_imf( let msg = receive_imf(
@@ -4363,6 +4458,8 @@ async fn test_keep_member_list_if_possibly_nomember() -> Result<()> {
async fn test_adhoc_grp_name_no_prefix() -> Result<()> { async fn test_adhoc_grp_name_no_prefix() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
let chat_id = receive_imf( let chat_id = receive_imf(
alice, alice,
b"Subject: Re: Once upon a time this was with the only Re: here\n\ b"Subject: Re: Once upon a time this was with the only Re: here\n\
@@ -4398,12 +4495,12 @@ async fn test_outgoing_msg_forgery() -> Result<()> {
imex(alice, ImexMode::ExportSelfKeys, export_dir.path(), None).await?; 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 // We need Bob only to encrypt the forged message to Alice's key, actually Bob doesn't
// participate in the scenario. // 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); assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 0);
bob.configure_addr("bob@example.net").await; bob.configure_addr("bob@example.net").await;
imex(bob, ImexMode::ImportSelfKeys, export_dir.path(), None).await?; imex(bob, ImexMode::ImportSelfKeys, export_dir.path(), None).await?;
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1); 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; malice.configure_addr(alice_addr).await;
let malice_chat_id = tcm let malice_chat_id = tcm
@@ -4413,9 +4510,8 @@ async fn test_outgoing_msg_forgery() -> Result<()> {
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1); assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1);
let sent_msg = malice.send_text(malice_chat_id, "hi from malice").await; let sent_msg = malice.send_text(malice_chat_id, "hi from malice").await;
let msg = alice.recv_msg(&sent_msg).await; let msg = alice.recv_msg_opt(&sent_msg).await;
assert_eq!(msg.state, MessageState::OutDelivered); assert!(msg.is_none());
assert!(!msg.get_showpadlock());
Ok(()) Ok(())
} }
@@ -4510,6 +4606,7 @@ async fn test_protected_group_add_remove_member_missing_key() -> Result<()> {
async fn test_older_message_from_2nd_device() -> Result<()> { async fn test_older_message_from_2nd_device() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
let chat_id = alice let chat_id = alice
.create_chat_with_contact("", "bob@example.net") .create_chat_with_contact("", "bob@example.net")
.await .await
@@ -4645,6 +4742,7 @@ async fn test_forged_from() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_multiline_iso_8859_1_subject() -> Result<()> { async fn test_multiline_iso_8859_1_subject() -> Result<()> {
let t = &TestContext::new_alice().await; let t = &TestContext::new_alice().await;
t.allow_unencrypted().await?;
let mail = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ let mail = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
From: bob@example.com\n\ From: bob@example.com\n\
To: alice@example.org, claire@example.com\n\ To: alice@example.org, claire@example.com\n\
@@ -4709,6 +4807,7 @@ async fn test_references() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_list_from() -> Result<()> { async fn test_list_from() -> Result<()> {
let t = &TestContext::new_alice().await; let t = &TestContext::new_alice().await;
t.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/list-from.eml"); let raw = include_bytes!("../../test-data/message/list-from.eml");
let received = receive_imf(t, raw, false).await?.unwrap(); let received = receive_imf(t, raw, false).await?.unwrap();
@@ -4838,6 +4937,7 @@ async fn test_make_n_send_vcard() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_unencrypted_group_id_no_recipients() -> Result<()> { async fn test_unencrypted_group_id_no_recipients() -> Result<()> {
let t = &TestContext::new_alice().await; let t = &TestContext::new_alice().await;
t.allow_unencrypted().await?;
let raw = "From: alice@example.org let raw = "From: alice@example.org
Subject: Group Subject: Group
Chat-Version: 1.0 Chat-Version: 1.0
@@ -5129,10 +5229,12 @@ async fn test_recv_outgoing_msg_no_intended_recipient_fingerprint() -> Result<()
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sanitize_filename_in_received() -> Result<()> { async fn test_sanitize_filename_in_received() -> Result<()> {
let alice = &TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let raw = b"Message-ID: Mr.XA6y3og8-az.WGbH9_dNcQx@testr let raw = b"Message-ID: Mr.XA6y3og8-az.WGbH9_dNcQx@testr
To: <tmp_5890965001269692@testrun.org> To: <alice@example.org>
From: \"=?utf-8?q??=\" <tmp_6272287793210918@testrun.org> From: \"=?utf-8?q??=\" <bob@example.net>
Content-Type: multipart/mixed; boundary=\"mwkNRwaJw1M5n2xcr2ODfAqvTjcj9Z\" Content-Type: multipart/mixed; boundary=\"mwkNRwaJw1M5n2xcr2ODfAqvTjcj9Z\"
@@ -5151,7 +5253,7 @@ PGh0bWw+PGJvZHk+dGV4dDwvYm9keT5kYXRh
--mwkNRwaJw1M5n2xcr2ODfAqvTjcj9Z--"; --mwkNRwaJw1M5n2xcr2ODfAqvTjcj9Z--";
let msg = receive_imf(alice, raw, false).await?.unwrap(); let msg = test_utils::receive_encrypted_imf(alice, bob, raw).await?;
let msg = Message::load_from_db(alice, msg.msg_ids[0]).await?; let msg = Message::load_from_db(alice, msg.msg_ids[0]).await?;
assert_eq!(msg.get_filename().unwrap(), "test.HTML"); assert_eq!(msg.get_filename().unwrap(), "test.HTML");
@@ -5267,6 +5369,7 @@ async fn test_outgoing_unencrypted_chat_assignment() {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await.unwrap();
tcm.section("Alice receives unencrypted message from Bob"); tcm.section("Alice receives unencrypted message from Bob");
receive_imf( receive_imf(
@@ -5311,6 +5414,7 @@ async fn test_outgoing_unencrypted_chat_assignment() {
async fn test_incoming_reply_with_date_in_past() -> Result<()> { async fn test_incoming_reply_with_date_in_past() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await?;
let msg0 = receive_imf( let msg0 = receive_imf(
alice, alice,
@@ -5452,6 +5556,8 @@ async fn test_small_unencrypted_group() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await?;
bob.allow_unencrypted().await?;
let alice_chat_id = chat::create_group_unencrypted(alice, "Unencrypted group").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; let alice_bob_id = alice.add_or_lookup_address_contact_id(bob).await;
@@ -5484,6 +5590,7 @@ async fn test_small_unencrypted_group() -> Result<()> {
async fn test_bcc_not_a_group() -> Result<()> { async fn test_bcc_not_a_group() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
alice.allow_unencrypted().await.unwrap();
let received = receive_imf( let received = receive_imf(
alice, alice,
@@ -5531,6 +5638,7 @@ async fn test_lookup_key_contact_by_address_self() -> Result<()> {
async fn test_calendar_alternative() -> Result<()> { async fn test_calendar_alternative() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let t = &tcm.alice().await; let t = &tcm.alice().await;
t.allow_unencrypted().await?;
let raw = include_bytes!("../../test-data/message/calendar-alternative.eml"); let raw = include_bytes!("../../test-data/message/calendar-alternative.eml");
let msg = receive_imf(t, raw, false).await?.unwrap(); let msg = receive_imf(t, raw, false).await?.unwrap();
assert_eq!(msg.msg_ids.len(), 1); assert_eq!(msg.msg_ids.len(), 1);

View File

@@ -772,6 +772,7 @@ fn manipulate_qr(v3: bool, remove_invite: bool, qr: &mut String) {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_no_qr() -> Result<()> { async fn test_adhoc_group_no_qr() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.allow_unencrypted().await?;
let mime = br#"Subject: First thread let mime = br#"Subject: First thread
Message-ID: first@example.org Message-ID: first@example.org
@@ -1415,6 +1416,7 @@ async fn test_vc_request_encrypted_at_rest() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await; let alice = &tcm.alice().await;
let bob = &tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await?;
let qr = get_securejoin_qr(alice, None).await?; let qr = get_securejoin_qr(alice, None).await?;

View File

@@ -41,7 +41,7 @@ use crate::login_param::EnteredLoginParam;
use crate::message::{Message, MessageState, MsgId, update_msg_state}; use crate::message::{Message, MessageState, MsgId, update_msg_state};
use crate::mimeparser::{MimeMessage, SystemMessage}; use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::pgp::SeipdVersion; use crate::pgp::SeipdVersion;
use crate::receive_imf::receive_imf; use crate::receive_imf::{ReceivedMsg, receive_imf};
use crate::securejoin::{get_securejoin_qr, join_securejoin}; use crate::securejoin::{get_securejoin_qr, join_securejoin};
use crate::smtp::msg_has_pending_smtp_job; use crate::smtp::msg_has_pending_smtp_job;
use crate::stock_str::StockStrings; use crate::stock_str::StockStrings;
@@ -834,10 +834,7 @@ ORDER BY id"
/// Receive a message using the `receive_imf()` pipeline. This is similar /// Receive a message using the `receive_imf()` pipeline. This is similar
/// to `recv_msg()`, but doesn't assume that the message is shown in the chat. /// to `recv_msg()`, but doesn't assume that the message is shown in the chat.
pub async fn recv_msg_opt( pub async fn recv_msg_opt(&self, msg: &SentMessage<'_>) -> Option<ReceivedMsg> {
&self,
msg: &SentMessage<'_>,
) -> Option<crate::receive_imf::ReceivedMsg> {
receive_imf(self, msg.payload().as_bytes(), false) receive_imf(self, msg.payload().as_bytes(), false)
.await .await
.unwrap() .unwrap()
@@ -1232,6 +1229,12 @@ ORDER BY id"
.await .await
.unwrap(); .unwrap();
} }
/// Allow reception of unencrypted messages.
pub async fn allow_unencrypted(&self) -> Result<()> {
self.set_config_bool(Config::ForceEncryption, false).await?;
Ok(())
}
} }
pub async fn encrypt_raw_message( pub async fn encrypt_raw_message(
@@ -1284,6 +1287,18 @@ Content-Type: application/octet-stream\r
Ok(res) Ok(res)
} }
pub async fn receive_encrypted_imf(
context: &TestContext,
from: &TestContext,
imf_raw: &[u8],
) -> Result<ReceivedMsg> {
let encrypted_message = encrypt_raw_message(from, &[context], imf_raw).await?;
let received_msg = receive_imf(context, encrypted_message.as_bytes(), false)
.await?
.unwrap();
Ok(received_msg)
}
impl Deref for TestContext { impl Deref for TestContext {
type Target = Context; type Target = Context;

View File

@@ -13,6 +13,7 @@ use crate::mimeparser::SystemMessage;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::securejoin::{get_securejoin_qr, join_securejoin}; use crate::securejoin::{get_securejoin_qr, join_securejoin};
use crate::stock_str; use crate::stock_str;
use crate::test_utils;
use crate::test_utils::{ use crate::test_utils::{
E2EE_INFO_MSGS, TestContext, TestContextManager, get_chat_msg, mark_as_verified, E2EE_INFO_MSGS, TestContext, TestContextManager, get_chat_msg, mark_as_verified,
}; };
@@ -172,32 +173,33 @@ async fn test_missing_key_reexecute_securejoin() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_degrade_verified_oneonone_chat() -> Result<()> { async fn test_degrade_verified_oneonone_chat() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = tcm.alice().await; let alice = &tcm.alice().await;
let bob = tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await?;
mark_as_verified(&alice, &bob).await; mark_as_verified(alice, bob).await;
let alice_chat = alice.create_chat(&bob).await; let alice_chat = alice.create_chat(bob).await;
receive_imf( receive_imf(
&alice, alice,
b"From: Bob <bob@example.net>\n\ b"From: Bob <bob@example.net>\r\n\
To: alice@example.org\n\ To: alice@example.org\r\n\
Message-ID: <1234-2@example.org>\n\ Message-ID: <1234-2@example.net>\r\n\
\n\ \r\n\
hello\n", hello\r\n",
false, false,
) )
.await?; .await?;
let msg0 = get_chat_msg(&alice, alice_chat.id, 0, 1).await; let msg0 = get_chat_msg(alice, alice_chat.id, 0, 1).await;
let enabled = stock_str::messages_e2ee_info_msg(&alice); let enabled = stock_str::messages_e2ee_info_msg(alice);
assert_eq!(msg0.text, enabled); assert_eq!(msg0.text, enabled);
assert_eq!(msg0.param.get_cmd(), SystemMessage::ChatE2ee); assert_eq!(msg0.param.get_cmd(), SystemMessage::ChatE2ee);
let email_chat = alice.get_email_chat(&bob).await; let email_chat = alice.get_email_chat(bob).await;
assert!(!email_chat.is_encrypted(&alice).await?); assert!(!email_chat.is_encrypted(alice).await?);
let email_msg = get_chat_msg(&alice, email_chat.id, 0, 1).await; let email_msg = get_chat_msg(alice, email_chat.id, 0, 1).await;
assert_eq!(email_msg.text, "hello".to_string()); assert_eq!(email_msg.text, "hello".to_string());
assert!(!email_msg.is_system_message()); assert!(!email_msg.is_system_message());
@@ -209,32 +211,32 @@ async fn test_degrade_verified_oneonone_chat() -> Result<()> {
/// This test tests that the messages are still in the right order. /// This test tests that the messages are still in the right order.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_old_message_4() -> Result<()> { async fn test_old_message_4() -> Result<()> {
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let msg_incoming = receive_imf( let alice = &tcm.alice().await;
&alice, let bob = &tcm.bob().await;
b"From: Bob <bob@example.net>\n\ let msg_incoming = test_utils::receive_encrypted_imf(
To: alice@example.org\n\ alice,
Message-ID: <1234-2-3@example.org>\n\ bob,
Date: Sun, 08 Dec 2019 19:00:27 +0000\n\ b"From: Bob <bob@example.net>\r\n\
\n\ To: alice@example.org\r\n\
Thanks, Alice!\n", Message-ID: <1234-2-3@example.org>\r\n\
true, Date: Sun, 08 Dec 2019 19:00:27 +0000\r\n\
\r\n\
Thanks, Alice!\r\n",
) )
.await? .await?;
.unwrap();
let msg_sent = receive_imf( let msg_sent = test_utils::receive_encrypted_imf(
&alice, alice,
b"From: alice@example.org\n\ alice,
To: Bob <bob@example.net>\n\ b"From: alice@example.org\r\n\
Message-ID: <1234-2-4@example.org>\n\ To: Bob <bob@example.net>\r\n\
Date: Sat, 07 Dec 2019 19:00:27 +0000\n\ Message-ID: <1234-2-4@example.org>\r\n\
\n\ Date: Sat, 07 Dec 2019 19:00:27 +0000\r\n\
Happy birthday, Bob!\n", \r\n\
true, Happy birthday, Bob!\r\n",
) )
.await? .await?;
.unwrap();
// The "Happy birthday" message should be shown first, and then the "Thanks" message // The "Happy birthday" message should be shown first, and then the "Thanks" message
assert!(msg_sent.sort_timestamp < msg_incoming.sort_timestamp); assert!(msg_sent.sort_timestamp < msg_incoming.sort_timestamp);
@@ -269,17 +271,18 @@ async fn test_mdn_doesnt_disable_verification() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_mua_msg() -> Result<()> { async fn test_outgoing_mua_msg() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = tcm.alice().await; let alice = &tcm.alice().await;
let bob = tcm.bob().await; let bob = &tcm.bob().await;
alice.allow_unencrypted().await?;
mark_as_verified(&alice, &bob).await; mark_as_verified(alice, bob).await;
mark_as_verified(&bob, &alice).await; mark_as_verified(bob, alice).await;
tcm.send_recv_accept(&bob, &alice, "Heyho from DC").await; tcm.send_recv_accept(bob, alice, "Heyho from DC").await;
assert_verified(&alice, &bob).await; assert_verified(alice, bob).await;
let sent = receive_imf( let sent = receive_imf(
&alice, alice,
b"From: alice@example.org\n\ b"From: alice@example.org\n\
To: bob@example.net\n\ To: bob@example.net\n\
\n\ \n\
@@ -288,7 +291,7 @@ async fn test_outgoing_mua_msg() -> Result<()> {
) )
.await? .await?
.unwrap(); .unwrap();
tcm.send_recv(&alice, &bob, "Sending with DC again").await; tcm.send_recv(alice, bob, "Sending with DC again").await;
// Unencrypted message from MUA gets into a separate chat. // Unencrypted message from MUA gets into a separate chat.
// PGP chat gets all encrypted messages. // PGP chat gets all encrypted messages.
@@ -296,7 +299,7 @@ async fn test_outgoing_mua_msg() -> Result<()> {
.golden_test_chat(sent.chat_id, "test_outgoing_mua_msg") .golden_test_chat(sent.chat_id, "test_outgoing_mua_msg")
.await; .await;
alice alice
.golden_test_chat(alice.get_chat(&bob).await.id, "test_outgoing_mua_msg_pgp") .golden_test_chat(alice.get_chat(bob).await.id, "test_outgoing_mua_msg_pgp")
.await; .await;
Ok(()) Ok(())
@@ -332,6 +335,7 @@ async fn test_reply() -> Result<()> {
let mut tcm = TestContextManager::new(); let mut tcm = TestContextManager::new();
let alice = tcm.alice().await; let alice = tcm.alice().await;
let bob = tcm.bob().await; let bob = tcm.bob().await;
alice.allow_unencrypted().await?;
if verified { if verified {
mark_as_verified(&alice, &bob).await; mark_as_verified(&alice, &bob).await;

View File

@@ -60,6 +60,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
async fn check_parse_receive_headers_integration(raw: &[u8], expected: &str) { async fn check_parse_receive_headers_integration(raw: &[u8], expected: &str) {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.allow_unencrypted().await.unwrap();
let received = receive_imf(&t, raw, false).await.unwrap().unwrap(); let received = receive_imf(&t, raw, false).await.unwrap().unwrap();
assert_eq!(received.msg_ids.len(), 1); assert_eq!(received.msg_ids.len(), 1);

View File

@@ -87,8 +87,8 @@ impl Params {
mod tests { mod tests {
use super::*; use super::*;
use crate::chat::Chat; use crate::chat::Chat;
use crate::receive_imf::receive_imf; use crate::test_utils;
use crate::test_utils::TestContext; use crate::test_utils::TestContextManager;
use crate::tools::time; use crate::tools::time;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -115,37 +115,39 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_out_of_order_subject() -> Result<()> { async fn test_out_of_order_subject() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
receive_imf( test_utils::receive_encrypted_imf(
&t, alice,
b"From: Bob Authname <bob@example.org>\n\ bob,
To: alice@example.org\n\ b"From: Bob Authname <bob@example.net>\r\n\
Subject: updated subject\n\ To: alice@example.org\r\n\
Message-ID: <msg2@example.org>\n\ Subject: updated subject\r\n\
Chat-Version: 1.0\n\ Message-ID: <msg2@example.org>\r\n\
Date: Sun, 22 Mar 2021 23:37:57 +0000\n\ Chat-Version: 1.0\r\n\
\n\ Date: Sun, 22 Mar 2021 23:37:57 +0000\r\n\
second message\n", \r\n\
false, second message\r\n",
) )
.await?; .await?;
receive_imf( test_utils::receive_encrypted_imf(
&t, alice,
b"From: Bob Authname <bob@example.org>\n\ bob,
To: alice@example.org\n\ b"From: Bob Authname <bob@example.net>\r\n\
Subject: original subject\n\ To: alice@example.org\r\n\
Message-ID: <msg1@example.org>\n\ Subject: original subject\r\n\
Chat-Version: 1.0\n\ Message-ID: <msg1@example.org>\r\n\
Date: Sun, 22 Mar 2021 22:37:57 +0000\n\ Chat-Version: 1.0\r\n\
\n\ Date: Sun, 22 Mar 2021 22:37:57 +0000\r\n\
first message\n", \r\n\
false, first message\r\n",
) )
.await?; .await?;
let msg = t.get_last_msg().await; let msg = alice.get_last_msg().await;
let chat = Chat::load_from_db(&t, msg.chat_id).await?; let chat = Chat::load_from_db(alice, msg.chat_id).await?;
assert_eq!( assert_eq!(
chat.param.get(Param::LastSubject).unwrap(), chat.param.get(Param::LastSubject).unwrap(),
"updated subject" "updated subject"

View File

@@ -11,8 +11,8 @@ use crate::chat::{
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::config::Config; use crate::config::Config;
use crate::ephemeral; use crate::ephemeral;
use crate::receive_imf::receive_imf;
use crate::securejoin::get_securejoin_qr; use crate::securejoin::get_securejoin_qr;
use crate::test_utils;
use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager}; use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager};
use crate::tools::{self, SystemTime}; use crate::tools::{self, SystemTime};
use crate::{message, sql}; use crate::{message, sql};
@@ -257,24 +257,26 @@ async fn test_resend_webxdc_instance_and_info() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_receive_webxdc_instance() -> Result<()> { async fn test_receive_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
receive_imf( let alice = &tcm.alice().await;
&t, let bob = &tcm.bob().await;
test_utils::receive_encrypted_imf(
alice,
bob,
include_bytes!("../../test-data/message/webxdc_good_extension.eml"), include_bytes!("../../test-data/message/webxdc_good_extension.eml"),
false,
) )
.await?; .await?;
let instance = t.get_last_msg().await; let instance = alice.get_last_msg().await;
assert_eq!(instance.viewtype, Viewtype::Webxdc); assert_eq!(instance.viewtype, Viewtype::Webxdc);
assert_eq!(instance.get_filename().unwrap(), "minimal.xdc"); assert_eq!(instance.get_filename().unwrap(), "minimal.xdc");
receive_imf( test_utils::receive_encrypted_imf(
&t, alice,
bob,
include_bytes!("../../test-data/message/webxdc_bad_extension.eml"), include_bytes!("../../test-data/message/webxdc_bad_extension.eml"),
false,
) )
.await?; .await?;
let instance = t.get_last_msg().await; let instance = alice.get_last_msg().await;
assert_eq!(instance.viewtype, Viewtype::File); // we require the correct extension, only a mime type is not sufficient assert_eq!(instance.viewtype, Viewtype::File); // we require the correct extension, only a mime type is not sufficient
assert_eq!(instance.get_filename().unwrap(), "index.html"); assert_eq!(instance.get_filename().unwrap(), "index.html");
@@ -682,13 +684,16 @@ async fn expect_status_update_event(t: &TestContext, instance_id: MsgId) -> Resu
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_webxdc_status_update() -> Result<()> { async fn test_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await; let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice.set_config_bool(Config::BccSelf, true).await?; alice.set_config_bool(Config::BccSelf, true).await?;
let bob = TestContext::new_bob().await; alice.allow_unencrypted().await?;
let bob = &tcm.bob().await;
bob.allow_unencrypted().await?;
// Alice sends an webxdc instance and a status update // Alice sends an webxdc instance and a status update
let alice_chat = alice.create_email_chat(&bob).await; let alice_chat = alice.create_email_chat(bob).await;
let alice_instance = send_webxdc_instance(&alice, alice_chat.id).await?; let alice_instance = send_webxdc_instance(alice, alice_chat.id).await?;
let sent1 = &alice.pop_sent_msg().await; let sent1 = &alice.pop_sent_msg().await;
assert_eq!(alice_instance.viewtype, Viewtype::Webxdc); assert_eq!(alice_instance.viewtype, Viewtype::Webxdc);
assert!(!sent1.payload().contains("report-type=status-update")); assert!(!sent1.payload().contains("report-type=status-update"));
@@ -697,7 +702,7 @@ async fn test_send_webxdc_status_update() -> Result<()> {
.send_webxdc_status_update(alice_instance.id, r#"{"payload" : {"foo":"bar"}}"#) .send_webxdc_status_update(alice_instance.id, r#"{"payload" : {"foo":"bar"}}"#)
.await?; .await?;
alice.flush_status_updates().await?; alice.flush_status_updates().await?;
expect_status_update_event(&alice, alice_instance.id).await?; expect_status_update_event(alice, alice_instance.id).await?;
let sent2 = &alice.pop_sent_msg().await; let sent2 = &alice.pop_sent_msg().await;
let alice_update = sent2.load_from_db().await; let alice_update = sent2.load_from_db().await;
assert!(alice_update.hidden); assert!(alice_update.hidden);
@@ -706,10 +711,10 @@ async fn test_send_webxdc_status_update() -> Result<()> {
assert_eq!(alice_update.text, BODY_DESCR.to_string()); assert_eq!(alice_update.text, BODY_DESCR.to_string());
assert_eq!(alice_update.chat_id, alice_instance.chat_id); assert_eq!(alice_update.chat_id, alice_instance.chat_id);
assert_eq!( assert_eq!(
alice_update.parent(&alice).await?.unwrap().id, alice_update.parent(alice).await?.unwrap().id,
alice_instance.id alice_instance.id
); );
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1); assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, 1);
assert!(sent2.payload().contains("report-type=status-update")); assert!(sent2.payload().contains("report-type=status-update"));
assert!(sent2.payload().contains(BODY_DESCR)); assert!(sent2.payload().contains(BODY_DESCR));
assert_eq!( assert_eq!(
@@ -735,12 +740,12 @@ async fn test_send_webxdc_status_update() -> Result<()> {
let bob_chat_id = bob_instance.chat_id; let bob_chat_id = bob_instance.chat_id;
assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid); assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid);
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc); assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1); assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 1);
let bob_received_update = bob.recv_msg_opt(sent2).await; let bob_received_update = bob.recv_msg_opt(sent2).await;
assert!(bob_received_update.is_none()); assert!(bob_received_update.is_none());
expect_status_update_event(&bob, bob_instance.id).await?; expect_status_update_event(bob, bob_instance.id).await?;
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1); assert_eq!(bob_chat_id.get_msg_cnt(bob).await?, 1);
assert_eq!( assert_eq!(
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
@@ -749,19 +754,20 @@ async fn test_send_webxdc_status_update() -> Result<()> {
); );
// Alice has a second device and also receives messages there // Alice has a second device and also receives messages there
let alice2 = TestContext::new_alice().await; let alice2 = &tcm.alice().await;
alice2.allow_unencrypted().await?;
alice2.recv_msg(sent1).await; alice2.recv_msg(sent1).await;
alice2.recv_msg_trash(sent2).await; alice2.recv_msg_trash(sent2).await;
let alice2_instance = alice2.get_last_msg().await; let alice2_instance = alice2.get_last_msg().await;
let alice2_chat_id = alice2_instance.chat_id; let alice2_chat_id = alice2_instance.chat_id;
assert_eq!(alice2_instance.viewtype, Viewtype::Webxdc); assert_eq!(alice2_instance.viewtype, Viewtype::Webxdc);
assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 1); assert_eq!(alice2_chat_id.get_msg_cnt(alice2).await?, 1);
// To support the second device, Alice has enabled bcc_self and will receive their own messages; // To support the second device, Alice has enabled bcc_self and will receive their own messages;
// these messages, however, should be ignored // these messages, however, should be ignored
alice.recv_msg_opt(sent1).await; alice.recv_msg_opt(sent1).await;
alice.recv_msg_opt(sent2).await; alice.recv_msg_opt(sent2).await;
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1); assert_eq!(alice_chat.id.get_msg_cnt(alice).await?, 1);
assert_eq!( assert_eq!(
alice alice
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0)) .get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
@@ -983,7 +989,7 @@ async fn test_pop_status_update() -> Result<()> {
async fn test_draft_and_send_webxdc_status_update() -> Result<()> { async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().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, // prepare webxdc instance,
// status updates are not sent for drafts, therefore send_webxdc_status_update() returns Ok(None) // status updates are not sent for drafts, therefore send_webxdc_status_update() returns Ok(None)
@@ -1030,8 +1036,6 @@ async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
let bob_instance = bob.recv_msg(&sent1).await; let bob_instance = bob.recv_msg(&sent1).await;
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc); assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert_eq!(bob_instance.get_filename().unwrap(), "minimal.xdc"); 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!( assert_eq!(
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
.await?, .await?,

View File

@@ -1,28 +1,27 @@
Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET) From: Test Sender <bob@example.net>
From: Test Sender <sender@testrun.org> Subject: Large image test
Subject: Large image test To: alice@example.org
To: alice@example.org Message-ID: <big-image-test@testrun.org>
Message-ID: <big-image-test@testrun.org> Date: Thu, 17 Dec 2020 15:38:45 +0100
Date: Thu, 17 Dec 2020 15:38:45 +0100 User-Agent: Test-Agent/1.0
User-Agent: Test-Agent/1.0 MIME-Version: 1.0
MIME-Version: 1.0 Content-Type: multipart/mixed;
Content-Type: multipart/mixed; boundary="------------BIG_IMAGE_BOUNDARY"
boundary="------------BIG_IMAGE_BOUNDARY" Content-Language: en-US
Content-Language: en-US
This is a multi-part message in MIME format.
This is a multi-part message in MIME format. --------------BIG_IMAGE_BOUNDARY
--------------BIG_IMAGE_BOUNDARY Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit
Content-Transfer-Encoding: 7bit
Here is a 4k image
Here is a 4k image
--------------BIG_IMAGE_BOUNDARY
--------------BIG_IMAGE_BOUNDARY Content-Type: image/png;
Content-Type: image/png; name="4k_image.png"
name="4k_image.png" Content-Transfer-Encoding: base64
Content-Transfer-Encoding: base64 Content-Disposition: attachment;
Content-Disposition: attachment; filename="4k_image.png"
filename="4k_image.png"
iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg==
iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAIAAAAf3FwlAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg== --------------BIG_IMAGE_BOUNDARY--
--------------BIG_IMAGE_BOUNDARY--

View File

@@ -1,28 +1,27 @@
Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET) From: Test Sender <bob@example.net>
From: Test Sender <sender@testrun.org> Subject: Large image test
Subject: Large image test To: alice@example.org
To: alice@example.org Message-ID: <huge-image-test@localhost>
Message-ID: <huge-image-test@testrun.org> Date: Thu, 17 Dec 2020 15:38:45 +0100
Date: Thu, 17 Dec 2020 15:38:45 +0100 User-Agent: Test-Agent/1.0
User-Agent: Test-Agent/1.0 MIME-Version: 1.0
MIME-Version: 1.0 Content-Type: multipart/mixed;
Content-Type: multipart/mixed; boundary="------------HUGE_IMAGE_BOUNDARY"
boundary="------------HUGE_IMAGE_BOUNDARY" Content-Language: en-US
Content-Language: en-US
This is a multi-part message in MIME format.
This is a multi-part message in MIME format. --------------HUGE_IMAGE_BOUNDARY
--------------HUGE_IMAGE_BOUNDARY Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit
Content-Transfer-Encoding: 7bit
Here is a huge image
Here is a huge image
--------------HUGE_IMAGE_BOUNDARY
--------------HUGE_IMAGE_BOUNDARY Content-Type: image/png;
Content-Type: image/png; name="huge_image.png"
name="huge_image.png" Content-Transfer-Encoding: base64
Content-Transfer-Encoding: base64 Content-Disposition: attachment;
Content-Disposition: attachment; filename="huge_image.png"
filename="huge_image.png"
iVBORw0KGgoAAAANSUhEUgAAH0AAAB9ACAIAAACJkzqjAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg==
iVBORw0KGgoAAAANSUhEUgAAH0AAAB9ACAIAAACJkzqjAAAAEElEQVR4nAEFAPr/AP////8AAAAFCeiupAAAAABJRU5ErkJggg== --------------HUGE_IMAGE_BOUNDARY--
--------------HUGE_IMAGE_BOUNDARY--

View File

@@ -1,76 +1,74 @@
X-Mozilla-Status: 0801 Content-Type: multipart/mixed; boundary="------------L1v4sF5IlAZ0HirXymXElgpK"
X-Mozilla-Status2: 10000000 Message-ID: <1e3b3bb0-f34f-71e2-6b86-bce80bef2c6f@example.net>
Content-Type: multipart/mixed; boundary="------------L1v4sF5IlAZ0HirXymXElgpK" Date: Thu, 3 Aug 2023 13:31:01 -0300
Message-ID: <1e3b3bb0-f34f-71e2-6b86-bce80bef2c6f@example.org> MIME-Version: 1.0
Date: Thu, 3 Aug 2023 13:31:01 -0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
MIME-Version: 1.0 Thunderbird/102.13.0
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Content-Language: en-US
Thunderbird/102.13.0 To: bob@example.net
Content-Language: en-US From: Alice <alice@example.org>
To: bob@example.net X-Identity-Key: id3
From: Alice <alice@example.org> Fcc: imap://alice%40example.org@in.example.org/Sent
X-Identity-Key: id3
Fcc: imap://alice%40example.org@in.example.org/Sent This is a multi-part message in MIME format.
--------------L1v4sF5IlAZ0HirXymXElgpK
This is a multi-part message in MIME format. Content-Type: text/plain; charset=UTF-8; format=flowed
--------------L1v4sF5IlAZ0HirXymXElgpK Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
--------------L1v4sF5IlAZ0HirXymXElgpK
Content-Type: application/octet-stream; name="rusty_deltachat_logo.jpeg"
--------------L1v4sF5IlAZ0HirXymXElgpK Content-Disposition: attachment; filename="rusty_deltachat_logo.jpeg"
Content-Type: application/octet-stream; name="rusty_deltachat_logo.jpeg" Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="rusty_deltachat_logo.jpeg"
Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQIAzADMAAD/2wBDAP//////////////////////////////////////
////////////////////////////////////////////////2wBDAf//////////////////
/9j/4AAQSkZJRgABAQIAzADMAAD/2wBDAP////////////////////////////////////// ////////////////////////////////////////////////////////////////////wAAR
////////////////////////////////////////////////2wBDAf////////////////// CAEAAQADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAA
////////////////////////////////////////////////////////////////////wAAR AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK
CAEAAQADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAA FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG
AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl
FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG 5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA
h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl AgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYk
5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA NOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
AgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYk hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk
NOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE 5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCSiiigAooooAKKKKACiiigAooprHAzQA6i
hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk mhgfY0McA0AAYGnVX6U/efagA3Hdnt6VL15qvS5OMZ4oAUt82R26f596lByM1BSgkcA0APZ+
5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCSiiigAooooAKKKKACiiigAooprHAzQA6i eO3X3qSq9PV8DBoAl6UVCzZ+lPQ8fSgB9FFICD0oAWiiigAooooAKKKKACiiigAooooAKKKK
mhgfY0McA0AAYGnVX6U/efagA3Hdnt6VL15qvS5OMZ4oAUt82R26f596lByM1BSgkcA0APZ+ ACiiigApCcDNLTH6UAKGB9j6UjkYx3qKigAooooAKKKKACiiigAooooAKKKKAClBI6UlFACk
eO3X3qSq9PV8DBoAl6UVCzZ+lPQ8fSgB9FFICD0oAWiiigAooooAKKKKACiiigAooooAKKKK k9akTofrUVKCR0oAnoqEuT7fSnp0P1oAfRRRQAUUUUAFFFFABRRRQAUUUUAFIwyDTd496QuM
ACiiigApCcDNLTH6UAKGB9j6UjkYx3qKigAooooAKKKKACiiigAooooAKKKKAClBI6UlFACk cUAMBI6GkJJ60UUAFFFFABRSqMmpgAOlAEYQnrxTtg96fSEgdTQAmxfT+dLtX0FJvX1o3r6/
k9akTofrUVKCR0oAnoqEuT7fSnp0P1oAfRRRQAUUUUAFFFFABRRRQAUUUUAFIwyDTd496QuM zoAXavoKTYvp/OlBB6GloAZsHvTSh7HNS0UAQEEdRSVYqNk7j8qAI6KKKACnq2ODTKKAJS47
cUAMBI6GkJJ60UUAFFFFABRSqMmpgAOlAEYQnrxTtg96fSEgdTQAmxfT+dLtX0FJvX1o3r6/ CnDkA1AOtWKACiiigAooooAKKKKACimscDOM03zPb9f/AK1ADXGD9eabSk5OTSUAFFFFABRR
zoAXavoKTYvp/OlBB6GloAZsHvTSh7HNS0UAQEEdRSVYqNk7j8qAI6KKKACnq2ODTKKAJS47 RQBMowB7806kXoPpQeh+hoAjZ+w/OmUUUAFFFFABTw/r+dMooAsdaKhDEfT0qUEHkUALRRRQ
CnDkA1AOtWKACiiigAooooAKKKKACimscDOM03zPb9f/AK1ADXGD9eabSk5OTSUAFFFFABRR BG69x+NR1O33T9KgoAKKKKACnBiBim0UASKSTyeKkpiYwfWn0AFFFFABRRTWbb2zQA4jPFV6
RQBMowB7806kXoPpQeh+hoAjZ+w/OmUUUAFFFFABTw/r+dMooAsdaKhDEfT0qUEHkUALRRRQ eX46Y/GmUAFFFFABRRRQAUUUUASIe35VJVepVbPB6/zoAay45HT+VMqxTCgPTg0ARUUpBHWk
BG69x+NR1O33T9KgoAKKKKACnBiBim0UASKSTyeKkpiYwfWn0AFFFFABRRTWbb2zQA4jPFV6 oAKKKKAClBI6UlFAEocd+KXevr+hqGigBzNn6U2iigAooooAKKKKAAEjpTgWJxk02pExk+tA
eX46Y/GmUAFFFFABRRRQAUUUUASIe35VJVepVbPB6/zoAay45HT+VMqxTCgPTg0ARUUpBHWk ElFFFABSEZGKCQOtNLjtzQBF0ooooAKKKKACiiigAooooAKKKKAJFfsfz/xqSq9OViPcUATY
oAKKKKAClBI6UlFAEocd+KXevr+hqGigBzNn6U2iigAooooAKKKKAAEjpTgWJxk02pExk+tA z1qMp6flTwQelLQBX6UVOQD1qMoR05/nQAyiiigAooooAKKKKACiiigAooooAKAcdKKKAHBm
ElFFFABSEZGKCQOtNLjtzQBF0ooooAKKKKACiiigAooooAKKKKAJFfsfz/xqSq9OViPcUATY +tTUxAMZ70+gBCARioSCDzU+cdahY5PHagBtFFFABRRRQAUUUUAFFFFABRRRQAUUdelPCHvx
z1qMp6flTwQelLQBX6UVOQD1qMoR05/nQAyiiigAooooAKKKKACiiigAooooAKAcdKKKAHBm QA0EjpUqsD9fSkCD3NOCgdqAFooooAaVB+vrURUj/Gp6KAK9FSMncfl/hUdABRRRQAUUUUAF
+tTUxAMZ70+gBCARioSCDzU+cdahY5PHagBtFFFABRRRQAUUUUAFFFFABRRRQAUUdelPCHvx FFFABRRRQA4MR0p6tnjHNRgZOKmCgUARsp69R/KmVYqA9Tj1oASiiigAooooAKKKKACiilAJ
QA0EjpUqsD9fSkCD3NOCgdqAFooooAaVB+vrURUj/Gp6KAK9FSMncfl/hUdABRRRQAUUUUAF 4FACVIE9fypwUD6+tOoAQADpS0UhIHJoAWkLAdTUZcnpwKZQBIXHYUB+eelR0UAWKKiRux/C
FFFABRRRQA4MR0p6tnjHNRgZOKmCgUARsp69R/KmVYqA9Tj1oASiiigAooooAKKKKACiilAJ paACmMueR1/nT6KAK9FSsueR1/nUVABRRRQAUUUUAFFFFACg4OalDA+31qGpFTufyoAazE8d
4FACVIE9fypwUD6+tOoAQADpS0UhIHJoAWkLAdTUZcnpwKZQBIXHYUB+eelR0UAWKKiRux/C B6U2pyAetQsMEigBKKKKACiiigAooooABzxU4GBTEHf8qkoAKKKKADpUDHJ/lT3Pb8TUdABR
paACmMueR1/nT6KAK9FSsueR1/nUVABRRRQAUUUUAFFFFACg4OalDA+31qGpFTufyoAazE8d RRQAUUUUAFTKcj3FQ05Tg/pQBNRRRQAVE4wc9j/OpaQjIIoAgooooAKKKKACiiigBRwQanqv
B6U2pyAetQsMEigBKKKKACiiigAooooABzxU4GBTEHf8qkoAKKKKADpUDHJ/lT3Pb8TUdABR T03Z9vegB7Nj61DUxUGmMuKAGUUUUAFFFFABRRSr1H1oAmAwAKWiigAooooAgY5JpKKKACii
RRQAUUUUAFTKcj3FQ05Tg/pQBNRRRQAVE4wc9j/OpaQjIIoAgooooAKKKKACiiigBRwQanqv igAooooAKKKKAJwcgGlpifdp9ABRRRQBCwwx/Om09+o+lMoAKKKKACiiigCRB1qSoAcHNTA5
T03Z9vegB7Nj61DUxUGmMuKAGUUUUAFFFFABRRSr1H1oAmAwAKWiigAooooAgY5JpKKKACii GaAFqJ2zwPxqWo9h9aAI6KUjBwaSgAooooAKcv3hTaUdR9aAJ6KKKACkPQ/Q0tFAFeiiigAo
igAooooAKKKKAJwcgGlpifdp9ABRRRQBCwwx/Om09+o+lMoAKKKKACiiigCRB1qSoAcHNTA5 oooAKKKKACiiigCVOh+v9BT6an3adQAUUUUARP1H0plOc5Y/lTaACiiigApVGSBSUdOaAJ8D
GaAFqJ2zwPxqWo9h9aAI6KUjBwaSgAooooAKcv3hTaUdR9aAJ6KKKACkPQ/Q0tFAFeiiigAo 0FLSA5GaWgAoooPTjrQBE5yfpTKfsb2ppBBwaAEooooAKKKKAJ1OQDS1Ehwcev8AOpaACiii
oooAKKKKACiiigCVOh+v9BT6an3adQAUUUUARP1H0plOc5Y/lTaACiiigApVGSBSUdOaAJ8D gCAjBP1pKe45z60ygAooooAKKKKACiinIMn2FAEoGABS0UUAFITgZpaidsnHYUAMooooAKKK
0FLSA5GaWgAoooPTjrQBE5yfpTKfsb2ppBBwaAEooooAKKKKAJ1OQDS1Ehwcev8AOpaACiii KACpti+n6moalVs8Hr/OgBwGOBS0UUAFFFFABUb9hUlRlCSTkUAR0U8oQM9aZQAUUUUAFSq2
gCAjBP1pKe45z60ygAooooAKKKKACiinIMn2FAEoGABS0UUAFITgZpaidsnHYUAMooooAKKK eD1/nUVFAFiimK2eD1/nT6AEIyMVCQQcGp6QgHrQBBRTyh7c03afQ0AJRS7T6GnBD34oAaAS
KACpti+n6moalVs8Hr/OgBwGOBS0UUAFFFFABUb9hUlRlCSTkUAR0U8oQM9aZQAUUUUAFSq2 cCpgMDFAAHSloAKKKjZ+w/P/AAoAGbsPxqOiigAooooAKKKKAFUZOKlCgf41EDg5FSBwevFA
eD1/nUVFAFiimK2eD1/nT6AEIyMVCQQcGp6QgHrQBBRTyh7c03afQ0AJRS7T6GnBD34oAaAS D6KKKACiiigAooooAQnAJqCpyMjFIEUds/WgCGinuoHI/KmUAFFFFABTw5HXn+dMooAnBB6G
cCpgMDFAAHSloAKKKjZ+w/P/AAoAGbsPxqOiigAooooAKKKKAFUZOKlCgf41EDg5FSBwevFA lqvTgxHegCaiot59BS+Z7frQBJRUfme1JvPsKAJaaXA96iJJ6mkoAcWJ+npTaKKACiiigAoo
D6KKKACiiigAooooAQnAJqCpyMjFIEUds/WgCGinuoHI/KmUAFFFFABTw5HXn+dMooAnBB6G ooAKKKKACpVXHJ61FUocYGetAD6KQEHoaWgAooooAKKKKACiiigBj9B9aaEJ68VLRQBAQR1p
lqvTgxHegCaiot59BS+Z7frQBJRUfme1JvPsKAJaaXA96iJJ6mkoAcWJ+npTaKKACiiigAoo KlfoPrTApNADaKUgjrSUAFFFFABRRRQAUUUUAFFFFABRRSkEdRQAlFKBkgVKUGOOKAIaKXBz
ooAKKKKACpVXHJ61FUocYGetAD6KQEHoaWgAooooAKKKKACiiigBj9B9aaEJ68VLRQBAQR1p ipto9BQBBRQRg4p+w4z39KAGUUUUASouOe5/lT6QHIzS0AFFFFABRRRQAUUUUAFFFFACEA9a
KlfoPrTApNADaKUgjrSUAFFFFABRRRQAUUUUAFFFFABRRSkEdRQAlFKBkgVKUGOOKAIaKXBz WiigBr/d+lRqufpU1FAEbJgZFR1YqNU557dKAG7W9KbVioip3YHegBoBPQUlTgYGKay55HX+
ipto9BQBBRQRg4p+w4z39KAGUUUUASouOe5/lT6QHIzS0AFFFFABRRRQAUUUUAFFFFACEA9a dADFXdSshHPWnqMD3p1AESdfwqWkAA6d6WgBgQA5H5U+iigBMDOe9LRRQAm0ZzS0UUARFTu9
WiigBr/d+lRqufpU1FAEbJgZFR1YqNU557dKAG7W9KbVioip3YHegBoBPQUlTgYGKay55HX+ jzmpNoxjFLRQAgGOlLRRQB//2Q==
dADFXdSshHPWnqMD3p1AESdfwqWkAA6d6WgBgQA5H5U+iigBMDOe9LRRQAm0ZzS0UUARFTu9
jzmpNoxjFLRQAgGOlLRRQB//2Q== --------------L1v4sF5IlAZ0HirXymXElgpK--
--------------L1v4sF5IlAZ0HirXymXElgpK--

View File

@@ -1,21 +1,21 @@
Subject: webxdc object attached Subject: webxdc object attached
Message-ID: 67890@example.org Message-ID: 67890@example.net
Date: Fri, 03 Dec 2021 10:00:27 +0000 Date: Fri, 03 Dec 2021 10:00:27 +0000
To: alice@example.org To: alice@example.org
From: bob@example.org From: bob@example.net
Chat-Version: 1.0 Chat-Version: 1.0
Content-Type: multipart/mixed; boundary="==BREAK==" Content-Type: multipart/mixed; boundary="==BREAK=="
--==BREAK== --==BREAK==
Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8
webxdc with bad extension and bad content. webxdc with bad extension and bad content.
--==BREAK== --==BREAK==
Content-Type: application/webxdc+zip Content-Type: application/webxdc+zip
Content-Disposition: attachment; filename=index.html Content-Disposition: attachment; filename=index.html
<html>hey!<html> <html>hey!<html>
--==BREAK==-- --==BREAK==--

View File

@@ -1,29 +1,29 @@
Subject: webxdc object attached Subject: webxdc object attached
Message-ID: 12345@example.org Message-ID: 12345@example.net
Date: Fri, 03 Dec 2021 10:00:27 +0000 Date: Fri, 03 Dec 2021 10:00:27 +0000
To: alice@example.org To: alice@example.org
From: bob@example.org From: bob@example.net
Chat-Version: 1.0 Chat-Version: 1.0
Content-Type: multipart/mixed; boundary="==BREAK==" Content-Type: multipart/mixed; boundary="==BREAK=="
--==BREAK== --==BREAK==
Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8
webxdc with good extension; webxdc with good extension;
the mimetype is ignored then, the mimetype is ignored then,
content is checked. content is checked.
--==BREAK== --==BREAK==
Content-Type: text/html Content-Type: text/html
Content-Disposition: attachment; filename=minimal.xdc Content-Disposition: attachment; filename=minimal.xdc
Content-Transfer-Encoding: base64 Content-Transfer-Encoding: base64
UEsDBBQAAgAIAFJqnVOItjSofAAAALwAAAAKABwAaW5kZXguaHRtbFVUCQADG1LMYV1SzGF1eAsAAQ UEsDBBQAAgAIAFJqnVOItjSofAAAALwAAAAKABwAaW5kZXguaHRtbFVUCQADG1LMYV1SzGF1eAsAAQ
T1AQAABBQAAACzUXTxdw6JDHBVyCjJzbHjsoFQCgo2SfkplSCGgkJJanGJIphlU5xclFlQAhFWUEjJ T1AQAABBQAAACzUXTxdw6JDHBVyCjJzbHjsoFQCgo2SfkplSCGgkJJanGJIphlU5xclFlQAhFWUEjJ
Ty7NTc0r0SsvyixJ1VAqyU8EqlTStIYo1kdWbZOXj6o5uiQjsxhodkWJQnFGfmlOikJefolCUiqIV5 Ty7NTc0r0SsvyixJ1VAqyU8EqlTStIYo1kdWbZOXj6o5uiQjsxhodkWJQnFGfmlOikJefolCUiqIV5
4XCzUCWZeNPsRNNvoQRwIAUEsBAh4DFAACAAgAUmqdU4i2NKh8AAAAvAAAAAoAGAAAAAAAAQAAAKSB 4XCzUCWZeNPsRNNvoQRwIAUEsBAh4DFAACAAgAUmqdU4i2NKh8AAAAvAAAAAoAGAAAAAAAAQAAAKSB
AAAAAGluZGV4Lmh0bWxVVAUAAxtSzGF1eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBQAAAAwAAAAA AAAAAGluZGV4Lmh0bWxVVAUAAxtSzGF1eAsAAQT1AQAABBQAAABQSwUGAAAAAAEAAQBQAAAAwAAAAA
AA AA
--==BREAK==-- --==BREAK==--