From a0f57e28876bfaf0115277f561ac5ca112f1de3f Mon Sep 17 00:00:00 2001 From: bbottema Date: Thu, 4 Apr 2024 22:13:41 +0200 Subject: [PATCH] #500: [bug] Fix parsing addresses from headers in EML files, like a Disposition-Notification-To with umlaut --- .../internal/config/EmailProperty.java | 2 + .../mimemessage/MimeMessageParser.java | 44 ++++--- .../converter/EmailConverterTest.java | 7 ++ .../mimemessage/MimeMessageParserTest.java | 3 +- ... umlaut in Disposition-Notification-To.eml | 112 ++++++++++++++++++ 5 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 modules/simple-java-mail/src/test/resources/test-messages/#500 Email with problematic umlaut in Disposition-Notification-To.eml diff --git a/modules/core-module/src/main/java/org/simplejavamail/internal/config/EmailProperty.java b/modules/core-module/src/main/java/org/simplejavamail/internal/config/EmailProperty.java index a600cd79..a96b0829 100644 --- a/modules/core-module/src/main/java/org/simplejavamail/internal/config/EmailProperty.java +++ b/modules/core-module/src/main/java/org/simplejavamail/internal/config/EmailProperty.java @@ -1,5 +1,6 @@ package org.simplejavamail.internal.config; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -14,6 +15,7 @@ */ @RequiredArgsConstructor @Getter +@SuppressFBWarnings("SE_BAD_FIELD") public enum EmailProperty { HEADERS(Email::getHeaders, true), diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java index 6516220d..1e06e0b9 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java @@ -2,6 +2,7 @@ import jakarta.activation.*; import jakarta.mail.Address; +import jakarta.mail.Header; import jakarta.mail.Message.RecipientType; import jakarta.mail.MessagingException; import jakarta.mail.Multipart; @@ -29,7 +30,6 @@ import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toList; import static org.simplejavamail.internal.util.MiscUtil.extractCID; import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; import static org.slf4j.LoggerFactory.getLogger; @@ -75,7 +75,7 @@ public static ParsedMimeMessageComponents parseMimeMessage(@NotNull final MimeMe } private static void parseMimePartTree(@NotNull final MimePart currentPart, @NotNull final ParsedMimeMessageComponents parsedComponents, final boolean fetchAttachmentData) { - for (final DecodedHeader header : retrieveAllHeaders(currentPart)) { + for (final Header header : retrieveAllHeaders(currentPart)) { parseHeader(header, parsedComponents); } @@ -122,8 +122,8 @@ private static void parseDataSource(@NotNull MimePart currentPart, @NotNull Pars private static void checkContentTransferEncoding(final MimePart currentPart, @NotNull final ParsedMimeMessageComponents parsedComponents) { if (parsedComponents.contentTransferEncoding == null) { - for (final DecodedHeader header : retrieveAllHeaders(currentPart)) { - if (isEmailHeader(header, "Content-Transfer-Encoding")) { + for (final Header header : retrieveAllHeaders(currentPart)) { + if (isEmailHeader(DecodedHeader.of(header), "Content-Transfer-Encoding")) { parsedComponents.contentTransferEncoding = header.getValue(); } } @@ -139,21 +139,20 @@ private static MimeDataSource parseAttachment(@Nullable final String contentId, .build(); } - private static void parseHeader(final DecodedHeader header, @NotNull final ParsedMimeMessageComponents parsedComponents) { - val headerValue = decodeText(header.getValue()); - val headerName = decodeText(header.getName()); + private static void parseHeader(final Header header, @NotNull final ParsedMimeMessageComponents parsedComponents) { + val decodedHeader = DecodedHeader.of(header); - if (isEmailHeader(header, "Disposition-Notification-To")) { - parsedComponents.dispositionNotificationTo = createAddress(headerValue, "Disposition-Notification-To"); - } else if (isEmailHeader(header, "Return-Receipt-To")) { - parsedComponents.returnReceiptTo = createAddress(headerValue, "Return-Receipt-To"); - } else if (isEmailHeader(header, "Return-Path")) { - parsedComponents.bounceToAddress = createAddress(headerValue, "Return-Path"); + if (isEmailHeader(decodedHeader, "Disposition-Notification-To")) { + parsedComponents.dispositionNotificationTo = createAddressFromEncodedHeader(header, "Disposition-Notification-To"); + } else if (isEmailHeader(decodedHeader, "Return-Receipt-To")) { + parsedComponents.returnReceiptTo = createAddressFromEncodedHeader(header, "Return-Receipt-To"); + } else if (isEmailHeader(decodedHeader, "Return-Path")) { + parsedComponents.bounceToAddress = createAddressFromEncodedHeader(header, "Return-Path"); } else { - if (!parsedComponents.headers.containsKey(headerName)) { - parsedComponents.headers.put(headerName, new ArrayList<>()); + if (!parsedComponents.headers.containsKey(decodedHeader.getName())) { + parsedComponents.headers.put(decodedHeader.getName(), new ArrayList<>()); } - parsedComponents.headers.get(headerName).add(MimeUtility.unfold(headerValue)); + parsedComponents.headers.get(decodedHeader.getName()).add(MimeUtility.unfold(decodedHeader.getValue())); } } @@ -280,25 +279,24 @@ private static String parseResourceName(@Nullable String possibleWrappedContentI @SuppressWarnings("WeakerAccess") @NotNull - public static List retrieveAllHeaders(@NotNull final MimePart part) { + public static List
retrieveAllHeaders(@NotNull final MimePart part) { try { - return Collections.list(part.getAllHeaders()).stream() - .map(DecodedHeader::of) - .collect(toList()); + return Collections.list(part.getAllHeaders()); } catch (final MessagingException e) { throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_ALL_HEADERS, e); } } @Nullable - static InternetAddress createAddress(final String address, final String typeOfAddress) { + static InternetAddress createAddressFromEncodedHeader(final Header headerWithAddress, final String typeOfAddress) { + val encodedAddress = headerWithAddress.getValue(); try { - return address.trim().isEmpty() ? null : new InternetAddress(address); + return encodedAddress.trim().isEmpty() ? null : InternetAddress.parseHeader(encodedAddress, true)[0]; } catch (final AddressException e) { if (e.getMessage().equals("Empty address")) { return null; } - throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_PARSING_ADDRESS, typeOfAddress, address), e); + throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_PARSING_ADDRESS, typeOfAddress, encodedAddress), e); } } diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java index 47b34063..47c9381d 100644 --- a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/EmailConverterTest.java @@ -175,6 +175,13 @@ public void testProblematicEmbeddedImage() { assertThat(s1.getHTMLText()).containsPattern("\"cid:DB294AA3-160F-4825-923A-B16C8B674543@home\""); } + @Test + public void testProblematicUmlautInDispositionNotificationTo() { + Email s1 = EmailConverter.emlToEmail(new File(RESOURCE_TEST_MESSAGES + "/#500 Email with problematic umlaut in Disposition-Notification-To.eml")); + EmailAssert.assertThat(s1).hasFromRecipient(new Recipient("Könok, Danny [Fake Company & Co. KG]", "test@fakedomain.de", null)); + EmailAssert.assertThat(s1).hasDispositionNotificationTo(new Recipient("Könok, Danny [Fake Company & Co. KG]", "test@fakedomain.de", null)); + } + @Test public void testProblematic8BitContentTransferEncoding() { Email s1 = EmailConverter.emlToEmail(new File(RESOURCE_TEST_MESSAGES + "/#485 Email with 8Bit Content Transfer Encoding.eml")); diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserTest.java index a3b346b9..3b276145 100644 --- a/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserTest.java +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParserTest.java @@ -2,6 +2,7 @@ import jakarta.activation.DataHandler; import jakarta.mail.BodyPart; +import jakarta.mail.Header; import jakarta.mail.MessagingException; import jakarta.mail.Part; import jakarta.mail.Session; @@ -177,7 +178,7 @@ public void testCreateAddress() throws UnsupportedEncodingException { @Nullable private InternetAddress interpretRecipient(String address) { - return MimeMessageParser.createAddress(address, "TO"); + return MimeMessageParser.createAddressFromEncodedHeader(new Header("ignored", address), "TO"); } @Test diff --git a/modules/simple-java-mail/src/test/resources/test-messages/#500 Email with problematic umlaut in Disposition-Notification-To.eml b/modules/simple-java-mail/src/test/resources/test-messages/#500 Email with problematic umlaut in Disposition-Notification-To.eml new file mode 100644 index 00000000..5799e8bd --- /dev/null +++ b/modules/simple-java-mail/src/test/resources/test-messages/#500 Email with problematic umlaut in Disposition-Notification-To.eml @@ -0,0 +1,112 @@ +From: + =?iso-8859-1?Q?K=F6nok=2C_Danny_=5BFake_Company_=26_Co=2E_K?= + =?iso-8859-1?Q?G=5D?= +To: "Bestellung [Fake Company]" + +Subject: Test +Disposition-Notification-To: + =?iso-8859-1?Q?K=F6nok=2C_Danny_=5BFake_Company_=26_Co=2E_K?= + =?iso-8859-1?Q?G=5D?= +Date: Thu, 28 Mar 2024 14:07:46 +0100 +Message-ID: <5ad10a63dcc64c02bb5b92dce63ab4ca@fakedomain.de> +Accept-Language: de-DE, en-US +Content-Language: de-DE +Content-Type: text/html; charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable +MIME-Version: 1.0 + + + + + + + + +
+

Hallo,

+

 

+

Lirumlarum – 02.04.2= +024

+

 

+

2,5t 550

+

1,2t 997

+

 

+

Danke.

+

 

+

Gr=FC=DFe, +

 

+
+

Danny K=F6nok<= +/span>

+ + + + + + + + + + + + + + + + + + + +
xxx/ Faktura
Telefon:+49 0000 / 000-000
Email:test@fakedomain.de
+
+ + +.