diff --git a/src/headerdef.rs b/src/headerdef.rs index ca282a81b..f65729c9a 100644 --- a/src/headerdef.rs +++ b/src/headerdef.rs @@ -38,6 +38,9 @@ pub enum HeaderDef { /// Mailing list ID defined in [RFC 2919](https://tools.ietf.org/html/rfc2919). ListId, ListPost, + + /// List-Help header defined in [RFC 2369](https://datatracker.ietf.org/doc/html/rfc2369). + ListHelp, References, /// In-Reply-To header containing Message-ID of the parent message. diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 95aab357f..0d1acef8f 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -1365,6 +1365,15 @@ impl MimeMessage { self.get_mailinglist_header().is_some() } + /// Detects Schleuder mailing list by List-Help header. + pub(crate) fn is_schleuder_message(&self) -> bool { + if let Some(list_help) = self.get_header(HeaderDef::ListHelp) { + list_help == "" + } else { + false + } + } + pub fn replace_msg_by_error(&mut self, error_msg: &str) { self.is_system_message = SystemMessage::Unknown; if let Some(part) = self.parts.first_mut() { @@ -1593,8 +1602,12 @@ impl MimeMessage { /// eg. when the user-edited-content is html. /// As these footers would appear as repeated, separate text-bubbles, /// we remove them. + /// + /// We make an exception for Schleuder mailing lists + /// because they typically create messages with two text parts, + /// one for headers and one for the actual contents. fn maybe_remove_inline_mailinglist_footer(&mut self) { - if self.is_mailinglist_message() { + if self.is_mailinglist_message() && !self.is_schleuder_message() { let text_part_cnt = self .parts .iter() @@ -3786,4 +3799,24 @@ Content-Disposition: reaction\n\ Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_schleuder() -> Result<()> { + let context = TestContext::new_alice().await; + let raw = include_bytes!("../test-data/message/schleuder.eml"); + + let msg = MimeMessage::from_bytes(&context.ctx, &raw[..], None) + .await + .unwrap(); + assert_eq!(msg.parts.len(), 2); + + // Header part. + assert_eq!(msg.parts[0].typ, Viewtype::Text); + + // Actual contents part. + assert_eq!(msg.parts[1].typ, Viewtype::Text); + assert_eq!(msg.parts[1].msg, "hello,\nbye"); + + Ok(()) + } } diff --git a/test-data/message/schleuder.eml b/test-data/message/schleuder.eml new file mode 100644 index 000000000..bdcbe492a --- /dev/null +++ b/test-data/message/schleuder.eml @@ -0,0 +1,66 @@ +Return-Path: +Delivered-To: alice@testrun.org +Date: Tue, 02 Jan 2024 05:00:00 +0000 +From: mailing-list@example.org +Sender: mailing-list-bounce@example.org +To: alice@testrun.org +Message-ID: <87wmss8juz.fsf@example.org> +In-Reply-To: +References: +Subject: [REPOST] Some subject +Mime-Version: 1.0 +Content-Type: multipart/signed; + boundary="--==_mimepart_65938a80866e8_663a2abed9b585c064398"; + micalg=pgp-sha1; + protocol="application/pgp-signature" +Content-Transfer-Encoding: 7bit +List-Id: +List-Owner: (Use list's public + key) +List-Help: +List-Post: + +This is an OpenPGP/MIME signed message (RFC 4880 and 3156) +----==_mimepart_65938a80866e8_663a2abed9b585c064398 +Content-Type: multipart/mixed; + boundary="--==_mimepart_65938a8086476_663a2abed9b585c0642c7"; + charset=UTF-8 +Content-Transfer-Encoding: 7bit + + +----==_mimepart_65938a8086476_663a2abed9b585c0642c7 +Content-Type: text/plain; + charset=UTF-8 +Content-Transfer-Encoding: 7bit + +From: bob@example.org +To: mailing-list@example.org +Cc: +Date: Tue, 02 Jan 2024 05:00:00 +0000 +Sig: Unsigned +Enc: Unencrypted + +----==_mimepart_65938a8086476_663a2abed9b585c0642c7 +Content-Type: text/plain; + charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +hello, +bye + +----==_mimepart_65938a8086476_663a2abed9b585c0642c7-- + +----==_mimepart_65938a80866e8_663a2abed9b585c064398 +Content-Type: application/pgp-signature; + name=signature.asc +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; + filename=signature.asc +Content-Description: OpenPGP digital signature + +-----BEGIN PGP SIGNATURE----- + +REDACTED +-----END PGP SIGNATURE----- + +----==_mimepart_65938a80866e8_663a2abed9b585c064398--