Repository: james-project Updated Branches: refs/heads/master 409c8cea6 -> b5c125798
JAMES-1752 Handle multipart mediatype when retrieving text/html body Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/f973b280 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/f973b280 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/f973b280 Branch: refs/heads/master Commit: f973b280b32efc64422ee85f6e11fa6434c5fd4e Parents: af7b1c6 Author: Antoine Duprat <adup...@linagora.com> Authored: Wed Jun 8 09:24:58 2016 +0200 Committer: Antoine Duprat <adup...@linagora.com> Committed: Fri Jun 10 08:25:59 2016 +0200 ---------------------------------------------------------------------- .../james/jmap/VacationIntegrationTest.java | 12 +- .../integration/GetMessagesMethodTest.java | 149 ++++++++--- .../htmlAndTextMultipartWithOneAttachment.eml | 64 +++++ .../test/resources/twoAttachmentsTextPlain.eml | 73 ++++++ .../org/apache/james/jmap/model/Message.java | 39 ++- .../jmap/model/message/IndexableMessage.java | 26 +- .../james/jmap/model/message/MimePart.java | 73 ++++-- .../james/jmap/model/message/MimePartTest.java | 249 +++++++++++++++++++ 8 files changed, 581 insertions(+), 104 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/VacationIntegrationTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/VacationIntegrationTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/VacationIntegrationTest.java index 3e7c155..50df787 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/VacationIntegrationTest.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/VacationIntegrationTest.java @@ -26,7 +26,6 @@ import static com.jayway.restassured.config.RestAssuredConfig.newConfig; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.nullValue; import java.util.List; import java.util.Map; @@ -56,6 +55,7 @@ public abstract class VacationIntegrationTest { private static final String USER_2 = "matthieu@" + DOMAIN; private static final String PASSWORD = "secret"; private static final String REASON = "Message explaining my wonderful vacations"; + private static final String HTML_REASON = "<b>" + REASON + "</b>"; public static final String ORIGINAL_MESSAGE_TEXT_BODY = "Hello someone, and thank you for joining example.com!"; private ConditionFactory calmlyAwait; @@ -137,7 +137,7 @@ public abstract class VacationIntegrationTest { calmlyAwait.atMost(10, TimeUnit.SECONDS) .until(() -> isTextMessageReceived(user1AccessToken, getInboxId(user1AccessToken), ORIGINAL_MESSAGE_TEXT_BODY, USER_2, USER_1)); calmlyAwait.atMost(10, TimeUnit.SECONDS) - .until( () -> assertOneMessageWithNullTextBodyReceived(user2AccessToken, getInboxId(user2AccessToken), USER_1, USER_2)); + .until( () -> assertOneMessageWithHtmlBodyReceived(user2AccessToken, getInboxId(user2AccessToken), HTML_REASON, USER_1, USER_2)); } @Test @@ -278,7 +278,7 @@ public abstract class VacationIntegrationTest { " \"singleton\" : {" + " \"id\": \"singleton\"," + " \"isEnabled\": \"true\"," + - " \"htmlBody\": \"<b>" + REASON + "</b>\"" + + " \"htmlBody\": \"" + HTML_REASON + "\"" + " }" + " }" + "}, \"#0\"" + @@ -376,7 +376,7 @@ public abstract class VacationIntegrationTest { .body(SECOND_ARGUMENTS + ".list[0].to.email[0]", equalTo(expectedTo)); } - private boolean assertOneMessageWithNullTextBodyReceived(AccessToken recipientToken, String mailboxId, String expectedFrom, String expectedTo) { + private boolean assertOneMessageWithHtmlBodyReceived(AccessToken recipientToken, String mailboxId, String expectedHtmlBody, String expectedFrom, String expectedTo) { try { with() .accept(ContentType.JSON) @@ -385,7 +385,7 @@ public abstract class VacationIntegrationTest { .body("[[\"getMessageList\", " + "{" + " \"fetchMessages\": true, " + - " \"fetchMessageProperties\": [\"textBody\", \"from\", \"to\", \"mailboxIds\"]," + + " \"fetchMessageProperties\": [\"htmlBody\", \"from\", \"to\", \"mailboxIds\"]," + " \"filter\": {" + " \"inMailboxes\":[\"" + mailboxId + "\"]" + " }" + @@ -395,7 +395,7 @@ public abstract class VacationIntegrationTest { .statusCode(200) .body(SECOND_NAME, equalTo("messages")) .body(SECOND_ARGUMENTS + ".list", hasSize(1)) - .body(SECOND_ARGUMENTS + ".list[0].textBody", nullValue()) + .body(SECOND_ARGUMENTS + ".list[0].htmlBody", equalTo(expectedHtmlBody)) .body(SECOND_ARGUMENTS + ".list[0].from.email", equalTo(expectedFrom)) .body(SECOND_ARGUMENTS + ".list[0].to.email", hasSize(1)) .body(SECOND_ARGUMENTS + ".list[0].to.email[0]", equalTo(expectedTo)); http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java index 90776d8..cff96a4 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java @@ -51,7 +51,8 @@ import com.jayway.restassured.http.ContentType; public abstract class GetMessagesMethodTest { private static final String NAME = "[0][0]"; private static final String ARGUMENTS = "[0][1]"; - private static final String ATTACHMENTS = ARGUMENTS + ".list[0].attachments"; + private static final String FIRST_MESSAGE = ARGUMENTS + ".list[0]"; + private static final String ATTACHMENTS = FIRST_MESSAGE + ".attachments"; private static final String FIRST_ATTACHMENT = ATTACHMENTS + "[0]"; private static final String SECOND_ATTACHMENT = ATTACHMENTS + "[1]"; @@ -182,15 +183,15 @@ public abstract class GetMessagesMethodTest { .statusCode(200) .body(NAME, equalTo("messages")) .body(ARGUMENTS + ".list", hasSize(1)) - .body(ARGUMENTS + ".list[0].id", equalTo(username + "|inbox|1")) - .body(ARGUMENTS + ".list[0].threadId", equalTo(username + "|inbox|1")) - .body(ARGUMENTS + ".list[0].subject", equalTo("my test subject")) - .body(ARGUMENTS + ".list[0].textBody", equalTo("testmail")) - .body(ARGUMENTS + ".list[0].isUnread", equalTo(true)) - .body(ARGUMENTS + ".list[0].preview", equalTo("testmail")) - .body(ARGUMENTS + ".list[0].headers", equalTo(ImmutableMap.of("subject", "my test subject"))) - .body(ARGUMENTS + ".list[0].date", equalTo("2014-10-30T14:12:00Z")) - .body(ARGUMENTS + ".list[0].hasAttachment", equalTo(false)) + .body(FIRST_MESSAGE + ".id", equalTo(username + "|inbox|1")) + .body(FIRST_MESSAGE + ".threadId", equalTo(username + "|inbox|1")) + .body(FIRST_MESSAGE + ".subject", equalTo("my test subject")) + .body(FIRST_MESSAGE + ".textBody", equalTo("testmail")) + .body(FIRST_MESSAGE + ".isUnread", equalTo(true)) + .body(FIRST_MESSAGE + ".preview", equalTo("testmail")) + .body(FIRST_MESSAGE + ".headers", equalTo(ImmutableMap.of("subject", "my test subject"))) + .body(FIRST_MESSAGE + ".date", equalTo("2014-10-30T14:12:00Z")) + .body(FIRST_MESSAGE + ".hasAttachment", equalTo(false)) .body(ATTACHMENTS, empty()); } @@ -215,14 +216,14 @@ public abstract class GetMessagesMethodTest { .statusCode(200) .body(NAME, equalTo("messages")) .body(ARGUMENTS + ".list", hasSize(1)) - .body(ARGUMENTS + ".list[0].id", equalTo(username + "|inbox|1")) - .body(ARGUMENTS + ".list[0].threadId", equalTo(username + "|inbox|1")) - .body(ARGUMENTS + ".list[0].subject", equalTo("my test subject")) - .body(ARGUMENTS + ".list[0].htmlBody", equalTo("This is a <b>HTML</b> mail")) - .body(ARGUMENTS + ".list[0].isUnread", equalTo(true)) - .body(ARGUMENTS + ".list[0].preview", equalTo("This is a <b>HTML</b> mail")) - .body(ARGUMENTS + ".list[0].headers", equalTo(ImmutableMap.of("content-type", "text/html", "subject", "my test subject"))) - .body(ARGUMENTS + ".list[0].date", equalTo("2014-10-30T14:12:00Z")); + .body(FIRST_MESSAGE + ".id", equalTo(username + "|inbox|1")) + .body(FIRST_MESSAGE + ".threadId", equalTo(username + "|inbox|1")) + .body(FIRST_MESSAGE + ".subject", equalTo("my test subject")) + .body(FIRST_MESSAGE + ".htmlBody", equalTo("This is a <b>HTML</b> mail")) + .body(FIRST_MESSAGE + ".isUnread", equalTo(true)) + .body(FIRST_MESSAGE + ".preview", equalTo("This is a <b>HTML</b> mail")) + .body(FIRST_MESSAGE + ".headers", equalTo(ImmutableMap.of("content-type", "text/html", "subject", "my test subject"))) + .body(FIRST_MESSAGE + ".date", equalTo("2014-10-30T14:12:00Z")); } @Test @@ -246,13 +247,13 @@ public abstract class GetMessagesMethodTest { .statusCode(200) .body(NAME, equalTo("messages")) .body(ARGUMENTS + ".list", hasSize(1)) - .body(ARGUMENTS + ".list[0].id", equalTo(username + "|inbox|1")) - .body(ARGUMENTS + ".list[0].subject", equalTo("my test subject")) - .body(ARGUMENTS + ".list[0].textBody", nullValue()) - .body(ARGUMENTS + ".list[0].isUnread", nullValue()) - .body(ARGUMENTS + ".list[0].preview", nullValue()) - .body(ARGUMENTS + ".list[0].headers", nullValue()) - .body(ARGUMENTS + ".list[0].date", nullValue()); + .body(FIRST_MESSAGE + ".id", equalTo(username + "|inbox|1")) + .body(FIRST_MESSAGE + ".subject", equalTo("my test subject")) + .body(FIRST_MESSAGE + ".textBody", nullValue()) + .body(FIRST_MESSAGE + ".isUnread", nullValue()) + .body(FIRST_MESSAGE + ".preview", nullValue()) + .body(FIRST_MESSAGE + ".headers", nullValue()) + .body(FIRST_MESSAGE + ".date", nullValue()); } @Test @@ -281,13 +282,13 @@ public abstract class GetMessagesMethodTest { .statusCode(200) .body(NAME, equalTo("messages")) .body(ARGUMENTS + ".list", hasSize(1)) - .body(ARGUMENTS + ".list[0].id", equalTo(username + "|inbox|1")) - .body(ARGUMENTS + ".list[0].subject", nullValue()) - .body(ARGUMENTS + ".list[0].textBody", nullValue()) - .body(ARGUMENTS + ".list[0].isUnread", nullValue()) - .body(ARGUMENTS + ".list[0].preview", nullValue()) - .body(ARGUMENTS + ".list[0].headers", equalTo(ImmutableMap.of("from", "u...@domain.tld", "header2", "Header2Content"))) - .body(ARGUMENTS + ".list[0].date", nullValue()); + .body(FIRST_MESSAGE + ".id", equalTo(username + "|inbox|1")) + .body(FIRST_MESSAGE + ".subject", nullValue()) + .body(FIRST_MESSAGE + ".textBody", nullValue()) + .body(FIRST_MESSAGE + ".isUnread", nullValue()) + .body(FIRST_MESSAGE + ".preview", nullValue()) + .body(FIRST_MESSAGE + ".headers", equalTo(ImmutableMap.of("from", "u...@domain.tld", "header2", "Header2Content"))) + .body(FIRST_MESSAGE + ".date", nullValue()); } @Test @@ -334,8 +335,8 @@ public abstract class GetMessagesMethodTest { .statusCode(200) .body(NAME, equalTo("messages")) .body(ARGUMENTS + ".list", hasSize(1)) - .body(ARGUMENTS + ".list[0].id", equalTo("usern...@domain.tld|mailbox|1")) - .body(ARGUMENTS + ".list[0].subject", equalTo("my test subject")); + .body(FIRST_MESSAGE + ".id", equalTo("usern...@domain.tld|mailbox|1")) + .body(FIRST_MESSAGE + ".subject", equalTo("my test subject")); } @Test @@ -359,7 +360,7 @@ public abstract class GetMessagesMethodTest { .statusCode(200) .body(NAME, equalTo("messages")) .body(ARGUMENTS + ".list", hasSize(1)) - .body(ARGUMENTS + ".list[0].hasAttachment", equalTo(true)) + .body(FIRST_MESSAGE + ".hasAttachment", equalTo(true)) .body(ATTACHMENTS, hasSize(2)) .body(FIRST_ATTACHMENT + ".blobId", equalTo("223a76c0e8c1b1762487d8e0598bd88497d73ef2")) .body(FIRST_ATTACHMENT + ".type", equalTo("image/jpeg")) @@ -368,4 +369,82 @@ public abstract class GetMessagesMethodTest { .body(SECOND_ATTACHMENT + ".type", equalTo("image/jpeg")) .body(SECOND_ATTACHMENT + ".size", equalTo(597)); } + + @Test + public void getMessagesShouldReturnHTMLBodyWhenSomeAttachmentsAndHTMLmail() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox"); + + ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z"); + jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "inbox"), + ClassLoader.getSystemResourceAsStream("twoAttachments.eml"), Date.from(dateTime.toInstant()), false, new Flags()); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + username + "|inbox|1\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(FIRST_MESSAGE + ".hasAttachment", equalTo(true)) + .body(ATTACHMENTS, hasSize(2)) + .body(FIRST_MESSAGE + ".preview", equalTo("<b>html</b>\n")) + .body(FIRST_MESSAGE + ".textBody", nullValue()) + .body(FIRST_MESSAGE + ".htmlBody", equalTo("<b>html</b>\n")); + } + + @Test + public void getMessagesShouldReturnTextBodyWhenSomeAttachmentsAndTextmail() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox"); + + ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z"); + jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "inbox"), + ClassLoader.getSystemResourceAsStream("twoAttachmentsTextPlain.eml"), Date.from(dateTime.toInstant()), false, new Flags()); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + username + "|inbox|1\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(FIRST_MESSAGE + ".hasAttachment", equalTo(true)) + .body(ATTACHMENTS, hasSize(2)) + .body(FIRST_MESSAGE + ".preview", equalTo("html\n")) + .body(FIRST_MESSAGE + ".textBody", equalTo("html\n")) + .body(FIRST_MESSAGE + ".htmlBody", nullValue()); + } + + @Test + public void getMessagesShouldReturnBothHTMLAndTextBodyWhenOneAttachmentAndBothHTMLAndTextmail() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox"); + + ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z"); + jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "inbox"), + ClassLoader.getSystemResourceAsStream("htmlAndTextMultipartWithOneAttachment.eml"), Date.from(dateTime.toInstant()), false, new Flags()); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + username + "|inbox|1\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(FIRST_MESSAGE + ".hasAttachment", equalTo(true)) + .body(ATTACHMENTS, hasSize(1)) + .body(FIRST_MESSAGE + ".preview", equalTo("<i>blabla</i>\n<b>bloblo</b>\n")) + .body(FIRST_MESSAGE + ".textBody", equalTo("/blabla/\n*bloblo*\n")) + .body(FIRST_MESSAGE + ".htmlBody", equalTo("<i>blabla</i>\n<b>bloblo</b>\n")); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/htmlAndTextMultipartWithOneAttachment.eml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/htmlAndTextMultipartWithOneAttachment.eml b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/htmlAndTextMultipartWithOneAttachment.eml new file mode 100644 index 0000000..a5ddc8b --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/htmlAndTextMultipartWithOneAttachment.eml @@ -0,0 +1,64 @@ +Return-Path: <us...@open-paas.org> +MIME-Version: 1.0 +Delivered-To: us...@open-paas.org +Received: from 172.18.0.1 (EHLO [172.18.0.1]) ([172.18.0.1]) + by open-paas.org (JAMES SMTP Server ) with ESMTP ID -674616268 + for <us...@open-paas.org>; + Tue, 07 Jun 2016 14:23:37 +0000 (UTC) +To: us...@open-paas.org +From: user0 <us...@open-paas.org> +Subject: BOTH +Message-ID: <1cc7f114-dbc4-42c2-99bd-f1100db6d...@open-paas.org> +Date: Tue, 7 Jun 2016 16:23:37 +0200 +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 + Thunderbird/45.1.1 +Content-Type: multipart/mixed; + boundary="------------7AF1D14DE1DFA16229726B54" + +This is a multi-part message in MIME format. +--------------7AF1D14DE1DFA16229726B54 +Content-Type: multipart/alternative; + boundary="------------172F9470CFA3BF3417835D92" + + +--------------172F9470CFA3BF3417835D92 +Content-Type: text/plain; charset=utf-8; format=flowed +Content-Transfer-Encoding: 7bit + +/blabla/ +*bloblo* + +--------------172F9470CFA3BF3417835D92 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: 7bit + +<i>blabla</i> +<b>bloblo</b> + +--------------172F9470CFA3BF3417835D92-- + +--------------7AF1D14DE1DFA16229726B54 +Content-Type: image/jpeg; + name="4037_014.jpg" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="4037_014.jpg" + +/9j/4X2cRXhpZgAASUkqAAgAAAANAA8BAgAKAAAAqgAAABABAgAJAAAAtAAAABIBAwABAAAA +AQAAABoBBQABAAAAvgAAABsBBQABAAAAxgAAACgBAwABAAAAAgAAADEBAgAKAAAAzgAAADIB +AgAUAAAA2AAAABMCAwABAAAAAgAAAGmHBAABAAAAfAIAAKXEBwDQAAAA7AAAANLGBwBAAAAA +vAEAANPGBwCAAAAA/AEAAEwqAABQYW5hc29uaWMARE1DLUZaNDUAALQAAAABAAAAtAAAAAEA +AABWZXIuMS4wICAAMjAxNDowMjoyNSAxMDozMjowOQBQcmludElNADAyNTAAAA4AAQAWABYA +AgAAAAAAAwBkAAAABwAAAAAACAAAAAAACQAAAAAACgAAAAAACwCsAAAADAAAAAAADQAAAAAA +DgDEAAAAAAEFAAAAAQEBAAAAEAGAAAAACREAABAnAAALDwAAECcAAJcFAAAQJwAAsAgAABAn +AAABHAAAECcAAF4CAAAQJwAAiwAAABAnAADLAwAAECcAAOUbAAAQJwAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + +--------------7AF1D14DE1DFA16229726B54-- http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/twoAttachmentsTextPlain.eml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/twoAttachmentsTextPlain.eml b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/twoAttachmentsTextPlain.eml new file mode 100644 index 0000000..7760185 --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/twoAttachmentsTextPlain.eml @@ -0,0 +1,73 @@ +Return-Path: <f...@linagora.com> +Received: from alderaan.linagora.com (smtp.linagora.dc1 [172.16.18.53]) + by imap (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; + Fri, 27 Feb 2015 20:32:14 +0100 +X-Sieve: CMU Sieve 2.2 +Received: from canondir.par.lng (unknown [92.103.166.6]) + (using TLSv1 with cipher AES256-SHA (256/256 bits)) + (No client certificate requested) + by alderaan.linagora.com (Postfix) with ESMTPSA id BAB0D728 + for <t...@linagora.com>; Fri, 27 Feb 2015 20:31:38 +0100 (CET) +X-Priority: 3 (Normal) +From: "From" + <f...@linagora.com> +To: "To" + <t...@linagora.com> +Subject: [8/10]Attached Image light with text +Date: Fri, 27 Mar 2015 21:48:38 +0100 +Message-Id: <20150227203838.0011.canontxno.4...@canondir.par.lng> +Mime-Version: 1.0 +Content-Type: multipart/mixed; + boundary="AHNPACBLDCDIDAGGGDDFAABECGCA" + +--AHNPACBLDCDIDAGGGDDFAABECGCA +Content-Type: text/plain; charset=utf-8; format=flowed +Content-Transfer-Encoding: 8bit + +html + +--AHNPACBLDCDIDAGGGDDFAABECGCA +Content-Type: image/jpeg; + name="4037_014.jpg" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="4037_014.jpg" + +/9j/4X2cRXhpZgAASUkqAAgAAAANAA8BAgAKAAAAqgAAABABAgAJAAAAtAAAABIBAwABAAAA +AQAAABoBBQABAAAAvgAAABsBBQABAAAAxgAAACgBAwABAAAAAgAAADEBAgAKAAAAzgAAADIB +AgAUAAAA2AAAABMCAwABAAAAAgAAAGmHBAABAAAAfAIAAKXEBwDQAAAA7AAAANLGBwBAAAAA +vAEAANPGBwCAAAAA/AEAAEwqAABQYW5hc29uaWMARE1DLUZaNDUAALQAAAABAAAAtAAAAAEA +AABWZXIuMS4wICAAMjAxNDowMjoyNSAxMDozMjowOQBQcmludElNADAyNTAAAA4AAQAWABYA +AgAAAAAAAwBkAAAABwAAAAAACAAAAAAACQAAAAAACgAAAAAACwCsAAAADAAAAAAADQAAAAAA +DgDEAAAAAAEFAAAAAQEBAAAAEAGAAAAACREAABAnAAALDwAAECcAAJcFAAAQJwAAsAgAABAn +AAABHAAAECcAAF4CAAAQJwAAiwAAABAnAADLAwAAECcAAOUbAAAQJwAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + +--AHNPACBLDCDIDAGGGDDFAABECGCA +Content-Type: image/jpeg; + name="4037_015.jpg" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="4037_015.jpg" + +iVBORw0KGgoAAAANSUhEUgAABYUAAAL4CAIAAACrzA8fAAAACXBIWXMAAAsTAAALEwEAmpwY +AAAAB3RJTUUH3wMNDiUMca0XkQAAIABJREFUeNq8vWm0bdlVHja/tc+99zX13qv2VSeVmlIv +FSqEOhA2AoEhxMRBg8QOyEkG6WMDSZzhMQTGDDcwiJPhBBMby4SMDJthgmOMAYvGMcF0QgIU +WRKobymp+qpX9fp779nry4+9mjnnWuuc80qMvCpdvXrv3nP22Xutueb85je/D1/3+q+ipF9z +nGfGdZznGCMjKaSIkJQwBYSAEMI0Tatptdrb21utVqvVtJKAAAABEAAiUP+IiECYfsfyJf1B +10+cOHH48OGVlZVSypaWlv379zc2Nr722msNDQ2u+y9zaYQeAQAAAACCHgEAAAB0y969e+ON +KnpOe3v7JZdccj6fwPXr10+aNKnAB956663Zs2fjTgMAAADOKzhOAQAAAFCYs+yO0XfNNQAA +AAAA/nNBfgQAAADQDVddddW6detSqdQZ7JvL5W655RZIEgAAAAAAEayKigqcBQAAAKAAhw8f +3rZt25AhQwYNGpTJZHq4V2tr6/bt25csWbJjxw6cQwAAAACACMiPAAAAAAAAAAAAQH8D/wgA +AAAAAAAAAAD0N9AjAAAAAAAAAAAA0N9AjwAAAAAAAAAAAEB/Az0CAAAAAAAAAAAA/Q30CAAA +AAAAAAAAAPQ30CMAAAAAAAAAAADQ30CPAAAAAAAAAAAAQH/z/0IKJwmNQbpqAAAAAElFTkSu +QmCC + +--AHNPACBLDCDIDAGGGDDFAABECGCA-- http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java index 58e62a6..8e87a1e 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.james.jmap.methods.GetMessagesMethod; import org.apache.james.jmap.methods.JmapResponseWriterImpl; @@ -46,7 +47,6 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; -import com.google.common.net.MediaType; @JsonDeserialize(builder = Message.Builder.class) @JsonFilter(JmapResponseWriterImpl.PROPERTIES_FILTER) @@ -121,12 +121,18 @@ public class Message { } private static String getPreview(IndexableMessage im) { - return Optional.ofNullable( - Strings.emptyToNull( - im.getBodyText() - .map(Message::computePreview) - .orElse(NO_BODY))) + Stream<Optional<String>> htmlThenTextStream = Stream.of( + im.getBodyHtml(), + im.getBodyText()); + + String body = htmlThenTextStream + .filter(Optional::isPresent) + .map(Optional::get) + .filter(stringBody -> !stringBody.isEmpty()) + .findFirst() .orElse(NO_BODY); + + return computePreview(body); } @VisibleForTesting static String computePreview(String body) { @@ -157,28 +163,11 @@ public class Message { } private static String getTextBody(IndexableMessage im) { - if (isText(im) && !isHtml(im)) { - return im.getBodyText().map(Strings::emptyToNull).orElse(null); - } - return null; + return im.getBodyText().map(Strings::emptyToNull).orElse(null); } private static String getHtmlBody(IndexableMessage im) { - if (isText(im) && isHtml(im)) { - return im.getBodyText().map(Strings::emptyToNull).orElse(null); - } - return null; - } - - private static boolean isText(IndexableMessage im) { - return im.getMediaType() == null - || im.getMediaType().equals(MediaType.ANY_TEXT_TYPE.type()); - } - - private static boolean isHtml(IndexableMessage im) { - return isText(im) - && im.getSubType() != null - && im.getSubType().equals(MediaType.HTML_UTF_8.subtype()); + return im.getBodyHtml().map(Strings::emptyToNull).orElse(null); } private static List<Attachment> getAttachments(List<org.apache.james.mailbox.store.mail.model.Attachment> attachments) { http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java index 055533a..972ac63 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java @@ -33,6 +33,7 @@ import org.apache.james.mailbox.store.mail.model.MailboxMessage; import org.apache.james.mailbox.store.mail.model.Property; import org.apache.james.mime4j.MimeException; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; @@ -45,13 +46,14 @@ public class IndexableMessage { IndexableMessage indexableMessage = new IndexableMessage(); try { MimePart parsingResult = new MimePartParser(message, textExtractor).parse(); - indexableMessage.bodyText = parsingResult.locateFirstTextualBody(); + indexableMessage.bodyText = parsingResult.retrieveTextPlainBody(); + indexableMessage.bodyHtml = parsingResult.retrieveTextHtmlBody(); indexableMessage.setFlattenedAttachments(parsingResult); indexableMessage.copyHeaderFields(parsingResult.getHeaderCollection(), getSanitizedInternalDate(message, zoneId)); + indexableMessage.copyMessageFields(message, zoneId); } catch (IOException | MimeException e) { throw Throwables.propagate(e); } - indexableMessage.copyMessageFields(message, zoneId); return indexableMessage; } @@ -77,8 +79,6 @@ public class IndexableMessage { this.modSeq = message.getModSeq(); this.size = message.getFullContentOctets(); this.date = DateResolutionFormater.DATE_TIME_FOMATTER.format(getSanitizedInternalDate(message, zoneId)); - this.mediaType = message.getMediaType(); - this.subType = message.getSubType(); this.isAnswered = message.isAnswered(); this.isDeleted = message.isDeleted(); this.isDraft = message.isDraft(); @@ -103,8 +103,6 @@ public class IndexableMessage { private long modSeq; private long size; private String date; - private String mediaType; - private String subType; private boolean isUnRead; private boolean isRecent; private boolean isFlagged; @@ -123,6 +121,7 @@ public class IndexableMessage { private List<Property> properties; private List<MimePart> attachments; private Optional<String> bodyText; + private Optional<String> bodyHtml; @JsonProperty(JsonMessageConstants.ID) public Long getId() { @@ -149,16 +148,6 @@ public class IndexableMessage { return date; } - @JsonProperty(JsonMessageConstants.MEDIA_TYPE) - public String getMediaType() { - return mediaType; - } - - @JsonProperty(JsonMessageConstants.SUBTYPE) - public String getSubType() { - return subType; - } - @JsonProperty(JsonMessageConstants.IS_UNREAD) public boolean isUnRead() { return isUnRead; @@ -249,6 +238,11 @@ public class IndexableMessage { return bodyText; } + @JsonIgnore + public Optional<String> getBodyHtml() { + return bodyHtml; + } + @JsonProperty(JsonMessageConstants.HAS_ATTACHMENT) public boolean getHasAttachment() { return attachments.size() > 0; http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java index 928d3fa..7f573d3 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java @@ -19,12 +19,12 @@ package org.apache.james.jmap.model.message; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; +import java.io.InputStream; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + import org.apache.commons.io.FilenameUtils; import org.apache.james.mailbox.store.extractor.DefaultTextExtractor; import org.apache.james.mailbox.store.extractor.ParsedContent; @@ -33,13 +33,23 @@ import org.apache.james.mime4j.stream.Field; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.InputStream; -import java.util.List; -import java.util.Optional; -import java.util.stream.Stream; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; public class MimePart { + private static final String HTML_SUBTYPE = "html"; + private static final String PLAIN_SUBTYPE = "plain"; + + public static Builder builder() { + return new Builder(); + } + public static class Builder implements MimePartContainerBuilder { private final HeaderCollection.Builder headerCollectionBuilder; @@ -160,11 +170,6 @@ public class MimePart { return Optional.empty(); } } - - } - - public static Builder builder() { - return new Builder(); } private static final Logger LOGGER = LoggerFactory.getLogger(MimePart.class); @@ -244,14 +249,38 @@ public class MimePart { } @JsonIgnore - public Optional<String> locateFirstTextualBody() { + public Optional<String> retrieveTextHtmlBody() { + return retrieveTextBody(MimePart::isHTML); + } + + @JsonIgnore + public Optional<String> retrieveTextPlainBody() { + return retrieveTextBody(MimePart::isPlain); + } + + private Optional<String> retrieveTextBody(Predicate<MimePart> filter) { return Stream.concat( - Stream.of(this), - attachments.stream()) - .map((mimePart) -> mimePart.bodyTextContent) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); + Stream.of(this), + getAttachmentsStream()) + .filter(filter) + .map(MimePart::getTextualBody) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst(); + } + + @JsonIgnore + @VisibleForTesting boolean isHTML() { + return subType + .filter(HTML_SUBTYPE::equals) + .isPresent(); + } + + @JsonIgnore + @VisibleForTesting boolean isPlain() { + return subType + .filter(PLAIN_SUBTYPE::equals) + .isPresent(); } @JsonIgnore http://git-wip-us.apache.org/repos/asf/james-project/blob/f973b280/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/message/MimePartTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/message/MimePartTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/message/MimePartTest.java new file mode 100644 index 0000000..c935560 --- /dev/null +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/message/MimePartTest.java @@ -0,0 +1,249 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.jmap.model.message; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.commons.io.IOUtils; +import org.junit.Test; + +public class MimePartTest { + + @Test + public void isHTMLShouldReturnTrueWhenHTMLSubType() { + MimePart mimePart = MimePart.builder() + .addSubType("html") + .build(); + assertThat(mimePart.isHTML()).isTrue(); + } + + @Test + public void isHTMLShouldReturnFalseWhenOtherSubType() { + MimePart mimePart = MimePart.builder() + .addSubType("other") + .build(); + assertThat(mimePart.isHTML()).isFalse(); + } + + @Test + public void isPlainShouldReturnTrueWhenPlainSubType() { + MimePart mimePart = MimePart.builder() + .addSubType("plain") + .build(); + assertThat(mimePart.isPlain()).isTrue(); + } + + @Test + public void isPlainShouldReturnFalseWhenOtherSubType() { + MimePart mimePart = MimePart.builder() + .addSubType("other") + .build(); + assertThat(mimePart.isPlain()).isFalse(); + } + + @Test + public void retrieveTextHtmlBodyShouldReturnEmptyWhenOtherSubType() { + MimePart mimePart = MimePart.builder() + .addSubType("other") + .build(); + assertThat(mimePart.retrieveTextHtmlBody()).isEmpty(); + } + + @Test + public void retrieveTextHtmlBodyShouldReturnHtmlBodyWhenHtmlSubType() { + String expectedContent = "<b>content</b>"; + MimePart htmlMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + + assertThat(htmlMimePart.retrieveTextHtmlBody()).contains(expectedContent); + } + + @Test + public void retrieveTextHtmlBodyShouldReturnHtmlBodyFromAttachmentsWhenHtmlSubTypeInAttachments() { + String expectedContent = "<b>content</b>"; + MimePart htmlMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(htmlMimePart) + .build(); + + assertThat(mimePart.retrieveTextHtmlBody()).contains(expectedContent); + } + + @Test + public void retrieveTextHtmlBodyShouldReturnHtmlBodyWhenMultipleAttachments() { + String expectedContent = "<b>content</b>"; + MimePart htmlMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + MimePart plainMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream("content")) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(plainMimePart) + .addChild(htmlMimePart) + .build(); + + assertThat(mimePart.retrieveTextHtmlBody()).contains(expectedContent); + } + + @Test + public void retrieveTextHtmlBodyShouldReturnFirstHtmlBodyWhenMultipleHtml() { + String expectedContent = "<b>first</b>"; + MimePart firstMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + MimePart secondMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream("<b>second</b>")) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(firstMimePart) + .addChild(secondMimePart) + .build(); + + assertThat(mimePart.retrieveTextHtmlBody()).contains(expectedContent); + } + + @Test + public void retrieveTextHtmlBodyShouldReturnEmptyWhenMultipleAttachmentsAndNoHtmlContent() { + MimePart htmlMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .build(); + MimePart plainMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream("content")) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(plainMimePart) + .addChild(htmlMimePart) + .build(); + + assertThat(mimePart.retrieveTextHtmlBody()).isEmpty(); + } + + @Test + public void retrieveTextPlainMimePartShouldReturnTextBodyWhenPlainSubType() { + String expectedContent = "content"; + MimePart plainMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + assertThat(plainMimePart.retrieveTextPlainBody()).contains(expectedContent); + } + + @Test + public void retrieveTextPlainMimePartShouldReturnTextBodyFromAttachmentsWhenPlainSubTypeInAttachments() { + String expectedContent = "content"; + MimePart plainMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + MimePart mimePart = MimePart.builder() + .addChild(plainMimePart) + .build(); + assertThat(mimePart.retrieveTextPlainBody()).contains(expectedContent); + } + + @Test + public void retrieveTextPlainBodyShouldReturnTextBodyWhenMultipleAttachments() { + String expectedContent = "content"; + MimePart plainMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + MimePart htmlMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream("<b>content</b>")) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(htmlMimePart) + .addChild(plainMimePart) + .build(); + + assertThat(mimePart.retrieveTextPlainBody()).contains(expectedContent); + } + + @Test + public void retrieveTextPlainBodyShouldReturnTheFirstTextBodyWhenMultipleText() { + String expectedContent = "first"; + MimePart firstMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream(expectedContent)) + .build(); + MimePart secondMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .addBodyContent(IOUtils.toInputStream("second")) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(firstMimePart) + .addChild(secondMimePart) + .build(); + + assertThat(mimePart.retrieveTextPlainBody()).contains(expectedContent); + } + + @Test + public void retrieveTextPlainBodyShouldReturnEmptyWhenMultipleAttachmentsAndNoTextContent() { + MimePart plainMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("plain") + .build(); + MimePart htmlMimePart = MimePart.builder() + .addMediaType("text") + .addSubType("html") + .addBodyContent(IOUtils.toInputStream("<b>content</b>")) + .build(); + + MimePart mimePart = MimePart.builder() + .addChild(htmlMimePart) + .addChild(plainMimePart) + .build(); + + assertThat(mimePart.retrieveTextPlainBody()).isEmpty(); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org