diff --git a/Cargo.lock b/Cargo.lock index bf119faf0..087c89826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1605,7 +1605,7 @@ dependencies = [ [[package]] name = "email" version = "0.0.21" -source = "git+https://github.com/deltachat/rust-email?branch=master#25702df99254d059483b41417cd80696a258df8e" +source = "git+https://github.com/deltachat/rust-email?branch=master#37778c89d5eb5a94b7983f3f37ff67769bde3cf9" dependencies = [ "base64 0.11.0", "chrono", diff --git a/src/mimefactory.rs b/src/mimefactory.rs index d9d6ea1ee..8e3243ff4 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -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); @@ -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(( @@ -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( @@ -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; @@ -1543,10 +1548,11 @@ 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; - use crate::test_utils::{get_chat_msg, TestContext}; + use crate::test_utils::{get_chat_msg, TestContext, TestContextManager}; #[test] fn test_render_email_address() { let display_name = "รค space"; @@ -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); @@ -2307,4 +2317,37 @@ mod tests { Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_protected_headers_directive() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + let chat = tcm + .send_recv_accept(&alice, &bob, "alice->bob") + .await + .chat_id; + + // Now Bob can send an encrypted message to Alice. + let mut msg = Message::new(Viewtype::File); + // Long messages are truncated and MimeMessage::decoded_data is set for them. We need + // decoded_data to check presense of the necessary headers. + msg.set_text("a".repeat(constants::DC_DESIRED_TEXT_LEN + 1)); + msg.set_file_from_bytes(&bob, "foo.bar", "content".as_bytes(), None) + .await?; + 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(()) + } } diff --git a/src/test_utils.rs b/src/test_utils.rs index 52c4dd111..596c0cba9 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -108,9 +108,15 @@ impl TestContextManager { /// - Let one TestContext send a message /// - Let the other TestContext receive it and accept the chat /// - Assert that the message arrived - pub async fn send_recv_accept(&self, from: &TestContext, to: &TestContext, msg: &str) { + pub async fn send_recv_accept( + &self, + from: &TestContext, + to: &TestContext, + msg: &str, + ) -> Message { let received_msg = self.send_recv(from, to, msg).await; received_msg.chat_id.accept(to).await.unwrap(); + received_msg } /// - Let one TestContext send a message