Skip to content

Commit 59d426a

Browse files
committed
Add inline attachment support
1 parent 7d4362c commit 59d426a

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
@@ -440,6 +440,14 @@ public MimeMessage toMimeMessage(Session session) throws IOException, MessagingE
440440

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

443+
final List<OutlookMessageAttachment> regularAttachments = getAttachments().stream()
444+
.filter(att -> att.getContentId() == null)
445+
.collect(Collectors.toList());
446+
447+
final List<OutlookMessageAttachment> inlineAttachments = getAttachments().stream()
448+
.filter(att -> att.getContentId() != null)
449+
.collect(Collectors.toList());
450+
443451
final MimeMultipart multipart = new MimeMultipart();
444452

445453
final List<MimeBodyPart> bodies = new ArrayList<>();
@@ -467,15 +475,44 @@ public MimeMessage toMimeMessage(Session session) throws IOException, MessagingE
467475
combinedBodiesPart = bodies.get(0);
468476
}
469477

470-
multipart.addBodyPart(combinedBodiesPart);
478+
if (inlineAttachments.isEmpty()) {
479+
multipart.addBodyPart(combinedBodiesPart);
480+
}
481+
else {
482+
final MimeMultipart relatedPart = new MimeMultipart("related");
483+
relatedPart.addBodyPart(combinedBodiesPart);
484+
485+
for(OutlookMessageAttachment attachment : inlineAttachments) {
486+
String name = attachment.getName();
487+
String mimeType = attachment.getMimeType();
488+
byte[] data = readAttachement(attachment);
489+
String contentId = attachment.getContentId();
490+
491+
MimeBodyPart part = new MimeBodyPart();
492+
part.setDataHandler(new DataHandler(new ByteArrayDataSource(data, mimeType)));
493+
part.setContentID(contentId);
494+
part.setHeader("Content-Disposition", "inline");
495+
part.setFileName(name);
496+
relatedPart.addBodyPart(part);
497+
}
471498

472-
for(OutlookMessageAttachment attachment : getAttachments()) {
499+
final MimeBodyPart relatedBodyPart = new MimeBodyPart();
500+
relatedBodyPart.setContent(relatedPart);
501+
multipart.addBodyPart(relatedBodyPart);
502+
}
503+
504+
for(OutlookMessageAttachment attachment : regularAttachments) {
473505
String name = attachment.getName();
474506
String mimeType = attachment.getMimeType();
475507
byte[] data = readAttachement(attachment);
508+
String contentId = attachment.getContentId();
476509

477510
MimeBodyPart part = new MimeBodyPart();
478511
part.setDataHandler(new DataHandler(new ByteArrayDataSource(data, mimeType)));
512+
if (contentId != null) {
513+
part.setContentID(contentId);
514+
part.setHeader("Content-Disposition", "inline");
515+
}
479516
part.setFileName(name);
480517
multipart.addBodyPart(part);
481518
}
@@ -660,6 +697,7 @@ public void writeTo(OutputStream outputStream) throws IOException {
660697

661698
String name = attachment.getName();
662699
String mimeType = attachment.getMimeType();
700+
String contentId = attachment.getContentId();
663701
byte[] data = readAttachement(attachment);
664702

665703
StoragePropertiesChunk attachStorage = new StoragePropertiesChunk();
@@ -669,6 +707,10 @@ public void writeTo(OutputStream outputStream) throws IOException {
669707
attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_LONG_FILENAME, FLAG_READABLE | FLAG_WRITEABLE, StringUtil.getToUnicodeLE(name)));
670708
}
671709
if(mimeType!=null) { attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_MIME_TAG, FLAG_READABLE | FLAG_WRITEABLE, StringUtil.getToUnicodeLE(mimeType))); }
710+
if(contentId!=null) {
711+
attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_CONTENT_ID, FLAG_READABLE | FLAG_WRITEABLE, StringUtil.getToUnicodeLE(contentId), Types.UNICODE_STRING));
712+
attachStorage.setProperty(createLongPropertyValue(MAPIProperty.ATTACH_FLAGS, 4));
713+
}
672714
attachStorage.setProperty(createLongPropertyValue(MAPIProperty.ATTACH_NUM, attachmentCounter));
673715
attachStorage.setProperty(createLongPropertyValue(MAPIProperty.ATTACH_METHOD, 1)); //ATTACH_BY_VALUE
674716
attachStorage.setProperty(new PropertyValue(MAPIProperty.ATTACH_DATA, FLAG_READABLE | FLAG_WRITEABLE, data));
@@ -888,13 +930,15 @@ protected void parseAttachments(MAPIMessage mapiMessage) throws ChunkNotFoundExc
888930
StringChunk fileName = attachmentChunk.getAttachFileName();
889931
ByteChunk data = attachmentChunk.getAttachData();
890932
StringChunk mimeType = attachmentChunk.getAttachMimeTag();
933+
StringChunk contentId = attachmentChunk.getAttachContentId();
891934

892935
String name = longFileName!=null ? longFileName.getValue() :
893936
fileName!=null ? fileName.getValue() :
894937
attachmentChunk.getPOIFSName();
895938
InputStream dataIS = data!=null ? new ByteArrayInputStream(data.getValue()) : null;
896939
String mimeTypeVal = mimeType!=null ? mimeType.getValue() : null;
897-
addAttachment(name, mimeTypeVal, dataIS);
940+
OutlookMessageAttachment attachment = addAttachment(name, mimeTypeVal, dataIS);
941+
attachment.setContentId(contentId!=null ? contentId.getValue() : null);
898942
}
899943
}
900944

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;
@@ -123,6 +124,22 @@ public void testHtmlBody() throws Exception {
123124
testMessage(message);
124125
}
125126

127+
@Test
128+
public void testHtmlBodyWithInlineAttachment() throws Exception {
129+
OutlookMessage message = new OutlookMessage();
130+
message.setSubject("This is a message");
131+
message.setFrom("sender@jotlmsg.com");
132+
message.setPlainTextBody("Hello,\n\nThis is a simple message that has been sent.\n\n.Bye.");
133+
message.setHtmlBody("<html><body>Sample body</body></html>");
134+
message.addRecipient(OutlookMessageRecipient.Type.TO, "cedric@jotlmsg.com", "Cédric");
135+
136+
final OutlookMessageAttachment inlineAttachment = new OutlookMessageAttachment("picture.png", "image/png", new ByteArrayInputStream(new byte[0]));
137+
inlineAttachment.setContentId("picture.png@" + UUID.randomUUID());
138+
message.addAttachment(inlineAttachment);
139+
140+
testMessage(message);
141+
}
142+
126143
private void testBinary(OutlookMessage message, String resPath) throws Exception {
127144
try(InputStream is = OutlookMessageMSGTest.class.getResourceAsStream(resPath)) {
128145
OutlookMessage source = new OutlookMessage(is);
@@ -181,6 +198,7 @@ private void compareMessage(OutlookMessage source, OutlookMessage other) throws
181198
OutlookMessageAttachment parsedAttachment = parsedAttachments.get(i);
182199
assertEquals(srcAttachment.getName(), parsedAttachment.getName());
183200
assertEquals(srcAttachment.getMimeType(), parsedAttachment.getMimeType());
201+
assertEquals(srcAttachment.getContentId(), parsedAttachment.getContentId());
184202
byte[] srcData = IOUtils.toByteArray(srcAttachment.getNewInputStream());
185203
byte[] parData = IOUtils.toByteArray(parsedAttachment.getNewInputStream());
186204
assertEquals(srcData.length, parData.length);

0 commit comments

Comments
 (0)