From 22da92c563cf9d97ffb33684acce4e1a7ab218c1 Mon Sep 17 00:00:00 2001 From: iequidoo Date: Wed, 5 Nov 2025 19:22:00 -0300 Subject: [PATCH] feat: Do not copy Chat-Version header into outer part Chat-Version is used sometimes by Sieve filters to move messages to DeltaChat folder: https://github.com/mailcow/mailcow-dockerized/blob/37beed6ad93f259b97cad41877982bce95295629/data/conf/dovecot/global_sieve_before This probably prevents notifications to MUAs that don't watch DeltaChat but watch INBOX. There are however disadvantages to exposing Chat-Version: 1. Spam filters may not like this header and it is difficult or impossible to tell if `Chat-Version` plays role in rejecting the message or delivering it into Spam folder. If there is no such header visible to the spam filter, this possibility can be ruled out. 2. Replies to chat messages may have no `Chat-Version` but have to be moved anyway. 3. The user may have no control over the Sieve filter, but it comes preconfigured in mailcow, so it is not possible to disable it on the client. Thanks to link2xt for providing this motivation. NOTE: Old Delta Chat will assign partially downloaded replies to an ad-hoc group with the sender instead of the 1:1 chat, but we're removing partial downloads anyway. --- src/mimefactory.rs | 9 ++------- src/receive_imf.rs | 12 ++++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/mimefactory.rs b/src/mimefactory.rs index e9b76aa72..6aed5e6dc 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -947,8 +947,7 @@ impl MimeFactory { // // These are standard headers such as Date, In-Reply-To, References, which cannot be placed // anywhere else according to the standard. Placing headers here also allows them to be fetched - // individually over IMAP without downloading the message body. This is why Chat-Version is - // placed here. + // individually over IMAP without downloading the message body. let mut unprotected_headers: Vec<(&'static str, HeaderType<'static>)> = Vec::new(); // Headers that MUST NOT (only) go into IMF header section: @@ -1063,11 +1062,7 @@ impl MimeFactory { mail_builder::headers::raw::Raw::new("[...]").into(), )); } - "in-reply-to" - | "references" - | "auto-submitted" - | "chat-version" - | "autocrypt-setup-message" => { + "in-reply-to" | "references" | "auto-submitted" | "autocrypt-setup-message" => { unprotected_headers.push(header.clone()); } _ => { diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 2d734d2b3..81f4e35cd 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -2414,7 +2414,14 @@ async fn lookup_chat_by_reply( // If this was a private message just to self, it was probably a private reply. // It should not go into the group then, but into the private chat. - if is_probably_private_reply(context, mime_parser, parent_chat_id).await? { + if is_probably_private_reply( + context, + mime_parser, + is_partial_download.is_some(), + parent_chat_id, + ) + .await? + { return Ok(None); } @@ -2561,6 +2568,7 @@ async fn lookup_or_create_adhoc_group( async fn is_probably_private_reply( context: &Context, mime_parser: &MimeMessage, + is_partial_download: bool, parent_chat_id: ChatId, ) -> Result { // Message cannot be a private reply if it has an explicit Chat-Group-ID header. @@ -2579,7 +2587,7 @@ async fn is_probably_private_reply( return Ok(false); } - if !mime_parser.has_chat_version() { + if !is_partial_download && !mime_parser.has_chat_version() { let chat_contacts = chat::get_chat_contacts(context, parent_chat_id).await?; if chat_contacts.len() == 2 && chat_contacts.contains(&ContactId::SELF) { return Ok(false);