77import jakarta .activation .DataSource ;
88import jakarta .activation .MailcapCommandMap ;
99import jakarta .mail .Address ;
10- import jakarta .mail .Header ;
1110import jakarta .mail .Message .RecipientType ;
1211import jakarta .mail .MessagingException ;
1312import jakarta .mail .Multipart ;
2120import jakarta .mail .internet .MimeUtility ;
2221import jakarta .mail .internet .ParseException ;
2322import jakarta .mail .util .ByteArrayDataSource ;
23+ import lombok .val ;
2424import org .jetbrains .annotations .NotNull ;
2525import org .jetbrains .annotations .Nullable ;
2626import org .simplejavamail .internal .util .MiscUtil ;
4848import static java .lang .String .format ;
4949import static java .nio .charset .StandardCharsets .UTF_8 ;
5050import static java .util .Optional .ofNullable ;
51+ import static java .util .stream .Collectors .toList ;
5152import static org .simplejavamail .internal .util .MiscUtil .extractCID ;
5253import static org .simplejavamail .internal .util .MiscUtil .valueNullOrEmpty ;
5354
@@ -139,7 +140,7 @@ public static ParsedMimeMessageComponents parseMimeMessage(@NotNull final MimeMe
139140 }
140141
141142 private static void parseMimePartTree (@ NotNull final MimePart currentPart , @ NotNull final ParsedMimeMessageComponents parsedComponents , final boolean fetchAttachmentData ) {
142- for (final Header header : retrieveAllHeaders (currentPart )) {
143+ for (final DecodedHeader header : retrieveAllHeaders (currentPart )) {
143144 parseHeader (header , parsedComponents );
144145 }
145146
@@ -180,7 +181,7 @@ private static void parseMimePartTree(@NotNull final MimePart currentPart, @NotN
180181
181182 private static void checkContentTransferEncoding (final MimePart currentPart , @ NotNull final ParsedMimeMessageComponents parsedComponents ) {
182183 if (parsedComponents .contentTransferEncoding == null ) {
183- for (final Header header : retrieveAllHeaders (currentPart )) {
184+ for (final DecodedHeader header : retrieveAllHeaders (currentPart )) {
184185 if (isEmailHeader (header , "Content-Transfer-Encoding" )) {
185186 parsedComponents .contentTransferEncoding = header .getValue ();
186187 }
@@ -198,24 +199,27 @@ private static MimeDataSource parseAttachment(@Nullable final String contentId,
198199 }
199200
200201 @ SuppressWarnings ("StatementWithEmptyBody" )
201- private static void parseHeader (final Header header , @ NotNull final ParsedMimeMessageComponents parsedComponents ) {
202+ private static void parseHeader (final DecodedHeader header , @ NotNull final ParsedMimeMessageComponents parsedComponents ) {
203+ val headerValue = decodeText (header .getValue ());
204+ val headerName = decodeText (header .getName ());
205+
202206 if (isEmailHeader (header , "Disposition-Notification-To" )) {
203- parsedComponents .dispositionNotificationTo = createAddress (header . getValue () , "Disposition-Notification-To" );
207+ parsedComponents .dispositionNotificationTo = createAddress (headerValue , "Disposition-Notification-To" );
204208 } else if (isEmailHeader (header , "Return-Receipt-To" )) {
205- parsedComponents .returnReceiptTo = createAddress (header . getValue () , "Return-Receipt-To" );
209+ parsedComponents .returnReceiptTo = createAddress (headerValue , "Return-Receipt-To" );
206210 } else if (isEmailHeader (header , "Return-Path" )) {
207- parsedComponents .bounceToAddress = createAddress (header . getValue () , "Return-Path" );
208- } else if (!HEADERS_TO_IGNORE .contains (header . getName () )) {
209- if (!parsedComponents .headers .containsKey (header . getName () )) {
210- parsedComponents .headers .put (header . getName () , new ArrayList <>());
211+ parsedComponents .bounceToAddress = createAddress (headerValue , "Return-Path" );
212+ } else if (!HEADERS_TO_IGNORE .contains (headerName )) {
213+ if (!parsedComponents .headers .containsKey (headerName )) {
214+ parsedComponents .headers .put (headerName , new ArrayList <>());
211215 }
212- parsedComponents .headers .get (header . getName ()) .add (MimeUtility .unfold (header . getValue () ));
216+ parsedComponents .headers .get (headerName ) .add (MimeUtility .unfold (headerValue ));
213217 } else {
214218 // header recognized, but not relevant (see #HEADERS_TO_IGNORE)
215219 }
216220 }
217221
218- private static boolean isEmailHeader (Header header , String emailHeaderName ) {
222+ private static boolean isEmailHeader (DecodedHeader header , String emailHeaderName ) {
219223 return header .getName ().equals (emailHeaderName ) &&
220224 !valueNullOrEmpty (header .getValue ()) &&
221225 !valueNullOrEmpty (header .getValue ().trim ()) &&
@@ -226,7 +230,7 @@ private static boolean isEmailHeader(Header header, String emailHeaderName) {
226230 public static String parseFileName (@ NotNull final Part currentPart ) {
227231 try {
228232 if (currentPart .getFileName () != null ) {
229- return currentPart .getFileName ();
233+ return decodeText ( currentPart .getFileName () );
230234 } else {
231235 // replicate behavior from Thunderbird
232236 if (Arrays .asList (currentPart .getHeader ("Content-Type" )).contains ("message/rfc822" )) {
@@ -276,7 +280,9 @@ public static String parseCalendarMethod(@NotNull MimePart currentPart) {
276280 @ Nullable
277281 public static String parseContentID (@ NotNull final MimePart currentPart ) {
278282 try {
279- return currentPart .getContentID ();
283+ return ofNullable (currentPart .getContentID ())
284+ .map (MimeMessageParser ::decodeText )
285+ .orElse (null );
280286 } catch (final MessagingException e ) {
281287 throw new MimeMessageParseException (MimeMessageParseException .ERROR_GETTING_CONTENT_ID , e );
282288 }
@@ -336,9 +342,11 @@ private static String parseResourceName(@Nullable String possibleWrappedContentI
336342
337343 @ SuppressWarnings ("WeakerAccess" )
338344 @ NotNull
339- public static List <Header > retrieveAllHeaders (@ NotNull final MimePart part ) {
345+ public static List <DecodedHeader > retrieveAllHeaders (@ NotNull final MimePart part ) {
340346 try {
341- return Collections .list (part .getAllHeaders ());
347+ return Collections .list (part .getAllHeaders ()).stream ()
348+ .map (DecodedHeader ::of )
349+ .collect (toList ());
342350 } catch (final MessagingException e ) {
343351 throw new MimeMessageParseException (MimeMessageParseException .ERROR_GETTING_ALL_HEADERS , e );
344352 }
@@ -430,15 +438,6 @@ private static String parseDataSourceName(@NotNull final Part part, @NotNull fin
430438 return !valueNullOrEmpty (result ) ? decodeText (result ) : null ;
431439 }
432440
433- @ NotNull
434- private static String decodeText (@ NotNull final String result ) {
435- try {
436- return MimeUtility .decodeText (result );
437- } catch (final UnsupportedEncodingException e ) {
438- throw new MimeMessageParseException (MimeMessageParseException .ERROR_DECODING_TEXT , e );
439- }
440- }
441-
442441 @ NotNull
443442 private static byte [] readContent (@ NotNull final InputStream is ) {
444443 try {
@@ -472,7 +471,9 @@ public static Address[] retrieveRecipients(@NotNull final MimeMessage mimeMessag
472471 try {
473472 // return mimeMessage.getRecipients(recipientType); // can fail in strict mode, see https://github.com/bbottema/simple-java-mail/issues/227
474473 // workaround following (copied and modified from JavaMail internal code):
475- String s = mimeMessage .getHeader (getHeaderName (recipientType ), "," );
474+ val s = ofNullable (mimeMessage .getHeader (getHeaderName (recipientType ), "," ))
475+ .map (MimeMessageParser ::decodeText )
476+ .orElse (null );
476477 return (s == null ) ? null : InternetAddress .parseHeader (s , false );
477478 } catch (final MessagingException e ) {
478479 throw new MimeMessageParseException (format (MimeMessageParseException .ERROR_GETTING_RECIPIENTS , recipientType ), e );
@@ -492,20 +493,33 @@ private static String getHeaderName(RecipientType recipientType) {
492493 @ Nullable
493494 public static String parseContentDescription (@ NotNull final MimePart mimePart ) {
494495 try {
495- return mimePart .getHeader ("Content-Description" , "," );
496+ return ofNullable (mimePart .getHeader ("Content-Description" , "," ))
497+ .map (MimeMessageParser ::decodeText )
498+ .orElse (null );
496499 } catch (final MessagingException e ) {
497500 throw new MimeMessageParseException (MimeMessageParseException .ERROR_GETTING_CONTENT_DESCRIPTION , e );
498501 }
499502 }
500503 @ Nullable
501504 public static String parseContentTransferEncoding (@ NotNull final MimePart mimePart ) {
502505 try {
503- return mimePart .getHeader ("Content-Transfer-Encoding" , "," );
506+ return ofNullable (mimePart .getHeader ("Content-Transfer-Encoding" , "," ))
507+ .map (MimeMessageParser ::decodeText )
508+ .orElse (null );
504509 } catch (final MessagingException e ) {
505510 throw new MimeMessageParseException (MimeMessageParseException .ERROR_GETTING_CONTENT_TRANSFER_ENCODING , e );
506511 }
507512 }
508513
514+ @ NotNull
515+ static String decodeText (@ NotNull final String result ) {
516+ try {
517+ return MimeUtility .decodeText (result );
518+ } catch (final UnsupportedEncodingException e ) {
519+ throw new MimeMessageParseException (MimeMessageParseException .ERROR_DECODING_TEXT , e );
520+ }
521+ }
522+
509523 @ NotNull
510524 private static List <InternetAddress > parseInternetAddresses (@ Nullable final Address [] recipients ) {
511525 final List <Address > addresses = (recipients != null ) ? Arrays .asList (recipients ) : new ArrayList <>();
0 commit comments