diff --git a/python/tests/test_account.py b/python/tests/test_account.py index 062d429c3..3d0222585 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -1161,8 +1161,8 @@ class TestOnlineAccount: # Majority prefers encryption now assert msg5.is_encrypted() - def test_reply_encrypted(self, acfactory, lp): - """Test that replies to encrypted messages are encrypted.""" + def test_quote_encrypted(self, acfactory, lp): + """Test that replies to encrypted messages with quotes are encrypted.""" ac1, ac2 = acfactory.get_two_online_accounts() lp.sec("ac1: create chat with ac2") @@ -1211,6 +1211,39 @@ class TestOnlineAccount: assert msg_in.quoted_text == quoted_msg.text assert msg_in.is_encrypted() == quoted_msg.is_encrypted() + def test_quote_attachment(self, tmpdir, acfactory, lp): + """Test that replies with an attachment and a quote are received correctly.""" + ac1, ac2 = acfactory.get_two_online_accounts() + + lp.sec("ac1 creates chat with ac2") + chat1 = ac1.create_chat(ac2) + + lp.sec("ac1 sends text message to ac2") + chat1.send_text("hi") + + lp.sec("ac2 receives contact request from ac1") + received_message = ac2._evtracker.wait_next_messages_changed() + assert received_message.text == "hi" + + basename = "attachment.txt" + p = os.path.join(tmpdir.strpath, basename) + with open(p, "w") as f: + f.write("data to send") + + lp.sec("ac2 sends a reply to ac1") + chat2 = received_message.create_chat() + reply = Message.new_empty(ac2, "file") + reply.set_text("message reply") + reply.set_file(p) + reply.quote = received_message + chat2.send_msg(reply) + + lp.sec("ac1 receives a reply from ac2") + received_reply = ac1._evtracker.wait_next_incoming_message() + assert received_reply.text == "message reply" + assert received_reply.quoted_text == received_message.text + assert open(received_reply.filename).read() == "data to send" + def test_saved_mime_on_received_message(self, acfactory, lp): ac1, ac2 = acfactory.get_two_online_accounts() diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 41b2a10d7..07aa0c34d 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -295,7 +295,8 @@ impl MimeMessage { /// Squashes mutlipart chat messages with attachment into single-part messages. /// /// Delta Chat sends attachments, such as images, in two-part messages, with the first message - /// containing an explanation. If such a message is detected, first part can be safely dropped. + /// containing a description. If such a message is detected, text from the first part can be + /// moved to the second part, and the first part dropped. #[allow(clippy::indexing_slicing)] fn squash_attachment_parts(&mut self) { if let [textpart, filepart] = &self.parts[..] { @@ -315,6 +316,9 @@ impl MimeMessage { // insert new one filepart.msg = self.parts[0].msg.clone(); + if let Some(quote) = self.parts[0].param.get(Param::Quote) { + filepart.param.set(Param::Quote, quote); + } // forget the one we use now self.parts[0].msg = "".to_string(); @@ -2299,4 +2303,22 @@ On 2020-10-25, Bob wrote: ); assert_eq!(message.parts[0].msg, "A reply."); } + + #[async_std::test] + async fn test_attachment_quote() { + let context = TestContext::new().await; + let raw = include_bytes!("../test-data/message/quote_attach.eml"); + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); + + assert_eq!(mimeparser.get_subject().unwrap(), "Message from Alice"); + assert_eq!(mimeparser.parts.len(), 1); + assert_eq!(mimeparser.parts[0].msg, "Reply"); + assert_eq!( + mimeparser.parts[0].param.get(Param::Quote).unwrap(), + "Quote" + ); + assert_eq!(mimeparser.parts[0].typ, Viewtype::File); + } } diff --git a/test-data/message/quote_attach.eml b/test-data/message/quote_attach.eml new file mode 100644 index 000000000..18a4c5a5a --- /dev/null +++ b/test-data/message/quote_attach.eml @@ -0,0 +1,27 @@ +Subject: Message from Alice +MIME-Version: 1.0 +In-Reply-To: +Date: Sun, 08 Nov 2020 01:16:26 +0000 +Chat-Version: 1.0 +Message-ID: +To: Bob +From: Alice +Content-Type: multipart/mixed; boundary="uWbWY2IyEtJ8wZmp282Na11hxBBXlV" + + +--uWbWY2IyEtJ8wZmp282Na11hxBBXlV +Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no + +> Quote + +Reply + +--uWbWY2IyEtJ8wZmp282Na11hxBBXlV +Content-Type: text/plain +Content-Disposition: attachment; filename="attachment.txt" +Content-Transfer-Encoding: base64 + +ZGF0YSB0byBzZW5k + +--uWbWY2IyEtJ8wZmp282Na11hxBBXlV-- +