From ade39fe026a3539d5279a6902f23fe6b825c17a7 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 14 Aug 2021 00:00:00 +0000 Subject: [PATCH] fix: do not set WantsMdn param for outgoing messages This bug sometimes results in sending read receipts to self in multi-device setups. It happens consistently in a setup where the first device is configured to move messages to DeltaChat folder and the second device is not. When both devices receive BCC-self message simultaneously, the first device moves the message to DeltaChat folder, while the second device tries to mark the message as seen in the Inbox. Regardless of whether the second device marks the message as seen successfully or fails because the message is already moved by the first device, `Job.markseen_msg_on_imap()` sends the read receipt to the From address. --- src/mimeparser.rs | 59 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/mimeparser.rs b/src/mimeparser.rs index c06ab5c13..57173882e 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -276,7 +276,7 @@ impl MimeMessage { parser.maybe_remove_bad_parts(); parser.maybe_remove_inline_mailinglist_footer(); parser.heuristically_parse_ndn(context).await; - parser.parse_headers(context).await; + parser.parse_headers(context).await?; if warn_empty_signature && parser.signatures.is_empty() { for part in parser.parts.iter_mut() { @@ -419,7 +419,7 @@ impl MimeMessage { } } - async fn parse_headers(&mut self, context: &Context) { + async fn parse_headers(&mut self, context: &Context) -> Result<()> { self.parse_system_message_headers(context); self.parse_avatar_headers(context).await; self.parse_videochat_headers(); @@ -464,15 +464,20 @@ impl MimeMessage { if !self.decrypting_failed && !self.parts.is_empty() { if let Some(ref dn_to) = self.chat_disposition_notification_to { if let Some(from) = self.from.get(0) { - if from.addr.to_lowercase() == dn_to.addr.to_lowercase() { - if let Some(part) = self.parts.last_mut() { - part.param.set_int(Param::WantsMdn, 1); + // Check that the message is not outgoing. + if !context.is_self_addr(&from.addr).await? { + if from.addr.to_lowercase() == dn_to.addr.to_lowercase() { + if let Some(part) = self.parts.last_mut() { + part.param.set_int(Param::WantsMdn, 1); + } + } else { + warn!( + context, + "{} requested a read receipt to {}, ignoring", + from.addr, + dn_to.addr + ); } - } else { - warn!( - context, - "{} requested a read receipt to {}, ignoring", from.addr, dn_to.addr - ); } } } @@ -502,6 +507,8 @@ impl MimeMessage { part.param.set(Param::Bot, "1"); } } + + Ok(()) } async fn avatar_action_from_header( @@ -2954,4 +2961,36 @@ Some reply Ok(()) } + + // Test that WantsMdn parameter is not set on outgoing messages. + #[async_std::test] + async fn test_outgoing_wants_mdn() -> Result<()> { + let alice = TestContext::new_alice().await; + let bob = TestContext::new_bob().await; + + let raw = br###"Date: Thu, 28 Jan 2021 00:26:57 +0000 +Chat-Version: 1.0\n\ +Message-ID: +To: Bob +From: Alice +Subject: subject +Chat-Disposition-Notification-To: alice@example.com + +Message. +"###; + + // Bob receives message. + dc_receive_imf(&bob, raw, "INBOX", 1, false).await?; + let msg = bob.get_last_msg().await; + // Message is incoming. + assert!(msg.param.get_bool(Param::WantsMdn).unwrap()); + + // Alice receives copy-to-self. + dc_receive_imf(&alice, raw, "INBOX", 1, false).await?; + let msg = alice.get_last_msg().await; + // Message is outgoing, don't send read receipt to self. + assert!(msg.param.get_bool(Param::WantsMdn).is_none()); + + Ok(()) + } }