Skip to content

Commit 3ff41b4

Browse files
committed
fix: Ignore protected headers in outer message part (#6357)
Delta Chat always adds protected headers to the inner encrypted or signed message, so if a protected header is only present in the outer part, it should be ignored because it's probably added by the server or somebody else. The exception is Subject because there are known cases when it's only present in the outer message part, e.g. an encrypted unsigned Thunderbird message. Also handle "Auto-Submitted" and "Autocrypt-Setup-Message" as protected headers on the receiver side, this was apparently forgotten. This may fix #6357 where Saved Messages (i.e. `ContactId::SELF`) and some contacts are unexpectedly marked as bots which can happen if e.g. the server adds "Auto-Submitted: auto-generated" to messages for some reason. Maybe sounds unlikely, but let's try.
1 parent 0cadfe3 commit 3ff41b4

File tree

3 files changed

+46
-21
lines changed

3 files changed

+46
-21
lines changed

src/decrypt.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ mod tests {
259259
let bob = TestContext::new_bob().await;
260260
receive_imf(&bob, attachment_mime, false).await?;
261261
let msg = bob.get_last_msg().await;
262-
assert_eq!(msg.text, "Hello from Thunderbird!");
262+
// Subject should be prepended because the attachment doesn't have "Chat-Version".
263+
assert_eq!(msg.text, "Hello, Bob! – Hello from Thunderbird!");
263264

264265
Ok(())
265266
}

src/mimeparser.rs

+26-20
Original file line numberDiff line numberDiff line change
@@ -1528,12 +1528,15 @@ impl MimeMessage {
15281528
chat_disposition_notification_to: &mut Option<SingleInfo>,
15291529
fields: &[mailparse::MailHeader<'_>],
15301530
) {
1531+
// Keep Subject so that it's displayed for signed-only messages. They are shown w/o a
1532+
// padlock anyway.
1533+
headers.retain(|k, _| !is_protected(k) || k == "subject");
15311534
for field in fields {
15321535
// lowercasing all headers is technically not correct, but makes things work better
15331536
let key = field.get_key().to_lowercase();
1534-
if !headers.contains_key(&key) || // key already exists, only overwrite known types (protected headers)
1535-
is_known(&key) || key.starts_with("chat-")
1536-
{
1537+
// Don't overwrite unprotected headers, but overwrite protected ones because DKIM
1538+
// signature applies to the last headers.
1539+
if !headers.contains_key(&key) || is_protected(&key) {
15371540
if key == HeaderDef::ChatDispositionNotificationTo.get_headername() {
15381541
match addrparse_header(field) {
15391542
Ok(addrlist) => {
@@ -1929,23 +1932,26 @@ pub(crate) fn parse_message_id(ids: &str) -> Result<String> {
19291932

19301933
/// Returns true if the header overwrites outer header
19311934
/// when it comes from protected headers.
1932-
fn is_known(key: &str) -> bool {
1933-
matches!(
1934-
key,
1935-
"return-path"
1936-
| "date"
1937-
| "from"
1938-
| "sender"
1939-
| "reply-to"
1940-
| "to"
1941-
| "cc"
1942-
| "bcc"
1943-
| "message-id"
1944-
| "in-reply-to"
1945-
| "references"
1946-
| "subject"
1947-
| "secure-join"
1948-
)
1935+
fn is_protected(key: &str) -> bool {
1936+
key.starts_with("chat-")
1937+
|| matches!(
1938+
key,
1939+
"return-path"
1940+
| "auto-submitted"
1941+
| "autocrypt-setup-message"
1942+
| "date"
1943+
| "from"
1944+
| "sender"
1945+
| "reply-to"
1946+
| "to"
1947+
| "cc"
1948+
| "bcc"
1949+
| "message-id"
1950+
| "in-reply-to"
1951+
| "references"
1952+
| "subject"
1953+
| "secure-join"
1954+
)
19491955
}
19501956

19511957
/// Parsed MIME part.

src/receive_imf/tests.rs

+18
Original file line numberDiff line numberDiff line change
@@ -4052,6 +4052,24 @@ async fn test_unsigned_chat_group_hdr() -> Result<()> {
40524052
Ok(())
40534053
}
40544054

4055+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
4056+
async fn test_ignore_protected_headers_in_outer_msg() -> Result<()> {
4057+
let mut tcm = TestContextManager::new();
4058+
let alice = &tcm.alice().await;
4059+
let bob = &tcm.bob().await;
4060+
let bob_chat_id = tcm.send_recv_accept(alice, bob, "hi").await.chat_id;
4061+
send_text_msg(bob, bob_chat_id, "hi all!".to_string()).await?;
4062+
let mut sent_msg = bob.pop_sent_msg().await;
4063+
sent_msg.payload = sent_msg.payload.replace(
4064+
"Chat-Version:",
4065+
"Auto-Submitted: auto-generated\r\nChat-Version:",
4066+
);
4067+
alice.recv_msg(&sent_msg).await;
4068+
let ab_contact = alice.add_or_lookup_contact(bob).await;
4069+
assert!(!ab_contact.is_bot());
4070+
Ok(())
4071+
}
4072+
40554073
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
40564074
async fn test_sync_member_list_on_rejoin() -> Result<()> {
40574075
let mut tcm = TestContextManager::new();

0 commit comments

Comments
 (0)