Skip to content

Commit

Permalink
#440: Bug: names manually specified for embedded images are overridde…
Browse files Browse the repository at this point in the history
…n and have extension added, breaking cid: references in HTML body - now only fixing file extension in case of attachments, not inline attachments (embedded images)
  • Loading branch information
bbottema committed Oct 9, 2023
1 parent 96e2739 commit d561ff9
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeUtility;
import jakarta.mail.util.ByteArrayDataSource;
import lombok.SneakyThrows;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -22,6 +23,7 @@
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.Charset;
Expand Down Expand Up @@ -442,4 +444,11 @@ private static void addOrOverrideHeaders(HashMap<String, Collection<String>> col
collectedHeaders.get(headerKey).addAll(headerValues);
});
}

@SneakyThrows
public static void assignToInstanceField(Object subject, String fieldName, Object newValue) {
Field field = subject.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(subject, newValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ private static BodyPart getBodyPartFromDatasource(final AttachmentResource attac
throws MessagingException {
final BodyPart attachmentPart = new MimeBodyPart();
// setting headers isn't working nicely using the javax mail API, so let's do that manually
final String resourceName = determineResourceName(attachmentResource, true);
final String fileName = determineResourceName(attachmentResource, false);
final String resourceName = determineResourceName(attachmentResource, dispositionType, true);
final String fileName = determineResourceName(attachmentResource, dispositionType, false);
attachmentPart.setDataHandler(new DataHandler(new NamedDataSource(fileName, attachmentResource.getDataSource())));
attachmentPart.setFileName(fileName);
final String contentType = attachmentResource.getDataSource().getContentType();
Expand All @@ -283,7 +283,7 @@ private static BodyPart getBodyPartFromDatasource(final AttachmentResource attac
/**
* Determines the right resource name and optionally attaches the correct extension to the name. The result is mime encoded.
*/
static String determineResourceName(final AttachmentResource attachmentResource, final boolean encodeResourceName) {
static String determineResourceName(final AttachmentResource attachmentResource, String dispositionType, final boolean encodeResourceName) {
final String datasourceName = attachmentResource.getDataSource().getName();

String resourceName;
Expand All @@ -295,7 +295,7 @@ static String determineResourceName(final AttachmentResource attachmentResource,
} else {
resourceName = "resource" + UUID.randomUUID();
}
if (!valueNullOrEmpty(datasourceName)) {
if (!valueNullOrEmpty(datasourceName) && dispositionType.equals(Part.ATTACHMENT)) {
resourceName = possiblyAddExtension(datasourceName, resourceName);
}
return encodeResourceName ? MiscUtil.encodeText(resourceName) : resourceName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.simplejavamail.converter;

import jakarta.mail.util.ByteArrayDataSource;
import org.apache.commons.codec.binary.Base64;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.simplejavamail.api.email.AttachmentResource;
Expand All @@ -23,7 +22,9 @@
import static jakarta.mail.Message.RecipientType.CC;
import static jakarta.mail.Message.RecipientType.TO;
import static java.nio.charset.Charset.defaultCharset;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonList;
import static org.apache.commons.codec.binary.Base64.encodeBase64Chunked;
import static org.assertj.core.api.Assertions.assertThat;
import static org.simplejavamail.api.email.ContentTransferEncoding.BIT7;
import static org.simplejavamail.internal.util.MiscUtil.normalizeNewlines;
Expand Down Expand Up @@ -194,19 +195,26 @@ public void testContentTransferEncodingBase64() {
final String eml = normalizeNewlines(EmailConverter.emailToEML(email));
final String emlRoundtrip = normalizeNewlines(EmailConverter.emailToEML(EmailConverter.emlToEmail(EmailConverter.emailToEML(email))));

assertThat(eml).contains("Content-Transfer-Encoding: base64\n"
+ "\n"
+ new String(Base64.encodeBase64("We should meet up!".getBytes())));
assertThat(eml).contains("Content-Transfer-Encoding: base64\n"
+ "\n"
+ new String(Base64.encodeBase64("<b>We should meet up!</b><img src='cid:thumbsup'>".getBytes())));
assertThat(eml).contains("Content-Transfer-Encoding: base64\n\n"
+ asBase64("We should meet up!"));
assertThat(eml).contains("Content-Transfer-Encoding: base64\n\n"
+ asBase64("<b>We should meet up!</b><img src='cid:thumbsup'><img src='cid:fixedNameWithoutFileExtensionForNamedEmbeddedImage'>"));

assertThat(emlRoundtrip).contains("Content-Transfer-Encoding: base64\n\n"
+ asBase64("We should meet up!"));
assertThat(emlRoundtrip).contains("Content-Transfer-Encoding: base64\n\n"
+ asBase64("<b>We should meet up!</b><img src='cid:thumbsup'><img src='cid:fixedNameWithoutFileExtensionForNamedEmbeddedImage'>"));

assertThat(eml).contains("Content-ID: <dresscode.txt>");
assertThat(eml).contains("Content-ID: <location.txt>");
assertThat(eml).contains("Content-ID: <thumbsup>");
assertThat(eml).contains("Content-ID: <fixedNameWithoutFileExtensionForNamedAttachment.txt>");
assertThat(eml).contains("Content-ID: <fixedNameWithoutFileExtensionForNamedEmbeddedImage>");
}

assertThat(emlRoundtrip).contains("Content-Transfer-Encoding: base64\n"
+ "\n"
+ new String(Base64.encodeBase64("We should meet up!".getBytes())));
assertThat(emlRoundtrip).contains("Content-Transfer-Encoding: base64\n"
+ "\n"
+ new String(Base64.encodeBase64("<b>We should meet up!</b><img src='cid:thumbsup'>".getBytes())));
@NotNull
private static String asBase64(String content) {
return normalizeNewlines(new String(encodeBase64Chunked(content.getBytes(UTF_8)), UTF_8));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.mail.BodyPart;
import jakarta.mail.MessagingException;
import jakarta.mail.Part;
import jakarta.mail.internet.ContentType;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
Expand Down Expand Up @@ -34,56 +35,64 @@ public void setup() {
public void determineResourceName1()
throws IOException {
AttachmentResource resource1 = new AttachmentResource(null, getDataSource("blahblah"));
assertThat(MimeMessageHelper.determineResourceName(resource1, true)).isEqualTo("blahblah");
assertThat(MimeMessageHelper.determineResourceName(resource1, Part.ATTACHMENT, true)).isEqualTo("blahblah");
assertThat(MimeMessageHelper.determineResourceName(resource1, Part.INLINE, true)).isEqualTo("blahblah");
}

@Test
public void determineResourceName2()
throws IOException {
AttachmentResource resource2 = new AttachmentResource(null, getDataSource("blahblah.txt"));
assertThat(MimeMessageHelper.determineResourceName(resource2, true)).isEqualTo("blahblah.txt");
assertThat(MimeMessageHelper.determineResourceName(resource2, Part.ATTACHMENT, true)).isEqualTo("blahblah.txt");
assertThat(MimeMessageHelper.determineResourceName(resource2, Part.INLINE, true)).isEqualTo("blahblah.txt");
}

@Test
public void determineResourceName3()
throws IOException {
AttachmentResource resource3 = new AttachmentResource("the resource", getDataSource(null));
assertThat(MimeMessageHelper.determineResourceName(resource3, true)).isEqualTo("the resource");
assertThat(MimeMessageHelper.determineResourceName(resource3, Part.ATTACHMENT, true)).isEqualTo("the resource");
assertThat(MimeMessageHelper.determineResourceName(resource3, Part.INLINE, true)).isEqualTo("the resource");
}

@Test
public void determineResourceName4()
throws IOException {
AttachmentResource resource4 = new AttachmentResource("the resource", getDataSource("blahblah.txt"));
assertThat(MimeMessageHelper.determineResourceName(resource4, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource4, Part.ATTACHMENT, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource4, Part.INLINE, true)).isEqualTo("the resource");
}

@Test
public void determineResourceName5()
throws IOException {
AttachmentResource resource5 = new AttachmentResource("the resource", getDataSource("blahblah"));
assertThat(MimeMessageHelper.determineResourceName(resource5, true)).isEqualTo("the resource");
assertThat(MimeMessageHelper.determineResourceName(resource5, Part.ATTACHMENT, true)).isEqualTo("the resource");
assertThat(MimeMessageHelper.determineResourceName(resource5, Part.INLINE, true)).isEqualTo("the resource");
}

@Test
public void determineResourceName6()
throws IOException {
AttachmentResource resource6 = new AttachmentResource("the resource.txt", getDataSource("blahblah.txt"));
assertThat(MimeMessageHelper.determineResourceName(resource6, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource6, Part.ATTACHMENT, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource6, Part.INLINE, true)).isEqualTo("the resource.txt");
}

@Test
public void determineResourceName7()
throws IOException {
AttachmentResource resource7 = new AttachmentResource("the resource.txt", getDataSource("blahblah"));
assertThat(MimeMessageHelper.determineResourceName(resource7, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource7, Part.ATTACHMENT, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource7, Part.INLINE, true)).isEqualTo("the resource.txt");
}

@Test
public void determineResourceName_ignoreExtensionFromResource()
throws IOException {
AttachmentResource resource7 = new AttachmentResource("the resource.txt", getDataSource("blahblah.1/www/get?id=3"));
assertThat(MimeMessageHelper.determineResourceName(resource7, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource7, Part.ATTACHMENT, true)).isEqualTo("the resource.txt");
assertThat(MimeMessageHelper.determineResourceName(resource7, Part.INLINE, true)).isEqualTo("the resource.txt");
}

private ByteArrayDataSource getDataSource(@Nullable String name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public void testBasicParsing()
receiveWindowStart.add(Calendar.SECOND, -5);
assertThat(mimeMessageParts.getSentDate()).isBetween(receiveWindowStart.getTime(), new Date());

assertThat(mimeMessageParts.getCidMap()).containsOnlyKeys("<thumbsup>");
assertThat(mimeMessageParts.getAttachmentList()).extracting("name").containsOnly("dresscode.txt", "location.txt");
assertThat(mimeMessageParts.getCidMap()).containsOnlyKeys("<thumbsup>", "<fixedNameWithoutFileExtensionForNamedEmbeddedImage>");
assertThat(mimeMessageParts.getAttachmentList()).extracting("name").containsOnly("dresscode.txt", "location.txt", "fixedNameWithoutFileExtensionForNamedAttachment.txt");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,25 @@ public void setup() {
@Test
public void testBuilderSimpleBuildWithStandardEmail()
throws IOException {
final String base64StringOfThumbsupImage = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABeElEQVRYw2NgoAAYGxu3GxkZ7TY1NZVloDcAWq4MxH+B+D8Qv3FwcOCgtwM6oJaDMTAUXOhmuYqKCjvQ0pdoDrCnmwNMTEwakC0H4u8GBgYC9Ap6DSD+iewAoIPm0ctyLqBlp9F8/x+YE4zpYT8T0LL16JYD8U26+B7oyz4sloPwenpYno3DchCeROsUbwa05A8eB3wB4kqgIxOAuArIng7EW4H4EhC/B+JXQLwDaI4ryZaDSjeg5mt4LCcFXyIn1fdSyXJQVt1OtMWGhoai0OD8T0W8GohZifE1PxD/o7LlsPLiFNAKRrwOABWptLAcqc6QGDAHQEOAYaAc8BNotsJAOgAUAosG1AFA/AtUoY3YEFhKMAvS2AE7iC1+WaG1H6gY3gzE36hUFJ8mqzbU1dUVBBqQBzTgIDQRkWo5qCZdpaenJ0Zx1aytrc0DDB0foIG1oAYKqC0IZK8D4n1AfA6IzwPxXpCFoGoZVEUDaRGGUTAKRgEeAAA2eGJC+ETCiAAAAABJRU5ErkJggg==";
ByteArrayDataSource namedAttachment = new ByteArrayDataSource("Black Tie Optional", "text/plain");
namedAttachment.setName("dresscode-ignored-because-of-override.txt");
String base64String = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABeElEQVRYw2NgoAAYGxu3GxkZ7TY1NZVloDcAWq4MxH+B+D8Qv3FwcOCgtwM6oJaDMTAUXOhmuYqKCjvQ0pdoDrCnmwNMTEwakC0H4u8GBgYC9Ap6DSD+iewAoIPm0ctyLqBlp9F8/x+YE4zpYT8T0LL16JYD8U26+B7oyz4sloPwenpYno3DchCeROsUbwa05A8eB3wB4kqgIxOAuArIng7EW4H4EhC/B+JXQLwDaI4ryZaDSjeg5mt4LCcFXyIn1fdSyXJQVt1OtMWGhoai0OD8T0W8GohZifE1PxD/o7LlsPLiFNAKRrwOABWptLAcqc6QGDAHQEOAYaAc8BNotsJAOgAUAosG1AFA/AtUoY3YEFhKMAvS2AE7iC1+WaG1H6gY3gzE36hUFJ8mqzbU1dUVBBqQBzTgIDQRkWo5qCZdpaenJ0Zx1aytrc0DDB0foIG1oAYKqC0IZK8D4n1AfA6IzwPxXpCFoGoZVEUDaRGGUTAKRgEeAAA2eGJC+ETCiAAAAABJRU5ErkJggg==";
ByteArrayDataSource namedEmbeddedImage = new ByteArrayDataSource(parseBase64Binary(base64StringOfThumbsupImage), "image/png");
namedEmbeddedImage.setName("thumbsupNamed-ignored-because-of-override.png");

fixLoadedPropertyPath(Property.SMIME_ENCRYPTION_CERTIFICATE);

final Email email = EmailBuilder.startingBlank()
.from("lollypop", "lol.pop@somemail.com")
.to("C.Cane", "candycane@candyshop.org")
.withPlainText("We should meet up!")
.withHTMLText("<b>We should meet up!</b><img src='cid:thumbsup'>")
.withHTMLText("<b>We should meet up!</b><img src='cid:thumbsup'><img src='cid:fixedNameWithoutFileExtensionForNamedEmbeddedImage'>")
.withSubject("hey")
.withAttachment("dresscode.txt", namedAttachment)
.withAttachment("location.txt", "On the moon!".getBytes(Charset.defaultCharset()), "text/plain")
.withEmbeddedImage("thumbsup", parseBase64Binary(base64String), "image/png")
.withEmbeddedImage("thumbsup", parseBase64Binary(base64StringOfThumbsupImage), "image/png")
.withAttachment("fixedNameWithoutFileExtensionForNamedAttachment", namedAttachment)
.withEmbeddedImage("fixedNameWithoutFileExtensionForNamedEmbeddedImage", namedEmbeddedImage)
.buildEmail();

assertThat(EmailHelper.createDummyEmailBuilder(true, true, false, false, false, false).buildEmail()).isEqualTo(email);
Expand All @@ -61,21 +65,25 @@ private void fixLoadedPropertyPath(@NotNull final Property property) {
@Test
public void testBuilderSimpleBuildWithStandardEmail_PlusOptionals()
throws IOException {
final String base64StringOfThumbsupImage = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABeElEQVRYw2NgoAAYGxu3GxkZ7TY1NZVloDcAWq4MxH+B+D8Qv3FwcOCgtwM6oJaDMTAUXOhmuYqKCjvQ0pdoDrCnmwNMTEwakC0H4u8GBgYC9Ap6DSD+iewAoIPm0ctyLqBlp9F8/x+YE4zpYT8T0LL16JYD8U26+B7oyz4sloPwenpYno3DchCeROsUbwa05A8eB3wB4kqgIxOAuArIng7EW4H4EhC/B+JXQLwDaI4ryZaDSjeg5mt4LCcFXyIn1fdSyXJQVt1OtMWGhoai0OD8T0W8GohZifE1PxD/o7LlsPLiFNAKRrwOABWptLAcqc6QGDAHQEOAYaAc8BNotsJAOgAUAosG1AFA/AtUoY3YEFhKMAvS2AE7iC1+WaG1H6gY3gzE36hUFJ8mqzbU1dUVBBqQBzTgIDQRkWo5qCZdpaenJ0Zx1aytrc0DDB0foIG1oAYKqC0IZK8D4n1AfA6IzwPxXpCFoGoZVEUDaRGGUTAKRgEeAAA2eGJC+ETCiAAAAABJRU5ErkJggg==";
ByteArrayDataSource namedAttachment = new ByteArrayDataSource("Black Tie Optional", "text/plain");
namedAttachment.setName("dresscode-ignored-because-of-override.txt");
String base64String = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABeElEQVRYw2NgoAAYGxu3GxkZ7TY1NZVloDcAWq4MxH+B+D8Qv3FwcOCgtwM6oJaDMTAUXOhmuYqKCjvQ0pdoDrCnmwNMTEwakC0H4u8GBgYC9Ap6DSD+iewAoIPm0ctyLqBlp9F8/x+YE4zpYT8T0LL16JYD8U26+B7oyz4sloPwenpYno3DchCeROsUbwa05A8eB3wB4kqgIxOAuArIng7EW4H4EhC/B+JXQLwDaI4ryZaDSjeg5mt4LCcFXyIn1fdSyXJQVt1OtMWGhoai0OD8T0W8GohZifE1PxD/o7LlsPLiFNAKRrwOABWptLAcqc6QGDAHQEOAYaAc8BNotsJAOgAUAosG1AFA/AtUoY3YEFhKMAvS2AE7iC1+WaG1H6gY3gzE36hUFJ8mqzbU1dUVBBqQBzTgIDQRkWo5qCZdpaenJ0Zx1aytrc0DDB0foIG1oAYKqC0IZK8D4n1AfA6IzwPxXpCFoGoZVEUDaRGGUTAKRgEeAAA2eGJC+ETCiAAAAABJRU5ErkJggg==";

ByteArrayDataSource namedEmbeddedImage = new ByteArrayDataSource(parseBase64Binary(base64StringOfThumbsupImage), "image/png");
namedEmbeddedImage.setName("thumbsupNamed-ignored-because-of-override.png");

final Email email = EmailBuilder.startingBlank()
.from("lollypop", "lol.pop@somemail.com")
.withReplyTo("lollypop-reply", "lol.pop.reply@somemail.com")
.withBounceTo("lollypop-bounce", "lol.pop.bounce@somemail.com")
.to("C.Cane", "candycane@candyshop.org")
.withPlainText("We should meet up!")
.withHTMLText("<b>We should meet up!</b><img src='cid:thumbsup'>")
.withHTMLText("<b>We should meet up!</b><img src='cid:thumbsup'><img src='cid:fixedNameWithoutFileExtensionForNamedEmbeddedImage'>")
.withSubject("hey")
.withAttachment("dresscode.txt", namedAttachment)
.withAttachment("location.txt", "On the moon!".getBytes(Charset.defaultCharset()), "text/plain")
.withEmbeddedImage("thumbsup", parseBase64Binary(base64String), "image/png")
.withEmbeddedImage("thumbsup", parseBase64Binary(base64StringOfThumbsupImage), "image/png")
.withAttachment("fixedNameWithoutFileExtensionForNamedAttachment", namedAttachment)
.withEmbeddedImage("fixedNameWithoutFileExtensionForNamedEmbeddedImage", namedEmbeddedImage)
.withDispositionNotificationTo("simple@address.com")
.withReturnReceiptTo("Complex Email", "simple@address.com")
.withHeader("dummyHeader", "dummyHeaderValue")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ private Email assertSendingEmail(final EmailPopulatingBuilder originalEmailPopul
}

if (compensateForDresscodeAttachmentNameOverrideErasure) {
TestDataHelper.fixDresscodeAttachment(receivedEmail);
TestDataHelper.retrofitLostOriginalAttachmentNames(receivedEmail);
}

assertThat(receivedEmail.getHeaders()).containsEntry("governanceDefaultTest1", singletonList("defaulted"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ public void testParser()
final MimeMessage mimeMessage = EmailConverter.emailToMimeMessage(emailNormal);
final Email emailFromMimeMessage = EmailConverter.mimeMessageToEmail(mimeMessage);

TestDataHelper.fixDresscodeAttachment(emailFromMimeMessage);
TestDataHelper.retrofitLostOriginalAttachmentNames(emailFromMimeMessage);

assertThat(emailFromMimeMessage).isEqualTo(emailNormal);
}
Expand Down
Loading

0 comments on commit d561ff9

Please sign in to comment.