Skip to content

Commit

Permalink
#179: Convert all embedded / INLINE resources to ATTACHMENT resources…
Browse files Browse the repository at this point in the history
… if the cid does not occur in the HTML
  • Loading branch information
Benny Bottema committed Nov 7, 2018
1 parent f2cb4c1 commit c79dd60
Showing 1 changed file with 65 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.TreeMap;

import static java.lang.String.format;
import static org.simplejavamail.internal.util.MiscUtil.extractCID;
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;

/**
Expand All @@ -42,7 +43,7 @@
* @version current: MimeMessageParser.java 2016-02-25 Benny Bottema
*/
public final class MimeMessageParser {

/**
* Contains the headers we will ignore, because either we set the information differently (such as Subject) or we recognize the header as
* interfering or obsolete for new emails).
Expand Down Expand Up @@ -107,16 +108,17 @@ public static ParsedMimeMessageComponents parseMimeMessage(@Nonnull final MimeMe
parsedComponents.fromAddress = parseFromAddress(mimeMessage);
parsedComponents.replyToAddresses = parseReplyToAddresses(mimeMessage);
parseMimePartTree(mimeMessage, parsedComponents);
moveInvalidEmbeddedResourcesToAttachments(parsedComponents);
return parsedComponents;
}

private static void parseMimePartTree(@Nonnull final MimePart currentPart, @Nonnull final ParsedMimeMessageComponents parsedComponents) {
for (final Header header : retrieveAllHeaders(currentPart)) {
parseHeader(header, parsedComponents);
}

final String disposition = parseDisposition(currentPart);

if (isMimeType(currentPart, "text/plain") && parsedComponents.plainContent == null && !Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
parsedComponents.plainContent = parseContent(currentPart);
} else if (isMimeType(currentPart, "text/html") && parsedComponents.htmlContent == null && !Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
Expand All @@ -128,10 +130,10 @@ private static void parseMimePartTree(@Nonnull final MimePart currentPart, @Nonn
}
} else {
final DataSource ds = createDataSource(currentPart);
// If the diposition is not provided, the part should be treated as attachment
if (disposition == null || Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
// if the diposition is not provided, for now the part should be treated as inline (later non-embedded inline attachments are moved)
if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
parsedComponents.attachmentList.put(parseResourceName(parseContentID(currentPart), parseFileName(currentPart)), ds);
} else if (Part.INLINE.equalsIgnoreCase(disposition)) {
} else if (disposition == null || Part.INLINE.equalsIgnoreCase(disposition)) {
if (parseContentID(currentPart) != null) {
parsedComponents.cidMap.put(parseContentID(currentPart), ds);
} else {
Expand All @@ -143,7 +145,7 @@ private static void parseMimePartTree(@Nonnull final MimePart currentPart, @Nonn
}
}
}

@SuppressWarnings("StatementWithEmptyBody")
private static void parseHeader(final Header header, @Nonnull final ParsedMimeMessageComponents parsedComponents) {
if (header.getName().equals("Disposition-Notification-To")) {
Expand All @@ -158,7 +160,7 @@ private static void parseHeader(final Header header, @Nonnull final ParsedMimeMe
// header recognized, but not relevant (see #HEADERS_TO_IGNORE)
}
}

@SuppressWarnings("WeakerAccess")
public static String parseFileName(@Nonnull final Part currentPart) {
try {
Expand All @@ -167,7 +169,7 @@ public static String parseFileName(@Nonnull final Part currentPart) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_FILENAME, e);
}
}

@SuppressWarnings("WeakerAccess")
@Nullable
public static String parseContentID(@Nonnull final MimePart currentPart) {
Expand All @@ -177,7 +179,7 @@ public static String parseContentID(@Nonnull final MimePart currentPart) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_CONTENT_ID, e);
}
}

@SuppressWarnings("WeakerAccess")
public static MimeBodyPart getBodyPartAtIndex(final Multipart parentMultiPart, final int index) {
try {
Expand All @@ -186,7 +188,7 @@ public static MimeBodyPart getBodyPartAtIndex(final Multipart parentMultiPart, f
throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_GETTING_BODYPART_AT_INDEX, index), e);
}
}

@SuppressWarnings("WeakerAccess")
public static int countBodyParts(final Multipart mp) {
try {
Expand All @@ -195,7 +197,7 @@ public static int countBodyParts(final Multipart mp) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_MULTIPART_COUNT, e);
}
}

@SuppressWarnings("WeakerAccess")
public static <T> T parseContent(@Nonnull final MimePart currentPart) {
try {
Expand All @@ -205,7 +207,7 @@ public static <T> T parseContent(@Nonnull final MimePart currentPart) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_CONTENT, e);
}
}

@SuppressWarnings("WeakerAccess")
public static String parseDisposition(@Nonnull final MimePart currentPart) {
try {
Expand All @@ -214,7 +216,7 @@ public static String parseDisposition(@Nonnull final MimePart currentPart) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_DISPOSITION, e);
}
}

@Nonnull
private static String parseResourceName(@Nullable final String possibleWrappedContentID, @Nonnull final String fileName) {
if (!valueNullOrEmpty(possibleWrappedContentID)) {
Expand All @@ -228,7 +230,7 @@ private static String parseResourceName(@Nullable final String possibleWrappedCo
return fileName;
}
}

@SuppressWarnings("WeakerAccess")
@Nonnull
public static List<Header> retrieveAllHeaders(@Nonnull final MimePart part) {
Expand All @@ -238,7 +240,7 @@ public static List<Header> retrieveAllHeaders(@Nonnull final MimePart part) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_ALL_HEADERS, e);
}
}

@Nonnull
private static InternetAddress createAddress(final Header header, final String typeOfAddress) {
try {
Expand All @@ -247,7 +249,7 @@ private static InternetAddress createAddress(final Header header, final String t
throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_PARSING_ADDRESS, typeOfAddress), e);
}
}

/**
* Checks whether the MimePart contains an object of the given mime type.
*
Expand All @@ -267,7 +269,7 @@ public static boolean isMimeType(@Nonnull final MimePart part, @Nonnull final St
return retrieveContentType(part).equalsIgnoreCase(mimeType);
}
}

@SuppressWarnings("WeakerAccess")
public static String retrieveContentType(@Nonnull final MimePart part) {
try {
Expand All @@ -276,7 +278,7 @@ public static String retrieveContentType(@Nonnull final MimePart part) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_CONTENT_TYPE, e);
}
}

@SuppressWarnings("WeakerAccess")
public static DataHandler retrieveDataHandler(@Nonnull final MimePart part) {
try {
Expand All @@ -285,7 +287,7 @@ public static DataHandler retrieveDataHandler(@Nonnull final MimePart part) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_DATAHANDLER, e);
}
}

/**
* Parses the MimePart to create a DataSource.
*
Expand All @@ -304,7 +306,7 @@ private static DataSource createDataSource(@Nonnull final MimePart part) {
result.setName(dataSourceName);
return result;
}

@SuppressWarnings("WeakerAccess")
public static InputStream retrieveInputStream(final DataSource dataSource) {
try {
Expand All @@ -313,13 +315,13 @@ public static InputStream retrieveInputStream(final DataSource dataSource) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_INPUTSTREAM, e);
}
}

@Nullable
private static String parseDataSourceName(@Nonnull final Part part, @Nonnull final DataSource dataSource) {
final String result = !valueNullOrEmpty(dataSource.getName()) ? dataSource.getName() : parseFileName(part);
return !valueNullOrEmpty(result) ? decodeText(result) : null;
}

@Nonnull
private static String decodeText(@Nonnull final String result) {
try {
Expand All @@ -328,13 +330,13 @@ private static String decodeText(@Nonnull final String result) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_DECODING_TEXT, e);
}
}

@Nonnull
private static byte[] readContent(@Nonnull final InputStream is) {
final BufferedInputStream isReader = new BufferedInputStream(is);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
final BufferedOutputStream osWriter = new BufferedOutputStream(os);

int ch;
try {
while ((ch = isReader.read()) != -1) {
Expand All @@ -361,26 +363,26 @@ private static String parseBaseMimeType(@Nonnull final String fullMimeType) {
}
return fullMimeType;
}


@SuppressWarnings("WeakerAccess")
@Nonnull
public static List<InternetAddress> parseToAddresses(@Nonnull final MimeMessage mimeMessage) {
return parseInternetAddresses(retrieveRecipients(mimeMessage, RecipientType.TO));
}

@SuppressWarnings("WeakerAccess")
@Nonnull
public static List<InternetAddress> parseCcAddresses(@Nonnull final MimeMessage mimeMessage) {
return parseInternetAddresses(retrieveRecipients(mimeMessage, RecipientType.CC));
}

@SuppressWarnings("WeakerAccess")
@Nonnull
public static List<InternetAddress> parseBccAddresses(@Nonnull final MimeMessage mimeMessage) {
return parseInternetAddresses(retrieveRecipients(mimeMessage, RecipientType.BCC));
}

@SuppressWarnings("WeakerAccess")
@Nullable
public static Address[] retrieveRecipients(@Nonnull final MimeMessage mimeMessage, final RecipientType recipientType) {
Expand All @@ -390,7 +392,7 @@ public static Address[] retrieveRecipients(@Nonnull final MimeMessage mimeMessag
throw new MimeMessageParseException(format(MimeMessageParseException.ERROR_GETTING_RECIPIENTS, recipientType), e);
}
}

@Nonnull
private static List<InternetAddress> parseInternetAddresses(@Nullable final Address[] recipients) {
final List<Address> addresses = (recipients != null) ? Arrays.asList(recipients) : new ArrayList<Address>();
Expand All @@ -402,7 +404,7 @@ private static List<InternetAddress> parseInternetAddresses(@Nullable final Addr
}
return mailAddresses;
}

@SuppressWarnings("WeakerAccess")
@Nullable
public static InternetAddress parseFromAddress(@Nonnull final MimeMessage mimeMessage) {
Expand All @@ -413,7 +415,7 @@ public static InternetAddress parseFromAddress(@Nonnull final MimeMessage mimeMe
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_FROMADDRESS, e);
}
}

@SuppressWarnings("WeakerAccess")
@Nullable
public static InternetAddress parseReplyToAddresses(@Nonnull final MimeMessage mimeMessage) {
Expand All @@ -424,7 +426,7 @@ public static InternetAddress parseReplyToAddresses(@Nonnull final MimeMessage m
throw new MimeMessageParseException(MimeMessageParseException.ERROR_PARSING_REPLY_TO_ADDRESSES, e);
}
}

@Nullable
public static String parseSubject(@Nonnull final MimeMessage mimeMessage) {
try {
Expand All @@ -433,8 +435,8 @@ public static String parseSubject(@Nonnull final MimeMessage mimeMessage) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_SUBJECT, e);
}
}


@SuppressWarnings("WeakerAccess")
@Nullable
public static String parseMessageId(@Nonnull final MimeMessage mimeMessage) {
Expand All @@ -444,7 +446,17 @@ public static String parseMessageId(@Nonnull final MimeMessage mimeMessage) {
throw new MimeMessageParseException(MimeMessageParseException.ERROR_GETTING_MESSAGE_ID, e);
}
}


private static void moveInvalidEmbeddedResourcesToAttachments(ParsedMimeMessageComponents parsedComponents) {
final String htmlContent = parsedComponents.htmlContent.toString();
for (Map.Entry<String, DataSource> cidEntry : parsedComponents.cidMap.entrySet()) {
if (!htmlContent.contains("cid:" + extractCID(cidEntry.getKey()))) {
parsedComponents.attachmentList.put(cidEntry.getKey(), cidEntry.getValue());
parsedComponents.cidMap.remove(cidEntry.getKey());
}
}
}

public static class ParsedMimeMessageComponents {
private final Map<String, DataSource> attachmentList = new TreeMap<>();
private final Map<String, DataSource> cidMap = new TreeMap<>();
Expand All @@ -461,63 +473,63 @@ public static class ParsedMimeMessageComponents {
private InternetAddress bounceToAddress;
private String plainContent;
private String htmlContent;

public String getMessageId() {
return messageId;
}

public Map<String, DataSource> getAttachmentList() {
return attachmentList;
}

public Map<String, DataSource> getCidMap() {
return cidMap;
}

public Map<String, Object> getHeaders() {
return headers;
}

public List<InternetAddress> getToAddresses() {
return toAddresses;
}

public List<InternetAddress> getCcAddresses() {
return ccAddresses;
}

public List<InternetAddress> getBccAddresses() {
return bccAddresses;
}

public String getSubject() {
return subject;
}

public InternetAddress getFromAddress() {
return fromAddress;
}

public InternetAddress getReplyToAddresses() {
return replyToAddresses;
}

public InternetAddress getDispositionNotificationTo() {
return dispositionNotificationTo;
}

public InternetAddress getReturnReceiptTo() {
return returnReceiptTo;
}

public InternetAddress getBounceToAddress() {
return bounceToAddress;
}

public String getPlainContent() {
return plainContent;
}

public String getHtmlContent() {
return htmlContent;
}
Expand Down

0 comments on commit c79dd60

Please sign in to comment.