Compare commits

..

1 Commits

Author SHA1 Message Date
link2xt
c0deb83206 WIP: feat: add option to process unencrypted messages 2026-05-08 10:23:39 +02:00
33 changed files with 426 additions and 245 deletions

9
Cargo.lock generated
View File

@@ -3941,14 +3941,15 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "openssl"
version = "0.10.79"
version = "0.10.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542"
checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
@@ -3981,9 +3982,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
version = "0.9.115"
version = "0.9.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781"
checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6"
dependencies = [
"cc",
"libc",

View File

@@ -4003,6 +4003,8 @@ int dc_msg_get_viewtype (const dc_msg_t* msg);
* Marked as read on IMAP and MDN may be sent. Use dc_markseen_msgs() to mark messages as being seen.
*
* Outgoing message states:
* - @ref DC_STATE_OUT_PREPARING - For files which need time to be prepared before they can be sent,
* the message enters this state before @ref DC_STATE_OUT_PENDING. Deprecated.
* - @ref DC_STATE_OUT_DRAFT - Message saved as draft using dc_set_draft()
* - @ref DC_STATE_OUT_PENDING - The user has pressed the "send" button but the
* message is not yet sent and is pending in some way. Maybe we're offline (no checkmark).
@@ -5587,6 +5589,13 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*/
#define DC_STATE_IN_SEEN 16
/**
* Outgoing message being prepared. See dc_msg_get_state() for details.
*
* @deprecated 2024-12-07
*/
#define DC_STATE_OUT_PREPARING 18
/**
* Outgoing message drafted. See dc_msg_get_state() for details.
*/

View File

@@ -230,6 +230,7 @@ pub enum LotState {
MsgInFresh = 10,
MsgInNoticed = 13,
MsgInSeen = 16,
MsgOutPreparing = 18,
MsgOutDraft = 19,
MsgOutPending = 20,
MsgOutFailed = 24,
@@ -245,6 +246,7 @@ impl From<MessageState> for LotState {
InFresh => LotState::MsgInFresh,
InNoticed => LotState::MsgInNoticed,
InSeen => LotState::MsgInSeen,
OutPreparing => LotState::MsgOutPreparing,
OutDraft => LotState::MsgOutDraft,
OutPending => LotState::MsgOutPending,
OutFailed => LotState::MsgOutFailed,

View File

@@ -190,6 +190,7 @@ class MessageState(IntEnum):
IN_FRESH = 10
IN_NOTICED = 13
IN_SEEN = 16
OUT_PREPARING = 18
OUT_DRAFT = 19
OUT_PENDING = 20
OUT_FAILED = 24

View File

@@ -271,6 +271,15 @@ class Chat:
sent out. This is the same object as was passed in, which
has been modified with the new state of the core.
"""
if msg.is_out_preparing():
assert msg.id != 0
# get a fresh copy of dc_msg, the core needs it
maybe_msg = Message.from_db(self.account, msg.id)
if maybe_msg is not None:
msg = maybe_msg
else:
raise ValueError("message does not exist")
sent_id = lib.dc_send_msg(self.account._dc_context, self.id, msg._dc_msg)
if sent_id == 0:
raise ValueError("message could not be sent")
@@ -324,6 +333,26 @@ class Chat:
raise ValueError("message could not be sent")
return Message.from_db(self.account, sent_id)
def send_prepared(self, message):
"""send a previously prepared message.
:param message: a :class:`Message` instance previously returned by
:meth:`prepare_file`.
:raises ValueError: if message can not be sent.
:returns: a :class:`deltachat.message.Message` instance as sent out.
"""
assert message.id != 0 and message.is_out_preparing()
# get a fresh copy of dc_msg, the core needs it
msg = Message.from_db(self.account, message.id)
# pass 0 as chat-id because core-docs say it's ok when out-preparing
sent_id = lib.dc_send_msg(self.account._dc_context, 0, msg._dc_msg)
if sent_id == 0:
raise ValueError("message could not be sent")
assert sent_id == msg.id
# modify message in place to avoid bad state for the caller
msg._dc_msg = Message.from_db(self.account, sent_id)._dc_msg
def set_draft(self, message):
"""set message as draft.

View File

@@ -351,12 +351,17 @@ class Message:
def is_outgoing(self):
"""Return True if Message is outgoing."""
return lib.dc_msg_get_state(self._dc_msg) in (
const.DC_STATE_OUT_PREPARING,
const.DC_STATE_OUT_PENDING,
const.DC_STATE_OUT_FAILED,
const.DC_STATE_OUT_MDN_RCVD,
const.DC_STATE_OUT_DELIVERED,
)
def is_out_preparing(self):
"""Return True if Message is outgoing, but its file is being prepared."""
return self._msgstate == const.DC_STATE_OUT_PREPARING
def is_out_pending(self):
"""Return True if Message is outgoing, but is pending (no single checkmark)."""
return self._msgstate == const.DC_STATE_OUT_PENDING

View File

@@ -2613,7 +2613,7 @@ pub async fn send_msg(context: &Context, chat_id: ChatId, msg: &mut Message) ->
"chat_id cannot be a special chat: {chat_id}"
);
if msg.state != MessageState::Undefined {
if msg.state != MessageState::Undefined && msg.state != MessageState::OutPreparing {
msg.param.remove(Param::GuaranteeE2ee);
msg.param.remove(Param::ForcePlaintext);
// create_send_msg_jobs() will update `param` in the db.
@@ -2721,7 +2721,10 @@ async fn prepare_send_msg(
None
};
if msg.state == MessageState::Undefined
if matches!(
msg.state,
MessageState::Undefined | MessageState::OutPreparing
)
// Legacy SecureJoin "v*-request" messages are unencrypted.
&& msg.param.get_cmd() != SystemMessage::SecurejoinMessage
&& chat.is_encrypted(context).await?
@@ -2934,8 +2937,8 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -
UPDATE msgs SET
timestamp=(
SELECT MAX(timestamp) FROM msgs INDEXED BY msgs_index7 WHERE
-- From `InFresh` to `OutDelivered` inclusive, except `OutDraft`.
state IN(10,13,16,18,20,24,26) AND
-- From `InFresh` to `OutMdnRcvd` inclusive except `OutDraft`.
state IN(10,13,16,18,20,24,26,28) AND
hidden IN(0,1) AND
chat_id=? AND
id<=?
@@ -2970,6 +2973,15 @@ WHERE id=?
)?;
for recipients_chunk in recipients.chunks(chunk_size) {
let recipients_chunk = recipients_chunk.join(" ");
if let Some(pre_msg) = &rendered_pre_msg {
let row_id = stmt.execute((
&pre_msg.rfc724_mid,
&recipients_chunk,
&pre_msg.message,
msg.id,
))?;
row_ids.push(row_id.try_into()?);
}
let row_id = stmt.execute((
&rendered_msg.rfc724_mid,
&recipients_chunk,
@@ -2977,16 +2989,6 @@ WHERE id=?
msg.id,
))?;
row_ids.push(row_id.try_into()?);
let Some(pre_msg) = &rendered_pre_msg else {
continue;
};
let row_id = stmt.execute((
&pre_msg.rfc724_mid,
&recipients_chunk,
&pre_msg.message,
msg.id,
))?;
row_ids.push(row_id.try_into()?);
}
Ok(row_ids)
};
@@ -4537,7 +4539,6 @@ pub async fn forward_msgs_2ctx(
msg.state = MessageState::OutPending;
msg.rfc724_mid = create_outgoing_rfc724_mid();
msg.pre_rfc724_mid.clear();
msg.timestamp_sort = curr_timestamp;
chat.prepare_msg_raw(ctx_dst, &mut msg, None).await?;

View File

@@ -1049,6 +1049,7 @@ async fn chatlist_len(ctx: &Context, listflags: usize) -> usize {
async fn test_archive() {
// create two chats
let t = TestContext::new_alice().await;
let mut msg = Message::new_text("foo".to_string());
let msg_id = add_device_msg(&t, None, Some(&mut msg)).await.unwrap();
let chat_id1 = message::Message::load_from_db(&t, msg_id)
@@ -1381,6 +1382,9 @@ async fn test_markfresh_chat() -> Result<()> {
async fn test_archive_fresh_msgs() -> Result<()> {
let t = TestContext::new_alice().await;
// FIXME: use encrypted messages
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
async fn msg_from(t: &TestContext, name: &str, num: u32) -> Result<()> {
receive_imf(
t,
@@ -1873,45 +1877,38 @@ async fn test_lookup_self_by_contact_id() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_marknoticed_chat() -> Result<()> {
let t = TestContext::new_alice().await;
let chat = t.create_chat_with_contact("bob", "bob@example.org").await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let chat = alice.create_chat(bob).await;
receive_imf(
&t,
b"From: bob@example.org\n\
To: alice@example.org\n\
Message-ID: <1@example.org>\n\
Chat-Version: 1.0\n\
Date: Fri, 23 Apr 2021 10:00:57 +0000\n\
\n\
hello\n",
false,
)
.await?;
let bob_chat_id = bob.create_chat_id(alice).await;
let sent = bob.send_text(bob_chat_id, "hello").await;
alice.recv_msg(&sent).await;
let chats = Chatlist::try_load(&t, 0, None, None).await?;
let chats = Chatlist::try_load(alice, 0, None, None).await?;
assert_eq!(chats.len(), 1);
assert_eq!(chats.get_chat_id(0)?, chat.id);
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 1);
assert_eq!(t.get_fresh_msgs().await?.len(), 1);
assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 1);
assert_eq!(alice.get_fresh_msgs().await?.len(), 1);
let msgs = get_chat_msgs(&t, chat.id).await?;
assert_eq!(msgs.len(), 1);
let msg_id = match msgs.first().unwrap() {
let msgs = get_chat_msgs(alice, chat.id).await?;
assert_eq!(msgs.len(), 2);
let msg_id = match msgs.last().unwrap() {
ChatItem::Message { msg_id } => *msg_id,
_ => MsgId::new_unset(),
};
let msg = message::Message::load_from_db(&t, msg_id).await?;
let msg = message::Message::load_from_db(alice, msg_id).await?;
assert_eq!(msg.state, MessageState::InFresh);
marknoticed_chat(&t, chat.id).await?;
marknoticed_chat(alice, chat.id).await?;
let chats = Chatlist::try_load(&t, 0, None, None).await?;
let chats = Chatlist::try_load(alice, 0, None, None).await?;
assert_eq!(chats.len(), 1);
let msg = message::Message::load_from_db(&t, msg_id).await?;
let msg = message::Message::load_from_db(alice, msg_id).await?;
assert_eq!(msg.state, MessageState::InNoticed);
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 0);
assert_eq!(t.get_fresh_msgs().await?.len(), 0);
assert_eq!(chat.id.get_fresh_msg_cnt(alice).await?, 0);
assert_eq!(alice.get_fresh_msgs().await?.len(), 0);
Ok(())
}
@@ -1919,6 +1916,7 @@ async fn test_marknoticed_chat() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_request_fresh_messages() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
let chats = Chatlist::try_load(&t, 0, None, None).await?;
assert_eq!(chats.len(), 0);
@@ -1970,40 +1968,43 @@ async fn test_contact_request_fresh_messages() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_request_archive() -> Result<()> {
let t = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
receive_imf(
&t,
b"From: bob@example.org\n\
To: alice@example.org\n\
Message-ID: <2@example.org>\n\
Chat-Version: 1.0\n\
Date: Sun, 22 Mar 2021 19:37:57 +0000\n\
\n\
hello\n",
false,
)
.await?;
let bob_chat_id = bob.create_chat_id(alice).await;
let bob_sent_text = bob.send_text(bob_chat_id, "hello").await;
alice.recv_msg(&bob_sent_text).await;
let chats = Chatlist::try_load(&t, 0, None, None).await?;
let chats = Chatlist::try_load(alice, 0, None, None).await?;
assert_eq!(chats.len(), 1);
let chat_id = chats.get_chat_id(0)?;
assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request());
assert_eq!(get_archived_cnt(&t).await?, 0);
assert!(
Chat::load_from_db(alice, chat_id)
.await?
.is_contact_request()
);
assert_eq!(get_archived_cnt(alice).await?, 0);
// archive request without accepting or blocking
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
chat_id
.set_visibility(alice, ChatVisibility::Archived)
.await?;
let chats = Chatlist::try_load(&t, 0, None, None).await?;
let chats = Chatlist::try_load(alice, 0, None, None).await?;
assert_eq!(chats.len(), 1);
let chat_id = chats.get_chat_id(0)?;
assert!(chat_id.is_archived_link());
assert_eq!(get_archived_cnt(&t).await?, 1);
assert_eq!(get_archived_cnt(alice).await?, 1);
let chats = Chatlist::try_load(&t, DC_GCL_ARCHIVED_ONLY, None, None).await?;
let chats = Chatlist::try_load(alice, DC_GCL_ARCHIVED_ONLY, None, None).await?;
assert_eq!(chats.len(), 1);
let chat_id = chats.get_chat_id(0)?;
assert!(Chat::load_from_db(&t, chat_id).await?.is_contact_request());
assert!(
Chat::load_from_db(alice, chat_id)
.await?
.is_contact_request()
);
Ok(())
}
@@ -2011,6 +2012,9 @@ async fn test_contact_request_archive() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_classic_email_chat() -> Result<()> {
let alice = TestContext::new_alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
// Alice receives a classic (non-chat) message from Bob.
receive_imf(

View File

@@ -473,6 +473,7 @@ mod tests {
add_contact_to_chat, create_broadcast, create_group, get_chat_contacts,
remove_contact_from_chat, send_text_msg, set_chat_name,
};
use crate::config::Config;
use crate::receive_imf::receive_imf;
use crate::securejoin::get_securejoin_qr;
use crate::stock_str::StockMessage;
@@ -665,6 +666,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_single_chat() -> anyhow::Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
// receive a one-to-one-message
receive_imf(
@@ -725,6 +727,7 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
// receive a one-to-one-message without authname set
receive_imf(

View File

@@ -486,6 +486,11 @@ pub enum Config {
/// Experimental option denoting that the current profile is shared between multiple team members.
/// For now, the only effect of this option is that seen flags are not synchronized.
TeamProfile,
/// Process unencrypted messages.
///
/// Unencrypted messages are fetched and processed only if this setting is explicitly enabled.
ProcessUnencrypted,
}
impl Config {

View File

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

View File

@@ -370,6 +370,7 @@ mod tests {
use super::*;
use crate::receive_imf::receive_imf;
use crate::test_utils::TestContext;
use crate::config::Config;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mixed_up_mime() -> Result<()> {
@@ -402,6 +403,7 @@ mod tests {
assert!(get_attachment_mime(&mail).is_some());
let bob = TestContext::new_bob().await;
bob.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(&bob, attachment_mime, false).await?;
let msg = bob.get_last_msg().await;
// Subject should be prepended because the attachment doesn't have "Chat-Version".
@@ -416,6 +418,7 @@ mod tests {
// Desktop via MS Exchange (actually made with TB though).
let mixed_up_mime = include_bytes!("../test-data/message/mixed-up-long.eml");
let bob = TestContext::new_bob().await;
bob.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(&bob, mixed_up_mime, false).await?;
let msg = bob.get_last_msg().await;
assert!(!msg.get_text().is_empty());

View File

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

View File

@@ -287,6 +287,7 @@ impl MsgId {
mod tests {
use super::*;
use crate::chat::{self, Chat, forward_msgs, save_msgs};
use crate::config::Config;
use crate::constants;
use crate::contact::ContactId;
use crate::message::{MessengerMessage, Viewtype};
@@ -450,6 +451,9 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
// alice receives a non-delta html-message
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let chat = alice
.create_chat_with_contact("", "sender@testrun.org")
.await;
@@ -483,6 +487,8 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
// bob: check that bob also got the html-part of the forwarded message
let bob = &tcm.bob().await;
bob.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let chat_bob = bob.create_chat_with_contact("", "alice@example.org").await;
async fn check_receiver(ctx: &TestContext, chat: &Chat, sender: &TestContext) {
let msg = ctx.recv_msg(&sender.pop_sent_msg().await).await;
@@ -520,8 +526,12 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_save_msg() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
// Alice receives a non-delta html-message
let alice = TestContext::new_alice().await;
let chat = alice
.create_chat_with_contact("", "sender@testrun.org")
.await;
@@ -555,6 +565,10 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
let mut tcm = TestContextManager::new();
// Alice receives a non-delta html-message
let alice = &tcm.alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
let chat = alice
.create_chat_with_contact("", "sender@testrun.org")
.await;
@@ -618,18 +632,22 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cp1252_html() -> Result<()> {
let t = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
receive_imf(
&t,
alice,
include_bytes!("../test-data/message/cp1252-html.eml"),
false,
)
.await?;
let msg = t.get_last_msg().await;
let msg = alice.get_last_msg().await;
assert_eq!(msg.viewtype, Viewtype::Text);
assert!(msg.text.contains("foo bar ä ö ü ß"));
assert!(msg.has_html());
let html = msg.get_id().get_html(&t).await?.unwrap();
let html = msg.get_id().get_html(alice).await?.unwrap();
println!("{html}");
assert!(html.contains("foo bar ä ö ü ß"));
Ok(())

View File

@@ -1996,12 +1996,21 @@ pub(crate) async fn prefetch_should_download(
// prevent_rename=true as this might be a mailing list message and in this case it would be bad if we rename the contact.
// (prevent_rename is the last argument of from_field_to_contact_id())
let is_encrypted = if let Some(content_type) = headers.get_header_value(HeaderDef::ContentType)
{
mailparse::parse_content_type(&content_type).mimetype == "multipart/encrypted"
} else {
false
};
if flags.any(|f| f == Flag::Draft) {
info!(context, "Ignoring draft message");
return Ok(false);
}
let should_download = !blocked_contact || maybe_ndn;
let should_download = maybe_ndn
|| (!blocked_contact
&& (is_encrypted || context.get_config_bool(Config::ProcessUnencrypted).await?));
Ok(should_download)
}

View File

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

View File

@@ -939,7 +939,9 @@ mod tests {
/// Tests that location.kml is hidden.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn receive_location_kml() -> Result<()> {
let alice = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&alice,

View File

@@ -1381,8 +1381,13 @@ pub enum MessageState {
/// IMAP and MDN may be sent.
InSeen = 16,
// Deprecated 2024-12-07. Removed 2026-04.
// OutPreparing = 18,
/// For files which need time to be prepared before they can be
/// sent, the message enters this state before
/// OutPending.
///
/// Deprecated 2024-12-07.
OutPreparing = 18,
/// Message saved as draft.
OutDraft = 19,
@@ -1415,6 +1420,7 @@ impl std::fmt::Display for MessageState {
Self::InFresh => "Fresh",
Self::InNoticed => "Noticed",
Self::InSeen => "Seen",
Self::OutPreparing => "Preparing",
Self::OutDraft => "Draft",
Self::OutPending => "Pending",
Self::OutFailed => "Failed",
@@ -1431,7 +1437,7 @@ impl MessageState {
use MessageState::*;
matches!(
self,
OutPending | OutDelivered | OutMdnRcvd // OutMdnRcvd can still fail because it could be a group message and only some recipients failed.
OutPreparing | OutPending | OutDelivered | OutMdnRcvd // OutMdnRcvd can still fail because it could be a group message and only some recipients failed.
)
}
@@ -1440,7 +1446,7 @@ impl MessageState {
use MessageState::*;
matches!(
self,
OutDraft | OutPending | OutFailed | OutDelivered | OutMdnRcvd
OutPreparing | OutDraft | OutPending | OutFailed | OutDelivered | OutMdnRcvd
)
}

View File

@@ -139,23 +139,14 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_chat_id() {
// Alice receives a message that pops up as a contact request
let alice = TestContext::new_alice().await;
receive_imf(
&alice,
b"From: Bob <bob@example.com>\n\
To: alice@example.org\n\
Chat-Version: 1.0\n\
Message-ID: <123@example.com>\n\
Date: Fri, 29 Jan 2021 21:37:55 +0000\n\
\n\
hello\n",
false,
)
.await
.unwrap();
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let chat_id = bob.create_chat_id(alice).await;
let sent = bob.send_text(chat_id, "hello").await;
let msg = bob.recv_msg(&sent).await;
// check chat-id of this message
let msg = alice.get_last_msg().await;
assert!(!msg.get_chat_id().is_special());
assert_eq!(msg.get_text(), "hello".to_string());
}
@@ -465,7 +456,9 @@ async fn test_get_state() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_is_bot() -> Result<()> {
let alice = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice.set_config(Config::ProcessUnencrypted, Some("1")).await?;
// Alice receives an auto-generated non-chat message.
//
@@ -473,7 +466,7 @@ async fn test_is_bot() -> Result<()> {
// in which case the message should be marked as bot-generated,
// but the contact should not.
receive_imf(
&alice,
alice,
b"From: Claire <claire@example.com>\n\
To: alice@example.org\n\
Message-ID: <789@example.com>\n\
@@ -492,7 +485,7 @@ async fn test_is_bot() -> Result<()> {
// Alice receives a message from Bob the bot.
receive_imf(
&alice,
alice,
b"From: Bob <bob@example.com>\n\
To: alice@example.org\n\
Chat-Version: 1.0\n\
@@ -512,7 +505,7 @@ async fn test_is_bot() -> Result<()> {
// Alice receives a message from Bob who is not the bot anymore.
receive_imf(
&alice,
alice,
b"From: Bob <bob@example.com>\n\
To: alice@example.org\n\
Chat-Version: 1.0\n\
@@ -526,7 +519,7 @@ async fn test_is_bot() -> Result<()> {
let msg = alice.get_last_msg().await;
assert_eq!(msg.get_text(), "hello again".to_string());
assert!(!msg.is_bot());
let contact = Contact::get_by_id(&alice, msg.from_id).await?;
let contact = Contact::get_by_id(alice, msg.from_id).await?;
assert!(!contact.is_bot());
Ok(())

View File

@@ -1503,31 +1503,23 @@ Some reply
// Test that WantsMdn parameter is not set on outgoing messages.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_wants_mdn() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let alice2 = &tcm.alice().await;
let bob = &tcm.bob().await;
let raw = br"Date: Thu, 28 Jan 2021 00:26:57 +0000
Chat-Version: 1.0\n\
Message-ID: <foobarbaz@example.org>
To: Bob <bob@example.org>
From: Alice <alice@example.org>
Subject: subject
Chat-Disposition-Notification-To: alice@example.org
Message.
";
let chat_id = alice.create_chat(bob).await.id;
let sent = alice.send_text(chat_id, "Message.").await;
// Bob receives message.
receive_imf(&bob, raw, false).await?;
let msg = bob.get_last_msg().await;
let bob_msg = bob.recv_msg(&sent).await;
// Message is incoming.
assert!(msg.param.get_bool(Param::WantsMdn).unwrap());
assert!(bob_msg.param.get_bool(Param::WantsMdn).unwrap());
// Alice receives copy-to-self.
receive_imf(&alice, raw, false).await?;
let msg = alice.get_last_msg().await;
let alice2_msg = alice2.recv_msg(&sent).await;
// Message is outgoing, don't send read receipt to self.
assert!(msg.param.get_bool(Param::WantsMdn).is_none());
assert!(alice2_msg.param.get_bool(Param::WantsMdn).is_none());
Ok(())
}
@@ -1604,7 +1596,9 @@ async fn test_ignore_read_receipt_to_self() -> Result<()> {
/// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ms_exchange_mdn() -> Result<()> {
let t = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let t = tcm.alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
let original =
include_bytes!("../../test-data/message/ms_exchange_report_original_message.eml");
@@ -2048,6 +2042,8 @@ async fn test_multiple_autocrypt_hdrs() -> Result<()> {
async fn test_receive_signed_only() -> Result<()> {
let mut tcm = TestContextManager::new();
let bob = &tcm.bob().await;
bob.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let imf_raw = include_bytes!("../../test-data/message/unencrypted_signed_simple.eml");
let msg = receive_imf(bob, imf_raw, false).await?.unwrap();

View File

@@ -66,6 +66,12 @@ impl Context {
/// Updates `quota.recent`, sets `quota.modified` to the current time
/// and emits an event to let the UIs update connectivity view.
///
/// Moreover, once each time quota gets larger than `QUOTA_WARN_THRESHOLD_PERCENTAGE`,
/// a device message is added.
/// As the message is added only once, the user is not spammed
/// in case for some providers the quota is always at ~100%
/// and new space is allocated as needed.
pub(crate) async fn update_recent_quota(
&self,
session: &mut ImapSession,

View File

@@ -505,6 +505,11 @@ pub(crate) async fn receive_imf_inner(
Ok(mime_parser) => mime_parser,
};
if !(mime_parser.was_encrypted() || mime_parser.get_header(HeaderDef::SecureJoin).is_some()) && !context.get_config_bool(Config::ProcessUnencrypted).await? {
warn!(context, "Fetched unencrypted message, ignoring");
return trash().await;
}
let rfc724_mid_orig = &mime_parser
.get_rfc724_mid()
.unwrap_or(rfc724_mid.to_string());
@@ -525,10 +530,18 @@ pub(crate) async fn receive_imf_inner(
"Receiving message {rfc724_mid_orig:?}, seen={seen}...",
);
let msg_id = message::rfc724_mid_exists(context, rfc724_mid_orig).await?;
if let Some(msg_id) = msg_id
&& !mime_parser.incoming
{
// These checks must be done before processing of SecureJoin and other special messages.
if mime_parser.pre_message == mimeparser::PreMessageMode::Post {
// Post-Message just replaces the attachment and modifies Params, not the whole message.
// This is done in the `handle_post_message` method.
} else if let Some(msg_id) = message::rfc724_mid_exists(context, rfc724_mid_orig).await? {
info!(
context,
"Message {rfc724_mid} is already in some chat or deleted."
);
if mime_parser.incoming {
return Ok(None);
}
// For the case if we missed a successful SMTP response. Be optimistic that the message is
// delivered also.
let self_addr = context.get_primary_self_addr().await?;
@@ -543,16 +556,6 @@ pub(crate) async fn receive_imf_inner(
if !msg_has_pending_smtp_job(context, msg_id).await? {
msg_id.set_delivered(context).await?;
}
}
// These checks must be done before processing of SecureJoin and other special messages.
if mime_parser.pre_message == mimeparser::PreMessageMode::Post {
// Post-Message just replaces the attachment and modifies Params, not the whole message.
// This is done in the `handle_post_message` method.
} else if msg_id.is_some() {
info!(
context,
"Message {rfc724_mid} is already in some chat or deleted."
);
return Ok(None);
}
@@ -808,6 +811,8 @@ UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1
if transport_changed {
info!(context, "Primary transport changed to {from_addr:?}.");
context.sql.uncache_raw_config("configured_addr").await;
// Regenerate User ID in V4 keys.
context.self_public_key.lock().await.take();
context.emit_event(EventType::TransportsModified);
@@ -2506,10 +2511,7 @@ WHERE id=?
part.typ,
part.bytes as isize,
part.error.as_deref().unwrap_or_default(),
match mime_parser.incoming {
true => state,
false => MessageState::Undefined,
},
state,
DownloadState::Done as u32,
original_msg.id,
),

View File

@@ -100,6 +100,9 @@ async fn test_adhoc_group_is_shown() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_accepted_contact_accepted() {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
// accept Bob by accepting a delta-message from Bob
receive_imf(&t, MSGRMSG, false).await.unwrap();
@@ -154,6 +157,9 @@ async fn test_adhoc_group_show_all() {
async fn test_adhoc_groups_merge() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await?;
receive_imf(
alice,
b"From: bob@example.net\n\
@@ -354,6 +360,9 @@ async fn test_no_message_id_header() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_escaped_from() {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
let contact_id = Contact::create(&t, "foobar", "foobar@example.com")
.await
.unwrap();
@@ -387,6 +396,9 @@ async fn test_escaped_from() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_escaped_recipients() {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
Contact::create(&t, "foobar", "foobar@example.com")
.await
.unwrap();
@@ -434,6 +446,9 @@ async fn test_escaped_recipients() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cc_to_contact() {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
Contact::create(&t, "foobar", "foobar@example.com")
.await
.unwrap();
@@ -590,6 +605,9 @@ async fn test_parse_ndn(
) -> (TestContext, MsgId) {
let t = TestContext::new().await;
t.configure_addr(self_addr).await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(
&t,
@@ -674,6 +692,7 @@ async fn test_resend_after_ndn() -> Result<()> {
async fn test_parse_ndn_group_msg() -> Result<()> {
let t = TestContext::new().await;
t.configure_addr("alice@gmail.com").await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
receive_imf(
&t,
@@ -715,6 +734,7 @@ async fn test_parse_ndn_group_msg() -> Result<()> {
async fn test_concat_multiple_ndns() -> Result<()> {
let t = TestContext::new().await;
t.configure_addr("alice@posteo.org").await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let mid = "1234@mail.gmail.com";
receive_imf(
&t,
@@ -772,6 +792,9 @@ async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_only_mail() {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
let msg = load_imf_email(&t, include_bytes!("../../test-data/message/wrong-html.eml")).await;
assert_eq!(
msg.text,
@@ -807,6 +830,7 @@ static GH_MAILINGLIST2: &str = "Received: (Postfix, from userid 1000); Mon, 4 De
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_github_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
receive_imf(&t.ctx, GH_MAILINGLIST, false).await?;
@@ -880,6 +904,8 @@ static DC_MAILINGLIST2: &[u8] = b"Received: (Postfix, from userid 1000); Mon, 4
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_classic_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
let chat_id = chats.get_chat_id(0).unwrap();
@@ -921,6 +947,8 @@ Hello mailinglist!\r\n"
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_other_device_writes_to_mailinglist() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
let first_msg = t.get_last_msg().await;
let first_chat = Chat::load_from_db(&t, first_msg.chat_id).await?;
@@ -971,6 +999,9 @@ async fn test_other_device_writes_to_mailinglist() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_block_mailing_list() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
t.evtracker.wait_next_incoming_message().await;
@@ -1005,6 +1036,9 @@ async fn test_block_mailing_list() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_block_then_unblock() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(&t, DC_MAILINGLIST, false).await.unwrap();
let blocked = Contact::get_all_blocked(&t).await.unwrap();
@@ -1035,6 +1069,9 @@ async fn test_mailing_list_decide_block_then_unblock() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_not_now() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
@@ -1062,6 +1099,9 @@ async fn test_mailing_list_decide_not_now() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_accept() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(&t.ctx, DC_MAILINGLIST, false).await.unwrap();
@@ -1084,6 +1124,8 @@ async fn test_mailing_list_decide_accept() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
b"From: Foo Bar <foo@bar.org>\n\
@@ -1108,6 +1150,7 @@ async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_majordomo_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
// test mailing lists not having a `ListId:`-header
receive_imf(
@@ -1160,6 +1203,7 @@ async fn test_majordomo_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailchimp_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
@@ -1193,6 +1237,7 @@ async fn test_mailchimp_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dhl_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
@@ -1218,6 +1263,7 @@ async fn test_dhl_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dpd_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
@@ -1243,6 +1289,7 @@ async fn test_dpd_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_xt_local_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
@@ -1276,6 +1323,7 @@ async fn test_xt_local_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_xing_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
@@ -1298,6 +1346,7 @@ async fn test_xing_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ttline_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
receive_imf(
&t,
@@ -1318,6 +1367,9 @@ async fn test_ttline_mailing_list() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_mimepart_footer() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
// the mailing list message contains two top-level texts.
// the second text is a footer that is added by some mailing list software
@@ -1345,6 +1397,9 @@ async fn test_mailing_list_with_mimepart_footer() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_mimepart_footer_signed() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(
&t,
@@ -1369,6 +1424,9 @@ async fn test_mailing_list_with_mimepart_footer_signed() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_apply_mailinglist_changes_assigned_by_reply() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(&t, GH_MAILINGLIST, false).await.unwrap();
@@ -1407,6 +1465,9 @@ async fn test_apply_mailinglist_changes_assigned_by_reply() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_chat_message() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(
&t,
@@ -1429,6 +1490,9 @@ async fn test_mailing_list_chat_message() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_bot() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
t.set_config(Config::Bot, Some("1")).await.unwrap();
receive_imf(
@@ -1461,6 +1525,10 @@ async fn test_dont_show_noreply_in_contacts_list() {
async fn check_dont_show_in_contacts_list(addr: &str) {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(
&t,
format!(
@@ -1490,6 +1558,9 @@ YEAAAAAA!.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pdf_filename_simple() {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
let msg = load_imf_email(
&t,
include_bytes!("../../test-data/message/pdf_filename_simple.eml"),
@@ -1510,6 +1581,9 @@ async fn test_pdf_filename_simple() {
async fn test_pdf_filename_continuation() {
// test filenames split across multiple header lines, see rfc 2231
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
let msg = load_imf_email(
&t,
include_bytes!("../../test-data/message/pdf_filename_continuation.eml"),
@@ -1535,6 +1609,9 @@ async fn test_pdf_filename_continuation() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_many_images() {
let t = TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
receive_imf(
&t,
@@ -1555,6 +1632,9 @@ async fn test_many_images() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_in_reply_to() {
let t = TestContext::new().await;
t.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
t.configure_addr("bob@example.com").await;
// Receive message from Alice about group "foo".
@@ -1632,6 +1712,10 @@ async fn test_save_mime_headers_off() -> anyhow::Result<()> {
async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: bool) {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
// Claire, a customer, sends a support request
// to the alias address <support@example.org>.
@@ -1698,6 +1782,11 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
let claire = tcm.unconfigured().await;
claire.configure_addr("claire@example.org").await;
claire
.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
receive_imf(&claire, claire_request.as_bytes(), false)
.await
.unwrap();
@@ -1912,6 +2001,7 @@ Message content",
async fn test_unencrypted_doesnt_goto_self_chat() -> Result<()> {
let mut tcm = TestContextManager::new();
let t = &tcm.alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
let mut chat_id = None;
for (i, to) in [
@@ -1993,6 +2083,10 @@ async fn test_no_smtp_job_for_self_chat() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_classic_mail_creates_chat() {
let alice = TestContext::new_alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
// Alice downloads outgoing classic email.
receive_imf(
@@ -2018,6 +2112,9 @@ Message content",
async fn test_duplicate_message() -> Result<()> {
// Test that duplicate messages are ignored based on the Message-ID
let alice = TestContext::new_alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await?;
let bob_contact_id = Contact::add_or_lookup(
&alice,
@@ -2076,6 +2173,8 @@ Second signature";
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ignore_footer_status_from_mailinglist() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let bob_id = Contact::add_or_lookup(
&t,
"",
@@ -2155,6 +2254,8 @@ Original signature updated",
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ignore_old_status_updates() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let bob_id = Contact::add_or_lookup(
&t,
"",
@@ -2224,11 +2325,15 @@ sig thursday",
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_private_classical_reply() {
let mut tcm = TestContextManager::new();
for outgoing_is_classical in &[true, false] {
let t = TestContext::new_alice().await;
let t = &tcm.alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
receive_imf(
&t,
t,
format!(
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
Subject: =?utf-8?q?single_reply-to?=
@@ -2270,7 +2375,7 @@ Message-ID: <Gr.eJ_llQIXf0K.buxmrnMmG0Y@gmx.de>"
assert_eq!(group_chat.name, "single reply-to");
receive_imf(
&t,
t,
format!(
r#"Subject: Re: single reply-to
To: "Alice" <alice@example.org>
@@ -2402,8 +2507,12 @@ Sent with my Delta Chat Messenger: https://delta.chat
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_nonprivate_classical_reply() {
let mut tcm = TestContextManager::new();
for outgoing_is_classical in &[true, false] {
let t = TestContext::new_alice().await;
let t = &tcm.alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true)
.await
.unwrap();
receive_imf(
&t,
@@ -2448,7 +2557,7 @@ Message-ID: <Gr.eJ_llQIXf0K.buxmrnMmG0Y@gmx.de>"
// =============== Receive another outgoing message and check that it is put into the same chat ===============
receive_imf(
&t,
t,
format!(
r#"Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])
Subject: Out subj
@@ -2658,6 +2767,7 @@ async fn test_read_receipts_dont_unmark_bots() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_gmx_forwarded_msg() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
receive_imf(
&t,
@@ -3253,6 +3363,9 @@ async fn test_blocked_contact_creates_group() -> Result<()> {
async fn test_outgoing_undecryptable() -> Result<()> {
let alice = &TestContext::new().await;
alice.configure_addr("alice@example.org").await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
receive_imf(alice, raw, false).await?;
@@ -3289,6 +3402,7 @@ async fn test_outgoing_undecryptable() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_autocrypt() -> Result<()> {
let t = TestContext::new_bob().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let raw = include_bytes!("../../test-data/message/thunderbird_with_autocrypt.eml");
let received_msg = receive_imf(&t, raw, false).await?.unwrap();
@@ -3336,6 +3450,7 @@ async fn test_issuer_fingerprint() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_prefer_encrypt_mutual_if_encrypted() -> Result<()> {
let t = TestContext::new_bob().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
// The message has public key attached *and* Autocrypt header.
//
@@ -3407,6 +3522,9 @@ async fn test_forged_from_and_no_valid_signatures() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> {
let t = &TestContext::new_bob().await;
// TODO: same test, but with ProcessUnencrypted, should trash the message
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_signed.eml");
let raw = String::from_utf8(raw.to_vec())?.replace("From: Alice", "From: A");
receive_imf(t, raw.as_bytes(), false).await?.unwrap();
@@ -3421,6 +3539,8 @@ async fn test_wrong_from_name_and_no_valid_signatures() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
let bob = &TestContext::new_bob().await;
bob.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
// Thunderbird message with Autocrypt header and a signature,
// but not encrypted.
@@ -3459,6 +3579,11 @@ async fn test_thunderbird_autocrypt_unencrypted() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_thunderbird_unsigned() -> Result<()> {
let alice = TestContext::new_alice().await;
// TODO: same test without process unencrypted should trash the message
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await
.unwrap();
// Alice receives an unsigned message from Bob.
let raw = include_bytes!("../../test-data/message/thunderbird_encrypted_unsigned.eml");
@@ -3568,6 +3693,7 @@ async fn test_big_forwarded_with_big_attachment() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_user_adds_member() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
receive_imf(
&t,
@@ -3619,6 +3745,9 @@ async fn test_mua_user_adds_member() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_user_adds_recipient_to_single_chat() -> Result<()> {
let alice = TestContext::new_alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await?;
// Alice sends a 1:1 message to Bob, creating a 1:1 chat.
let msg = receive_imf(
@@ -4051,6 +4180,9 @@ async fn test_dont_readd_with_normal_msg() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_cant_remove() -> Result<()> {
let alice = TestContext::new_alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await?;
let now = time();
@@ -4143,6 +4275,9 @@ async fn test_mua_cant_remove() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_can_add() -> Result<()> {
let alice = TestContext::new_alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let now = time();
@@ -4202,6 +4337,9 @@ async fn test_mua_can_add() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mua_can_readd() -> Result<()> {
let alice = TestContext::new_alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await?;
// Alice creates chat with 3 contacts.
let msg = receive_imf(
@@ -4365,6 +4503,10 @@ async fn test_keep_member_list_if_possibly_nomember() -> Result<()> {
async fn test_adhoc_grp_name_no_prefix() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let chat_id = receive_imf(
alice,
b"Subject: Re: Once upon a time this was with the only Re: here\n\
@@ -4400,12 +4542,12 @@ async fn test_outgoing_msg_forgery() -> Result<()> {
imex(alice, ImexMode::ExportSelfKeys, export_dir.path(), None).await?;
// We need Bob only to encrypt the forged message to Alice's key, actually Bob doesn't
// participate in the scenario.
let bob = &TestContext::new().await;
let bob = &tcm.unconfigured().await;
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 0);
bob.configure_addr("bob@example.net").await;
imex(bob, ImexMode::ImportSelfKeys, export_dir.path(), None).await?;
assert_eq!(crate::key::load_self_secret_keyring(bob).await?.len(), 1);
let malice = &TestContext::new().await;
let malice = &tcm.unconfigured().await;
malice.configure_addr(alice_addr).await;
let malice_chat_id = tcm
@@ -4647,6 +4789,7 @@ async fn test_forged_from() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_multiline_iso_8859_1_subject() -> Result<()> {
let t = &TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let mail = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
From: bob@example.com\n\
To: alice@example.org, claire@example.com\n\
@@ -4711,6 +4854,7 @@ async fn test_references() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_list_from() -> Result<()> {
let t = &TestContext::new_alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let raw = include_bytes!("../../test-data/message/list-from.eml");
let received = receive_imf(t, raw, false).await?.unwrap();
@@ -4840,6 +4984,7 @@ async fn test_make_n_send_vcard() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_unencrypted_group_id_no_recipients() -> Result<()> {
let t = &TestContext::new_alice().await;
t.set_config(Config::ProcessUnencrypted, Some("1")).await?;
let raw = "From: alice@example.org
Subject: Group
Chat-Version: 1.0
@@ -5313,6 +5458,9 @@ async fn test_outgoing_unencrypted_chat_assignment() {
async fn test_incoming_reply_with_date_in_past() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config_bool(Config::ProcessUnencrypted, true)
.await?;
let msg0 = receive_imf(
alice,
@@ -5454,6 +5602,11 @@ async fn test_small_unencrypted_group() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
alice
.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
bob.set_config(Config::ProcessUnencrypted, Some("1"))
.await?;
let alice_chat_id = chat::create_group_unencrypted(alice, "Unencrypted group").await?;
let alice_bob_id = alice.add_or_lookup_address_contact_id(bob).await;
@@ -5533,6 +5686,7 @@ async fn test_lookup_key_contact_by_address_self() -> Result<()> {
async fn test_calendar_alternative() -> Result<()> {
let mut tcm = TestContextManager::new();
let t = &tcm.alice().await;
t.set_config_bool(Config::ProcessUnencrypted, true).await?;
let raw = include_bytes!("../../test-data/message/calendar-alternative.eml");
let msg = receive_imf(t, raw, false).await?.unwrap();
assert_eq!(msg.msg_ids.len(), 1);
@@ -5592,27 +5746,27 @@ async fn test_mark_message_as_delivered_only_after_sent_out_fully() -> Result<()
.await
.unwrap();
let (post_msg_id, post_msg_payload) = first_row_in_smtp_queue(alice).await;
assert_eq!(msg_id, post_msg_id);
assert!(post_msg_payload.len() > file_bytes.len());
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
// Alice receives her own post-message because of bcc_self
// This should not yet mark the message as delivered,
// because not everything was sent,
// but it does remove the post-message from the SMTP queue.
receive_imf(alice, post_msg_payload.as_bytes(), false).await?;
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
let (pre_msg_id, pre_msg_payload) = first_row_in_smtp_queue(alice).await;
assert_eq!(msg_id, pre_msg_id);
assert!(pre_msg_payload.len() < file_bytes.len());
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
// Alice receives her own pre-message because of bcc_self
// This should not yet mark the message as delivered,
// because not everything was sent,
// but it does remove the pre-message from the SMTP queue
receive_imf(alice, pre_msg_payload.as_bytes(), false).await?;
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
let (post_msg_id, post_msg_payload) = first_row_in_smtp_queue(alice).await;
assert_eq!(msg_id, post_msg_id);
assert!(post_msg_payload.len() > file_bytes.len());
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutPending);
// Alice receives her own post-message because of bcc_self
// This should now mark the message as delivered,
// because everything was sent by now.
receive_imf(alice, pre_msg_payload.as_bytes(), false).await?;
receive_imf(alice, post_msg_payload.as_bytes(), false).await?;
assert_eq!(msg_id.get_state(alice).await?, MessageState::OutDelivered);
Ok(())

View File

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

View File

@@ -379,7 +379,7 @@ pub(crate) async fn send_msg_to_smtp(
if retries > 6 {
context
.sql
.execute("DELETE FROM smtp WHERE msg_id=?", (msg_id,))
.execute("DELETE FROM smtp WHERE id=?", (rowid,))
.await
.context("Failed to remove message with exceeded retry limit from smtp table")?;
if let Some(mut msg) = Message::load_from_db_optional(context, msg_id).await? {

View File

@@ -2373,27 +2373,6 @@ ALTER TABLE contacts ADD COLUMN name_normalized TEXT;
.await?;
}
inc_and_check(&mut migration_version, 152)?;
if dbversion < migration_version {
sql.execute_migration(
"
UPDATE msgs SET state=26 WHERE state=28; -- Change OutMdnRcvd to OutDelivered.
UPDATE msgs SET state=19 WHERE state=24; -- Change OutPreparing to OutFailed.
",
migration_version,
)
.await?;
}
inc_and_check(&mut migration_version, 153)?;
if dbversion < migration_version {
sql.execute_migration(
"CREATE INDEX smtp_index_msg_id ON smtp (msg_id, id)",
migration_version,
)
.await?;
}
let new_version = sql
.get_raw_config_int(VERSION_CFG)
.await?

View File

@@ -722,14 +722,12 @@ ORDER BY id"
})
}
/// Returns `SentMessage` instances representing `smtp` rows for the given message. Returned
/// items go in reverse order for historical reasons.
pub async fn get_smtp_rows_for_msg<'a>(&'a self, msg_id: MsgId) -> Vec<SentMessage<'a>> {
let sent_msgs = self
.ctx
.sql
.query_map_vec(
"SELECT id, msg_id, mime, recipients FROM smtp WHERE msg_id=? ORDER BY id DESC",
"SELECT id, msg_id, mime, recipients FROM smtp WHERE msg_id=?",
(msg_id,),
|row| {
let _id: MsgId = row.get(0)?;
@@ -1057,17 +1055,9 @@ ORDER BY id"
/// This is not hooked up to any SMTP-IMAP pipeline, so the other account must call
/// [`TestContext::recv_msg`] with the returned [`SentMessage`] if it wants to receive
/// the message.
///
/// Removes SMTP jobs existed before and marks the corresponding messages as delivered, as
/// tracking of these jobs is probably already lost by the test code.
pub async fn send_msg(&self, chat_id: ChatId, msg: &mut Message) -> SentMessage<'_> {
while self.pop_sent_msg_opt(Duration::ZERO).await.is_some() {}
let msg_id = chat::send_msg(self, chat_id, msg).await.unwrap();
let rev_order = false;
let res = self
.pop_sent_msg_ex(rev_order, Duration::ZERO)
.await
.unwrap();
let res = self.pop_sent_msg().await;
assert_eq!(
res.sender_msg_id, msg_id,
"Apparently the message was not actually sent out"

View File

@@ -1,6 +1,4 @@
//! Tests about forwarding and saving Pre-Messages
use std::time::Duration;
use anyhow::Result;
use pretty_assertions::assert_eq;
@@ -10,7 +8,6 @@ use crate::chatlist::get_last_message_for_chat;
use crate::download::{DownloadState, PRE_MSG_ATTACHMENT_SIZE_THRESHOLD};
use crate::message::{Message, Viewtype};
use crate::test_utils::TestContextManager;
use crate::tests::pre_messages::util::send_large_file_message;
/// Test that forwarding Pre-Message should forward additional text to not be empty
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -89,43 +86,6 @@ async fn test_forwarding_pre_message_empty_text() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_receive_both() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let alice_chat_id = alice.create_group_with_members("", &[bob]).await;
let (pre_message, post_message, alice_msg_id) =
send_large_file_message(alice, alice_chat_id, Viewtype::File, &vec![0u8; 200_000]).await?;
let msg = bob.recv_msg(&pre_message).await;
let _ = bob.recv_msg_trash(&post_message).await;
let msg = Message::load_from_db(bob, msg.id).await?;
assert_eq!(msg.download_state(), DownloadState::Done);
assert_eq!(msg.text, "test".to_owned());
forward_msgs(alice, &[alice_msg_id], alice_chat_id).await?;
let rev_order = true;
let msg = bob
.recv_msg(
&alice
.pop_sent_msg_ex(rev_order, Duration::ZERO)
.await
.unwrap(),
)
.await;
assert_eq!(msg.download_state(), DownloadState::Available);
assert_eq!(msg.is_forwarded(), true);
assert_eq!(msg.text, "test".to_owned());
let _ = bob.recv_msg_trash(&alice.pop_sent_msg().await).await;
let msg = Message::load_from_db(bob, msg.id).await?;
assert_eq!(msg.download_state(), DownloadState::Done);
assert_eq!(msg.is_forwarded(), true);
assert_eq!(msg.text, "test".to_owned());
Ok(())
}
/// Test that forwarding Pre-Message should forward additional text to not be empty
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_saving_pre_message_empty_text() -> Result<()> {

View File

@@ -547,8 +547,8 @@ async fn test_webxdc_updates_in_post_message_after_pre_message() -> Result<()> {
.await?;
send_msg(alice, alice_chat_id, &mut alice_instance).await?;
let pre_message = alice.pop_sent_msg().await;
let post_message = alice.pop_sent_msg().await;
let pre_message = alice.pop_sent_msg().await;
let bob_instance = bob.recv_msg(&pre_message).await;
assert_eq!(bob_instance.download_state, DownloadState::Available);
@@ -588,8 +588,8 @@ async fn test_webxdc_updates_in_post_message_without_pre_message() -> Result<()>
.await?;
send_msg(alice, alice_chat_id, &mut alice_instance).await?;
let pre_message = alice.pop_sent_msg().await;
let post_message = alice.pop_sent_msg().await;
let pre_message = alice.pop_sent_msg().await;
// Bob receives post-message first.
let bob_instance = bob.recv_msg(&post_message).await;

View File

@@ -332,6 +332,7 @@ async fn test_reply() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
let bob = tcm.bob().await;
alice.set_config(Config::ProcessUnencrypted, Some("1")).await?;
if verified {
mark_as_verified(&alice, &bob).await;

View File

@@ -791,18 +791,7 @@ pub(crate) async fn sync_transports(
context
.sql
.transaction(|transaction| {
let configured_addr = transaction.query_row(
"SELECT value FROM config WHERE keyname='configured_addr'",
(),
|row| {
let addr: String = row.get(0)?;
Ok(addr)
},
)?;
for RemovedTransportData { addr, timestamp } in removed_transports {
if *addr == configured_addr {
continue;
}
modified |= transaction.execute(
"DELETE FROM transports
WHERE addr=? AND add_timestamp<=?",

View File

@@ -550,7 +550,7 @@ impl Context {
let send_now = !matches!(
instance.state,
MessageState::Undefined | MessageState::OutDraft
MessageState::Undefined | MessageState::OutPreparing | MessageState::OutDraft
);
status_update.uid = Some(create_id());

View File

@@ -983,7 +983,7 @@ async fn test_pop_status_update() -> Result<()> {
async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
let alice_chat_id = alice.create_email_chat(&bob).await.id;
let alice_chat_id = alice.create_chat(&bob).await.id;
// prepare webxdc instance,
// status updates are not sent for drafts, therefore send_webxdc_status_update() returns Ok(None)
@@ -1030,8 +1030,6 @@ async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
let bob_instance = bob.recv_msg(&sent1).await;
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert_eq!(bob_instance.get_filename().unwrap(), "minimal.xdc");
assert!(sent1.payload().contains("Content-Type: application/json"));
assert!(sent1.payload().contains("status-update.json"));
assert_eq!(
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
.await?,