Skip to content

Commit 5ef6bc7

Browse files
committed
Add inline attachment support
1 parent 3a4d792 commit 5ef6bc7

File tree

4 files changed

+96
-3
lines changed

4 files changed

+96
-3
lines changed

src/main/java/ch/astorm/jotlmsg/OutlookMessage.java

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,14 @@ public MimeMessage toMimeMessage(Session session) throws IOException, MessagingE
450450

451451
if(plainText==null && html==null) { throw new MessagingException("missing body"); }
452452

453+
final List<OutlookMessageAttachment> regularAttachments = getAttachments().stream()
454+
.filter(att -> att.getContentId() == null)
455+
.collect(Collectors.toList());
456+
457+
final List<OutlookMessageAttachment> inlineAttachments = getAttachments().stream()
458+
.filter(att -> att.getContentId() != null)
459+
.collect(Collectors.toList());
460+
453461
final MimeMultipart multipart = new MimeMultipart();
454462

455463
final List<MimeBodyPart> bodies = new ArrayList<>();
@@ -477,15 +485,44 @@ public MimeMessage toMimeMessage(Session session) throws IOException, MessagingE
477485
combinedBodiesPart = bodies.get(0);
478486
}
479487

480-
multipart.addBodyPart(combinedBodiesPart);
488+
if (inlineAttachments.isEmpty()) {
489+
multipart.addBodyPart(combinedBodiesPart);
490+
}
491+
else {
492+
final MimeMultipart relatedPart = new MimeMultipart("related");
493+
relatedPart.addBodyPart(combinedBodiesPart);
494+
495+
for(OutlookMessageAttachment attachment : inlineAttachments) {
496+
String name = attachment.getName();
497+
String mimeType = attachment.getMimeType();
498+
byte[] data = readAttachement(attachment);
499+
String contentId = attachment.getContentId();
500+
501+
MimeBodyPart part = new MimeBodyPart();
502+
part.setDataHandler(new DataHandler(new ByteArrayDataSource(data, mimeType)));
503+
part.setContentID(contentId);
504+
part.setHeader("Content-Disposition", "inline");
505+
part.setFileName(name);
506+
relatedPart.addBodyPart(part);
507+
}
481508

482-
for(OutlookMessageAttachment attachment : getAttachments()) {
509+
final MimeBodyPart relatedBodyPart = new MimeBodyPart();
510+
relatedBodyPart.setContent(relatedPart);
511+
multipart.addBodyPart(relatedBodyPart);
512+
}
513+
514+
for(OutlookMessageAttachment attachment : regularAttachments) {
483515
String name = attachment.getName();
484516
String mimeType = attachment.getMimeType();
485517
byte[] data = readAttachement(attachment);
518+
String contentId = attachment.getContentId();
486519

487520
MimeBodyPart part = new MimeBodyPart();
488521
part.setDataHandler(new DataHandler(new ByteArrayDataSource(data, mimeType)));
522+
if (contentId != null) {
523+
part.setContentID(contentId);
524+
part.setHeader("Content-Disposition", "inline");
525+
}
489526
part.setFileName(name);
490527
multipart.addBodyPart(part);
491528
}
@@ -668,6 +705,7 @@ public void writeTo(OutputStream outputStream) throws IOException {
668705

669706
String name = attachment.getName();
670707
String mimeType = attachment.getMimeType();
708+
String contentId = attachment.getContentId();
671709
byte[] data = readAttachement(attachment);
672710

673711
StoragePropertiesChunk attachStorage = new StoragePropertiesChunk();
@@ -677,6 +715,10 @@ public void writeTo(OutputStream outputStream) throws IOException {
677715
attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_LONG_FILENAME, FLAG_READABLE | FLAG_WRITEABLE, StringUtil.getToUnicodeLE(name)));
678716
}
679717
if(mimeType!=null) { attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_MIME_TAG, FLAG_READABLE | FLAG_WRITEABLE, StringUtil.getToUnicodeLE(mimeType))); }
718+
if(contentId!=null) {
719+
attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_CONTENT_ID, FLAG_READABLE | FLAG_WRITEABLE, StringUtil.getToUnicodeLE(contentId), Types.UNICODE_STRING));
720+
attachStorage.setProperty(createLongPropertyValue(MAPIProperty.ATTACH_FLAGS, 4));
721+
}
680722
attachStorage.setProperty(createLongPropertyValue(MAPIProperty.ATTACH_NUM, attachmentCounter));
681723
attachStorage.setProperty(createLongPropertyValue(MAPIProperty.ATTACH_METHOD, 1)); //ATTACH_BY_VALUE
682724
attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_DATA, FLAG_READABLE | FLAG_WRITEABLE, data));
@@ -910,13 +952,15 @@ protected void parseAttachments(MAPIMessage mapiMessage) throws ChunkNotFoundExc
910952
StringChunk fileName = attachmentChunk.getAttachFileName();
911953
ByteChunk data = attachmentChunk.getAttachData();
912954
StringChunk mimeType = attachmentChunk.getAttachMimeTag();
955+
StringChunk contentId = attachmentChunk.getAttachContentId();
913956

914957
String name = longFileName!=null ? longFileName.getValue() :
915958
fileName!=null ? fileName.getValue() :
916959
attachmentChunk.getPOIFSName();
917960
InputStream dataIS = data!=null ? new ByteArrayInputStream(data.getValue()) : null;
918961
String mimeTypeVal = mimeType!=null ? mimeType.getValue() : null;
919-
addAttachment(name, mimeTypeVal, dataIS);
962+
OutlookMessageAttachment attachment = addAttachment(name, mimeTypeVal, dataIS);
963+
attachment.setContentId(contentId!=null ? contentId.getValue() : null);
920964
}
921965
}
922966

src/main/java/ch/astorm/jotlmsg/OutlookMessageAttachment.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class OutlookMessageAttachment {
4848
private final String name;
4949
private String mimeType;
5050
private InputStreamCreator inputStreamCreator;
51+
private String contentId;
5152

5253
/**
5354
* Represents an {@code InputStream} creator.
@@ -148,6 +149,13 @@ public final String getName() {
148149
*/
149150
public String getMimeType() { return mimeType; }
150151
public void setMimeType(String mimeType) { this.mimeType = mimeType; }
152+
153+
/**
154+
* Defines the Content-Id, by which this attachment is referenceable in an HTML body.
155+
* Setting this to anything other than {@code null} will make this an inline attachment.
156+
*/
157+
public String getContentId() { return contentId; }
158+
public void setContentId(String contentId) { this.contentId = contentId; }
151159

152160
/**
153161
* Defines the {@code InputStreamCreator} that handles the attachment content.

src/test/java/ch/astorm/jotlmsg/OutlookMessageMIMETest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,29 @@ public void plainAndHtmlMail_shouldUseMultiPartAlternative() throws Exception {
193193
assertTrue(firstBodyPart.getDataHandler().getContentType().startsWith("multipart/alternative"));
194194
}
195195

196+
@Test
197+
public void plainAndHtmlMailWithInlineAttachments_shouldUseMultiPartRelated() throws Exception {
198+
OutlookMessage message = new OutlookMessage();
199+
message.setSubject("This is a message");
200+
message.setFrom("sender@jotlmsg.com");
201+
message.setReplyTo(Arrays.asList("reply1@jotlmsg.com", "reply2@jotlmsg.com"));
202+
message.setPlainTextBody("Hello,\n\nThis is a simple message.\n\n.Bye.");
203+
message.setHtmlBody("<html><body>Simple body</body></html>");
204+
message.addRecipient(OutlookMessageRecipient.Type.TO, "cedric@jotlmsg.com", "Cédric");
205+
final OutlookMessageAttachment inlineAttachment = new OutlookMessageAttachment("picture.png", "image/png", new ByteArrayInputStream(new byte[0]));
206+
inlineAttachment.setContentId("picture.png");
207+
message.addAttachment(inlineAttachment);
208+
209+
final MimeMessage mimeMessage = message.toMimeMessage();
210+
assertTrue(mimeMessage.getDataHandler().getContentType().startsWith("multipart/mixed"));
211+
final Object content = mimeMessage.getContent();
212+
assertInstanceOf(MimeMultipart.class, content);
213+
final MimeMultipart mimeMultipart = (MimeMultipart) content;
214+
assertEquals(1, mimeMultipart.getCount());
215+
final BodyPart firstBodyPart = mimeMultipart.getBodyPart(0);
216+
assertTrue(firstBodyPart.getDataHandler().getContentType().startsWith("multipart/related"));
217+
}
218+
196219
private static class CheckableInputStream extends InputStream {
197220
private boolean closed = false;
198221

src/test/java/ch/astorm/jotlmsg/OutlookMessageMSGTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.ArrayList;
1111
import java.util.Arrays;
1212
import java.util.List;
13+
import java.util.UUID;
1314
import java.util.stream.IntStream;
1415
import org.apache.poi.util.IOUtils;
1516
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -126,6 +127,22 @@ public void testHtmlBody() throws Exception {
126127
testMessage(message);
127128
}
128129

130+
@Test
131+
public void testHtmlBodyWithInlineAttachment() throws Exception {
132+
OutlookMessage message = new OutlookMessage();
133+
message.setSubject("This is a message");
134+
message.setFrom("sender@jotlmsg.com");
135+
message.setPlainTextBody("Hello,\n\nThis is a simple message that has been sent.\n\n.Bye.");
136+
message.setHtmlBody("<html><body>Sample body</body></html>");
137+
message.addRecipient(OutlookMessageRecipient.Type.TO, "cedric@jotlmsg.com", "Cédric");
138+
139+
final OutlookMessageAttachment inlineAttachment = new OutlookMessageAttachment("picture.png", "image/png", new ByteArrayInputStream(new byte[0]));
140+
inlineAttachment.setContentId("picture.png@" + UUID.randomUUID());
141+
message.addAttachment(inlineAttachment);
142+
143+
testMessage(message);
144+
}
145+
129146
private void testBinary(OutlookMessage message, String resPath) throws Exception {
130147
try(InputStream is = OutlookMessageMSGTest.class.getResourceAsStream(resPath)) {
131148
OutlookMessage source = new OutlookMessage(is);
@@ -185,6 +202,7 @@ private void compareMessage(OutlookMessage source, OutlookMessage other) throws
185202
OutlookMessageAttachment parsedAttachment = parsedAttachments.get(i);
186203
assertEquals(srcAttachment.getName(), parsedAttachment.getName());
187204
assertEquals(srcAttachment.getMimeType(), parsedAttachment.getMimeType());
205+
assertEquals(srcAttachment.getContentId(), parsedAttachment.getContentId());
188206
byte[] srcData = IOUtils.toByteArray(srcAttachment.getNewInputStream());
189207
byte[] parData = IOUtils.toByteArray(parsedAttachment.getNewInputStream());
190208
assertEquals(srcData.length, parData.length);

0 commit comments

Comments
 (0)