Skip to content

Commit

Permalink
fix: Add protected-headers directive to Content-Type of encrypted/sig…
Browse files Browse the repository at this point in the history
…ned MIME (#2302)

Add protected-headers="v1" directive to Content-Type of an encrypted/signed MIME so that other MUAs
like Thunderbird display the true message Subject instead of "...".
  • Loading branch information
iequidoo committed Oct 2, 2023
1 parent 2dd44d5 commit 6345099
Showing 1 changed file with 54 additions and 7 deletions.
61 changes: 54 additions & 7 deletions src/mimefactory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,12 @@ impl<'a> MimeFactory<'a> {
})
};

let get_content_type_directives_header = || {
(
"Content-Type-Deltachat-Directives".to_string(),
"protected-headers=\"v1\"".to_string(),
)
};
let outer_message = if is_encrypted {
headers.protected.push(from_header);

Expand Down Expand Up @@ -714,10 +720,7 @@ impl<'a> MimeFactory<'a> {
if !existing_ct.ends_with(';') {
existing_ct += ";";
}
let message = message.replace_header(Header::new(
"Content-Type".to_string(),
format!("{existing_ct} protected-headers=\"v1\";"),
));
let message = message.header(get_content_type_directives_header());

// Set the appropriate Content-Type for the outer message
let outer_message = PartBuilder::new().header((
Expand Down Expand Up @@ -786,11 +789,12 @@ impl<'a> MimeFactory<'a> {
{
message
} else {
let message = message.header(get_content_type_directives_header());
let (payload, signature) = encrypt_helper.sign(context, message).await?;
PartBuilder::new()
.header((
"Content-Type".to_string(),
"multipart/signed; protocol=\"application/pgp-signature\"".to_string(),
"Content-Type",
"multipart/signed; protocol=\"application/pgp-signature\"",
))
.child(payload)
.child(
Expand Down Expand Up @@ -1535,6 +1539,7 @@ fn maybe_encode_words(words: &str) -> String {
#[cfg(test)]
mod tests {
use mailparse::{addrparse_header, MailHeaderMap};
use std::str;

use super::*;
use crate::chat::ChatId;
Expand All @@ -1543,6 +1548,7 @@ mod tests {
ProtectionStatus,
};
use crate::chatlist::Chatlist;
use crate::constants;
use crate::contact::{ContactAddress, Origin};
use crate::mimeparser::MimeMessage;
use crate::receive_imf::receive_imf;
Expand Down Expand Up @@ -2195,7 +2201,11 @@ mod tests {
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);

let part = payload.next().unwrap();
assert_eq!(part.match_indices("multipart/mixed").count(), 1);
assert_eq!(
part.match_indices("multipart/mixed; protected-headers=\"v1\"")
.count(),
1
);
assert_eq!(part.match_indices("Subject:").count(), 1);
assert_eq!(part.match_indices("Autocrypt:").count(), 0);
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
Expand Down Expand Up @@ -2307,4 +2317,41 @@ mod tests {

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_protected_headers_directive() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
let chat = alice
.create_chat_with_contact("bob", &bob.get_config(Config::Addr).await?.unwrap())
.await;
let sent1 = alice.send_text(chat.id, "alice->bob").await;
let msg = bob.recv_msg(&sent1).await;
let chat = msg.chat_id;
chat.accept(&bob).await?;

// Now Bob can send an encrypted message to Alice.
let filename_sent = "foo.bar";
let attachment = bob.blobdir.join(filename_sent);
let content = format!("File content of {filename_sent}");
tokio::fs::write(&attachment, content.as_bytes()).await?;
let mut msg = Message::new(Viewtype::File);
// Long messages are truncated and MimeMessage::decoded_data is set for them.
msg.set_text("a".repeat(constants::DC_DESIRED_TEXT_LEN + 1));
msg.set_file(attachment.to_str().unwrap(), None);
let sent = bob.send_msg(chat, &mut msg).await;
assert!(msg.get_showpadlock());

let mime = MimeMessage::from_bytes(&alice, sent.payload.as_bytes(), None).await?;
let mut payload = str::from_utf8(&mime.decoded_data)?.splitn(2, "\r\n\r\n");
let part = payload.next().unwrap();
assert_eq!(
part.match_indices("multipart/mixed; protected-headers=\"v1\"")
.count(),
1
);
assert_eq!(part.match_indices("Subject:").count(), 1);

Ok(())
}
}

0 comments on commit 6345099

Please sign in to comment.