You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2016/07/07 07:39:58 UTC
[2/2] james-project git commit: JAMES-1793 Generate MultiPart
response when mixed text and html content provided
JAMES-1793 Generate MultiPart response when mixed text and html content provided
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/812b9129
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/812b9129
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/812b9129
Branch: refs/heads/master
Commit: 812b9129f8d09ade820e8d966285889ab85c8c28
Parents: 16c2593
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Wed Jul 6 18:45:31 2016 +0200
Committer: Raphael Ouazana <ra...@linagora.com>
Committed: Wed Jul 6 18:45:31 2016 +0200
----------------------------------------------------------------------
.../integration/SetMessagesMethodTest.java | 62 ++++++++++++++++++++
.../jmap/methods/MIMEMessageConverter.java | 50 ++++++++++++++--
.../jmap/methods/MIMEMessageConverterTest.java | 48 +++++++++++++--
3 files changed, 150 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/812b9129/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index 1b6dab5..f6c18b1 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -1355,6 +1355,68 @@ public abstract class SetMessagesMethodTest {
}
@Test
+ public void setMessagesShouldSendAReadableTextPlusHtmlMessage() throws Exception {
+ // Sender
+ jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
+ // Recipient
+ String recipientAddress = "recipient" + "@" + USERS_DOMAIN;
+ String password = "password";
+ jmapServer.serverProbe().addUser(recipientAddress, password);
+ jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, recipientAddress, "inbox");
+ await();
+ AccessToken recipientToken = JmapAuthentication.authenticateJamesUser(recipientAddress, password);
+
+ String messageCreationId = "user|inbox|1";
+ String fromAddress = username;
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"" + recipientAddress + "\"}]," +
+ " \"subject\": \"Thank you for joining example.com!\"," +
+ " \"htmlBody\": \"Hello <b>someone</b>, and thank you for joining example.com!\"," +
+ " \"textBody\": \"Hello someone, and thank you for joining example.com, text version!\"," +
+ " \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ // Given
+ given()
+ .header("Authorization", this.accessToken.serialize())
+ .body(requestBody)
+ // When
+ .when()
+ .post("/jmap");
+
+ // Then
+ calmlyAwait.atMost(30, TimeUnit.SECONDS).until( () -> isTextPlusHtmlMessageReceived(recipientToken));
+ }
+
+ private boolean isTextPlusHtmlMessageReceived(AccessToken recipientToken) {
+ try {
+ with()
+ .header("Authorization", recipientToken.serialize())
+ .body("[[\"getMessageList\", {\"fetchMessages\": true, \"fetchMessageProperties\": [\"htmlBody\", \"textBody\"]}, \"#0\"]]")
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(SECOND_NAME, equalTo("messages"))
+ .body(SECOND_ARGUMENTS + ".list", hasSize(1))
+ .body(SECOND_ARGUMENTS + ".list[0].htmlBody", equalTo("Hello <b>someone</b>, and thank you for joining example.com!"))
+ .body(SECOND_ARGUMENTS + ".list[0].textBody", equalTo("Hello someone, and thank you for joining example.com, text version!"))
+ ;
+ return true;
+ } catch(AssertionError e) {
+ return false;
+ }
+ }
+
+ @Test
public void movingAMessageIsNotSupported() throws Exception {
String newMailboxName = "heartFolder";
jmapServer.serverProbe().createMailbox("#private", username, newMailboxName);
http://git-wip-us.apache.org/repos/asf/james-project/blob/812b9129/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
index 0240ccc..19e7c63 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
@@ -27,7 +27,6 @@ import java.util.TimeZone;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-import org.apache.commons.lang.NotImplementedException;
import org.apache.james.jmap.model.CreationMessage;
import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
import org.apache.james.jmap.model.CreationMessageId;
@@ -35,21 +34,33 @@ import org.apache.james.mime4j.Charsets;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.dom.FieldParser;
import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.TextBody;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.dom.field.UnstructuredField;
import org.apache.james.mime4j.field.UnstructuredFieldImpl;
import org.apache.james.mime4j.message.BasicBodyFactory;
+import org.apache.james.mime4j.message.BodyPartBuilder;
import org.apache.james.mime4j.message.DefaultMessageWriter;
import org.apache.james.mime4j.message.MessageBuilder;
+import org.apache.james.mime4j.message.MultipartBuilder;
import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.NameValuePair;
import org.apache.james.mime4j.stream.RawField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
+import com.google.common.net.MediaType;
public class MIMEMessageConverter {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MIMEMessageConverter.class);
+
+ private static final String PLAIN_TEXT_MEDIA_TYPE = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
+ private static final String HTML_MEDIA_TYPE = MediaType.HTML_UTF_8.withoutParameters().toString();
+ private static final NameValuePair UTF_8_CHARSET = new NameValuePair("charset", Charsets.UTF_8.name());
+ private static final String MIXED_SUB_TYPE = "mixed";
private final BasicBodyFactory bodyFactory;
@@ -75,7 +86,11 @@ public class MIMEMessageConverter {
}
MessageBuilder messageBuilder = MessageBuilder.create();
- messageBuilder.setBody(createTextBody(creationMessageEntry.getMessage()));
+ if (mixedTextAndHtml(creationMessageEntry.getMessage())) {
+ messageBuilder.setBody(createMultipartBody(creationMessageEntry.getMessage()));
+ } else {
+ messageBuilder.setBody(createTextBody(creationMessageEntry.getMessage()));
+ }
buildMimeHeaders(messageBuilder, creationMessageEntry.getCreationId(), creationMessageEntry.getMessage());
return messageBuilder.build();
}
@@ -106,7 +121,9 @@ public class MIMEMessageConverter {
// note that date conversion probably lose milliseconds!
messageBuilder.setDate(Date.from(newMessage.getDate().toInstant()), TimeZone.getTimeZone(newMessage.getDate().getZone()));
newMessage.getInReplyToMessageId().ifPresent(addInReplyToHeader(messageBuilder::addField));
- newMessage.getHtmlBody().ifPresent(x -> messageBuilder.setContentType("text/html", new NameValuePair("charset", "utf-8")));
+ if (!mixedTextAndHtml(newMessage)) {
+ newMessage.getHtmlBody().ifPresent(x -> messageBuilder.setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET));
+ }
}
private Consumer<String> addInReplyToHeader(Consumer<Field> headerAppender) {
@@ -117,16 +134,37 @@ public class MIMEMessageConverter {
};
}
+ private boolean mixedTextAndHtml(CreationMessage newMessage) {
+ return newMessage.getTextBody().isPresent() && newMessage.getHtmlBody().isPresent();
+ }
+
private TextBody createTextBody(CreationMessage newMessage) {
- if (newMessage.getTextBody().isPresent() && newMessage.getHtmlBody().isPresent()) {
- throw new NotImplementedException("Converter can't handle yet htmlBody and textBody in the same message");
- }
String body = newMessage.getHtmlBody()
.orElse(newMessage.getTextBody()
.orElse(""));
return bodyFactory.textBody(body, Charsets.UTF_8);
}
+ private Multipart createMultipartBody(CreationMessage newMessage) {
+ try {
+ return MultipartBuilder.create(MIXED_SUB_TYPE)
+ .addBodyPart(BodyPartBuilder.create()
+ .use(bodyFactory)
+ .setBody(newMessage.getTextBody().get(), Charsets.UTF_8)
+ .setContentType(PLAIN_TEXT_MEDIA_TYPE, UTF_8_CHARSET)
+ .build())
+ .addBodyPart(BodyPartBuilder.create()
+ .use(bodyFactory)
+ .setBody(newMessage.getHtmlBody().get(), Charsets.UTF_8)
+ .setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET)
+ .build())
+ .build();
+ } catch (IOException e) {
+ LOGGER.error("Error while creating textBody \n"+ newMessage.getTextBody().get() +"\n or htmlBody \n" + newMessage.getHtmlBody().get(), e);
+ throw Throwables.propagate(e);
+ }
+ }
+
private Mailbox convertEmailToMimeHeader(DraftEmailer address) {
if (!address.hasValidEmail()) {
throw new IllegalArgumentException("address");
http://git-wip-us.apache.org/repos/asf/james-project/blob/812b9129/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
index c9fb01d..c2cf064 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
@@ -26,12 +26,12 @@ import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
-import org.apache.commons.lang.NotImplementedException;
import org.apache.james.jmap.model.CreationMessage;
import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
import org.apache.james.jmap.model.CreationMessageId;
import org.apache.james.mime4j.Charsets;
import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.TextBody;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.message.BasicBodyFactory;
@@ -176,8 +176,8 @@ public class MIMEMessageConverterTest {
assertThat(result.getBody()).isEqualToComparingOnlyGivenFields(expected, "content", "charset");
}
- @Test(expected=NotImplementedException.class)
- public void convertToMimeShouldFailWhenHtmlBodyAndTxtBodyProvided() {
+ @Test
+ public void convertToMimeShouldGenerateMultipartWhenHtmlBodyAndTextBodyProvided() throws Exception {
// Given
MIMEMessageConverter sut = new MIMEMessageConverter();
@@ -190,8 +190,48 @@ public class MIMEMessageConverterTest {
.build();
// When
- sut.convertToMime(new MessageWithId.CreationMessageEntry(
+ Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
CreationMessageId.of("user|mailbox|1"), testMessage));
+
+ // Then
+ assertThat(result.getBody()).isInstanceOf(Multipart.class);
+ assertThat(result.isMultipart()).isTrue();
+ Multipart typedResult = (Multipart)result.getBody();
+ assertThat(typedResult.getBodyParts()).hasSize(2);
+ }
+
+ @Test
+ public void convertShouldGenerateExpectedMultipartWhenHtmlAndTextBodyProvided() throws Exception {
+ // Given
+ MIMEMessageConverter sut = new MIMEMessageConverter();
+
+ CreationMessage testMessage = CreationMessage.builder()
+ .mailboxIds(ImmutableList.of("dead-bada55"))
+ .subject("subject")
+ .from(DraftEmailer.builder().name("sender").build())
+ .textBody("Hello all!")
+ .htmlBody("Hello <b>all</b>!")
+ .build();
+
+ String expectedHeaders = "MIME-Version: 1.0\r\n" +
+ "Content-Type: multipart/mixed;\r\n" +
+ " boundary=\"-=Part.0.";
+ String expectedPart1 = "Content-Type: text/plain; charset=UTF-8\r\n" +
+ "\r\n" +
+ "Hello all!\r\n";
+ String expectedPart2 = "Content-Type: text/html; charset=UTF-8\r\n" +
+ "\r\n" +
+ "Hello <b>all</b>!\r\n";
+
+ // When
+ byte[] convert = sut.convert(new MessageWithId.CreationMessageEntry(
+ CreationMessageId.of("user|mailbox|1"), testMessage));
+
+ // Then
+ String actual = new String(convert, Charsets.UTF_8);
+ assertThat(actual).startsWith(expectedHeaders);
+ assertThat(actual).contains(expectedPart1);
+ assertThat(actual).contains(expectedPart2);
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org