Skip to content

Commit c79dd60

Browse files
author
Benny Bottema
committed
#179: Convert all embedded / INLINE resources to ATTACHMENT resources if the cid does not occur in the HTML
1 parent f2cb4c1 commit c79dd60

File tree

1 file changed

+65
-53
lines changed

1 file changed

+65
-53
lines changed

src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.TreeMap;
3535

3636
import static java.lang.String.format;
37+
import static org.simplejavamail.internal.util.MiscUtil.extractCID;
3738
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
3839

3940
/**
@@ -42,7 +43,7 @@
4243
* @version current: MimeMessageParser.java 2016-02-25 Benny Bottema
4344
*/
4445
public final class MimeMessageParser {
45-
46+
4647
/**
4748
* Contains the headers we will ignore, because either we set the information differently (such as Subject) or we recognize the header as
4849
* interfering or obsolete for new emails).
@@ -107,16 +108,17 @@ public static ParsedMimeMessageComponents parseMimeMessage(@Nonnull final MimeMe
107108
parsedComponents.fromAddress = parseFromAddress(mimeMessage);
108109
parsedComponents.replyToAddresses = parseReplyToAddresses(mimeMessage);
109110
parseMimePartTree(mimeMessage, parsedComponents);
111+
moveInvalidEmbeddedResourcesToAttachments(parsedComponents);
110112
return parsedComponents;
111113
}
112-
114+
113115
private static void parseMimePartTree(@Nonnull final MimePart currentPart, @Nonnull final ParsedMimeMessageComponents parsedComponents) {
114116
for (final Header header : retrieveAllHeaders(currentPart)) {
115117
parseHeader(header, parsedComponents);
116118
}
117-
119+
118120
final String disposition = parseDisposition(currentPart);
119-
121+
120122
if (isMimeType(currentPart, "text/plain") && parsedComponents.plainContent == null && !Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
121123
parsedComponents.plainContent = parseContent(currentPart);
122124
} else if (isMimeType(currentPart, "text/html") && parsedComponents.htmlContent == null && !Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
@@ -128,10 +130,10 @@ private static void parseMimePartTree(@Nonnull final MimePart currentPart, @Nonn
128130
}
129131
} else {
130132
final DataSource ds = createDataSource(currentPart);
131-
// If the diposition is not provided, the part should be treated as attachment
132-
if (disposition == null || Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
133+
// if the diposition is not provided, for now the part should be treated as inline (later non-embedded inline attachments are moved)
134+
if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
133135
parsedComponents.attachmentList.put(parseResourceName(parseContentID(currentPart), parseFileName(currentPart)), ds);
134-
} else if (Part.INLINE.equalsIgnoreCase(disposition)) {
136+
} else if (disposition == null || Part.INLINE.equalsIgnoreCase(disposition)) {
135137
if (parseContentID(currentPart) != null) {
136138
parsedComponents.cidMap.put(parseContentID(currentPart), ds);
137139
} else {
@@ -143,7 +145,7 @@ private static void parseMimePartTree(@Nonnull final MimePart currentPart, @Nonn
143145
}
144146
}
145147
}
146-
148+
147149
@SuppressWarnings("StatementWithEmptyBody")
148150
private static void parseHeader(final Header header, @Nonnull final ParsedMimeMessageComponents parsedComponents) {
149151
if (header.getName().equals("Disposition-Notification-To")) {
@@ -158,7 +160,7 @@ private static void parseHeader(final Header header, @Nonnull final ParsedMimeMe
158160
// header recognized, but not relevant (see #HEADERS_TO_IGNORE)
159161
}
160162
}
161-
163+
162164
@SuppressWarnings("WeakerAccess")
163165
public static String parseFileName(@Nonnull final Part currentPart) {
164166
try {
@@ -167,7 +169,7 @@ public static String parseFileName(@Nonnull final Part currentPart) {
167169
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_FILENAME, e);
168170
}
169171
}
170-
172+
171173
@SuppressWarnings("WeakerAccess")
172174
@Nullable
173175
public static String parseContentID(@Nonnull final MimePart currentPart) {
@@ -177,7 +179,7 @@ public static String parseContentID(@Nonnull final MimePart currentPart) {
177179
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_CONTENT_ID, e);
178180
}
179181
}
180-
182+
181183
@SuppressWarnings("WeakerAccess")
182184
public static MimeBodyPart getBodyPartAtIndex(final Multipart parentMultiPart, final int index) {
183185
try {
@@ -186,7 +188,7 @@ public static MimeBodyPart getBodyPartAtIndex(final Multipart parentMultiPart, f
186188
throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_GETTING_BODYPART_AT_INDEX, index), e);
187189
}
188190
}
189-
191+
190192
@SuppressWarnings("WeakerAccess")
191193
public static int countBodyParts(final Multipart mp) {
192194
try {
@@ -195,7 +197,7 @@ public static int countBodyParts(final Multipart mp) {
195197
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_MULTIPART_COUNT, e);
196198
}
197199
}
198-
200+
199201
@SuppressWarnings("WeakerAccess")
200202
public static <T> T parseContent(@Nonnull final MimePart currentPart) {
201203
try {
@@ -205,7 +207,7 @@ public static <T> T parseContent(@Nonnull final MimePart currentPart) {
205207
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_CONTENT, e);
206208
}
207209
}
208-
210+
209211
@SuppressWarnings("WeakerAccess")
210212
public static String parseDisposition(@Nonnull final MimePart currentPart) {
211213
try {
@@ -214,7 +216,7 @@ public static String parseDisposition(@Nonnull final MimePart currentPart) {
214216
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_DISPOSITION, e);
215217
}
216218
}
217-
219+
218220
@Nonnull
219221
private static String parseResourceName(@Nullable final String possibleWrappedContentID, @Nonnull final String fileName) {
220222
if (!valueNullOrEmpty(possibleWrappedContentID)) {
@@ -228,7 +230,7 @@ private static String parseResourceName(@Nullable final String possibleWrappedCo
228230
return fileName;
229231
}
230232
}
231-
233+
232234
@SuppressWarnings("WeakerAccess")
233235
@Nonnull
234236
public static List<Header> retrieveAllHeaders(@Nonnull final MimePart part) {
@@ -238,7 +240,7 @@ public static List<Header> retrieveAllHeaders(@Nonnull final MimePart part) {
238240
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_ALL_HEADERS, e);
239241
}
240242
}
241-
243+
242244
@Nonnull
243245
private static InternetAddress createAddress(final Header header, final String typeOfAddress) {
244246
try {
@@ -247,7 +249,7 @@ private static InternetAddress createAddress(final Header header, final String t
247249
throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_PARSING_ADDRESS, typeOfAddress), e);
248250
}
249251
}
250-
252+
251253
/**
252254
* Checks whether the MimePart contains an object of the given mime type.
253255
*
@@ -267,7 +269,7 @@ public static boolean isMimeType(@Nonnull final MimePart part, @Nonnull final St
267269
return retrieveContentType(part).equalsIgnoreCase(mimeType);
268270
}
269271
}
270-
272+
271273
@SuppressWarnings("WeakerAccess")
272274
public static String retrieveContentType(@Nonnull final MimePart part) {
273275
try {
@@ -276,7 +278,7 @@ public static String retrieveContentType(@Nonnull final MimePart part) {
276278
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_CONTENT_TYPE, e);
277279
}
278280
}
279-
281+
280282
@SuppressWarnings("WeakerAccess")
281283
public static DataHandler retrieveDataHandler(@Nonnull final MimePart part) {
282284
try {
@@ -285,7 +287,7 @@ public static DataHandler retrieveDataHandler(@Nonnull final MimePart part) {
285287
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_DATAHANDLER, e);
286288
}
287289
}
288-
290+
289291
/**
290292
* Parses the MimePart to create a DataSource.
291293
*
@@ -304,7 +306,7 @@ private static DataSource createDataSource(@Nonnull final MimePart part) {
304306
result.setName(dataSourceName);
305307
return result;
306308
}
307-
309+
308310
@SuppressWarnings("WeakerAccess")
309311
public static InputStream retrieveInputStream(final DataSource dataSource) {
310312
try {
@@ -313,13 +315,13 @@ public static InputStream retrieveInputStream(final DataSource dataSource) {
313315
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_INPUTSTREAM, e);
314316
}
315317
}
316-
318+
317319
@Nullable
318320
private static String parseDataSourceName(@Nonnull final Part part, @Nonnull final DataSource dataSource) {
319321
final String result = !valueNullOrEmpty(dataSource.getName()) ? dataSource.getName() : parseFileName(part);
320322
return !valueNullOrEmpty(result) ? decodeText(result) : null;
321323
}
322-
324+
323325
@Nonnull
324326
private static String decodeText(@Nonnull final String result) {
325327
try {
@@ -328,13 +330,13 @@ private static String decodeText(@Nonnull final String result) {
328330
throw new MimeMessageParseException(MimeMessageParseException.ERROR_DECODING_TEXT, e);
329331
}
330332
}
331-
333+
332334
@Nonnull
333335
private static byte[] readContent(@Nonnull final InputStream is) {
334336
final BufferedInputStream isReader = new BufferedInputStream(is);
335337
final ByteArrayOutputStream os = new ByteArrayOutputStream();
336338
final BufferedOutputStream osWriter = new BufferedOutputStream(os);
337-
339+
338340
int ch;
339341
try {
340342
while ((ch = isReader.read()) != -1) {
@@ -361,26 +363,26 @@ private static String parseBaseMimeType(@Nonnull final String fullMimeType) {
361363
}
362364
return fullMimeType;
363365
}
364-
365-
366+
367+
366368
@SuppressWarnings("WeakerAccess")
367369
@Nonnull
368370
public static List<InternetAddress> parseToAddresses(@Nonnull final MimeMessage mimeMessage) {
369371
return parseInternetAddresses(retrieveRecipients(mimeMessage, RecipientType.TO));
370372
}
371-
373+
372374
@SuppressWarnings("WeakerAccess")
373375
@Nonnull
374376
public static List<InternetAddress> parseCcAddresses(@Nonnull final MimeMessage mimeMessage) {
375377
return parseInternetAddresses(retrieveRecipients(mimeMessage, RecipientType.CC));
376378
}
377-
379+
378380
@SuppressWarnings("WeakerAccess")
379381
@Nonnull
380382
public static List<InternetAddress> parseBccAddresses(@Nonnull final MimeMessage mimeMessage) {
381383
return parseInternetAddresses(retrieveRecipients(mimeMessage, RecipientType.BCC));
382384
}
383-
385+
384386
@SuppressWarnings("WeakerAccess")
385387
@Nullable
386388
public static Address[] retrieveRecipients(@Nonnull final MimeMessage mimeMessage, final RecipientType recipientType) {
@@ -390,7 +392,7 @@ public static Address[] retrieveRecipients(@Nonnull final MimeMessage mimeMessag
390392
throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_GETTING_RECIPIENTS, recipientType), e);
391393
}
392394
}
393-
395+
394396
@Nonnull
395397
private static List<InternetAddress> parseInternetAddresses(@Nullable final Address[] recipients) {
396398
final List<Address> addresses = (recipients != null) ? Arrays.asList(recipients) : new ArrayList<Address>();
@@ -402,7 +404,7 @@ private static List<InternetAddress> parseInternetAddresses(@Nullable final Addr
402404
}
403405
return mailAddresses;
404406
}
405-
407+
406408
@SuppressWarnings("WeakerAccess")
407409
@Nullable
408410
public static InternetAddress parseFromAddress(@Nonnull final MimeMessage mimeMessage) {
@@ -413,7 +415,7 @@ public static InternetAddress parseFromAddress(@Nonnull final MimeMessage mimeMe
413415
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_FROMADDRESS, e);
414416
}
415417
}
416-
418+
417419
@SuppressWarnings("WeakerAccess")
418420
@Nullable
419421
public static InternetAddress parseReplyToAddresses(@Nonnull final MimeMessage mimeMessage) {
@@ -424,7 +426,7 @@ public static InternetAddress parseReplyToAddresses(@Nonnull final MimeMessage m
424426
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_REPLY_TO_ADDRESSES, e);
425427
}
426428
}
427-
429+
428430
@Nullable
429431
public static String parseSubject(@Nonnull final MimeMessage mimeMessage) {
430432
try {
@@ -433,8 +435,8 @@ public static String parseSubject(@Nonnull final MimeMessage mimeMessage) {
433435
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_SUBJECT, e);
434436
}
435437
}
436-
437-
438+
439+
438440
@SuppressWarnings("WeakerAccess")
439441
@Nullable
440442
public static String parseMessageId(@Nonnull final MimeMessage mimeMessage) {
@@ -444,7 +446,17 @@ public static String parseMessageId(@Nonnull final MimeMessage mimeMessage) {
444446
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_MESSAGE_ID, e);
445447
}
446448
}
447-
449+
450+
private static void moveInvalidEmbeddedResourcesToAttachments(ParsedMimeMessageComponents parsedComponents) {
451+
final String htmlContent = parsedComponents.htmlContent.toString();
452+
for (Map.Entry<String, DataSource> cidEntry : parsedComponents.cidMap.entrySet()) {
453+
if (!htmlContent.contains("cid:" + extractCID(cidEntry.getKey()))) {
454+
parsedComponents.attachmentList.put(cidEntry.getKey(), cidEntry.getValue());
455+
parsedComponents.cidMap.remove(cidEntry.getKey());
456+
}
457+
}
458+
}
459+
448460
public static class ParsedMimeMessageComponents {
449461
private final Map<String, DataSource> attachmentList = new TreeMap<>();
450462
private final Map<String, DataSource> cidMap = new TreeMap<>();
@@ -461,63 +473,63 @@ public static class ParsedMimeMessageComponents {
461473
private InternetAddress bounceToAddress;
462474
private String plainContent;
463475
private String htmlContent;
464-
476+
465477
public String getMessageId() {
466478
return messageId;
467479
}
468-
480+
469481
public Map<String, DataSource> getAttachmentList() {
470482
return attachmentList;
471483
}
472-
484+
473485
public Map<String, DataSource> getCidMap() {
474486
return cidMap;
475487
}
476-
488+
477489
public Map<String, Object> getHeaders() {
478490
return headers;
479491
}
480-
492+
481493
public List<InternetAddress> getToAddresses() {
482494
return toAddresses;
483495
}
484-
496+
485497
public List<InternetAddress> getCcAddresses() {
486498
return ccAddresses;
487499
}
488-
500+
489501
public List<InternetAddress> getBccAddresses() {
490502
return bccAddresses;
491503
}
492-
504+
493505
public String getSubject() {
494506
return subject;
495507
}
496-
508+
497509
public InternetAddress getFromAddress() {
498510
return fromAddress;
499511
}
500-
512+
501513
public InternetAddress getReplyToAddresses() {
502514
return replyToAddresses;
503515
}
504-
516+
505517
public InternetAddress getDispositionNotificationTo() {
506518
return dispositionNotificationTo;
507519
}
508-
520+
509521
public InternetAddress getReturnReceiptTo() {
510522
return returnReceiptTo;
511523
}
512-
524+
513525
public InternetAddress getBounceToAddress() {
514526
return bounceToAddress;
515527
}
516-
528+
517529
public String getPlainContent() {
518530
return plainContent;
519531
}
520-
532+
521533
public String getHtmlContent() {
522534
return htmlContent;
523535
}

0 commit comments

Comments
 (0)