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 bt...@apache.org on 2017/04/10 01:35:48 UTC
james-project git commit: JAMES-1989 Move MessageContentExtractor to
james-server-util-java8
Repository: james-project
Updated Branches:
refs/heads/master fc7ded8b7 -> e1c486e24
JAMES-1989 Move MessageContentExtractor to james-server-util-java8
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/e1c486e2
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/e1c486e2
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/e1c486e2
Branch: refs/heads/master
Commit: e1c486e24728a32d78533626a78f78c55c3c92d6
Parents: fc7ded8
Author: Antoine Duprat <ad...@linagora.com>
Authored: Thu Apr 6 12:09:15 2017 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Apr 6 12:16:25 2017 +0200
----------------------------------------------------------------------
.../org/apache/james/jmap/JMAPCommonModule.java | 2 +-
server/container/util-java8/pom.xml | 16 +
.../util/mime/MessageContentExtractor.java | 226 +++++++++
.../util/mime/MessageContentExtractorTest.java | 491 +++++++++++++++++++
.../jmap/model/MessageContentExtractor.java | 226 ---------
.../apache/james/jmap/model/MessageFactory.java | 3 +-
.../jmap/methods/GetMessagesMethodTest.java | 2 +-
.../SetMessagesCreationProcessorTest.java | 2 +-
.../jmap/model/MessageContentExtractorTest.java | 490 ------------------
.../james/jmap/model/MessageFactoryTest.java | 1 +
.../apache/james/jmap/send/MailFactoryTest.java | 2 +-
11 files changed, 740 insertions(+), 721 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPCommonModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
index c9d7678..565301f 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
@@ -31,7 +31,6 @@ import org.apache.james.jmap.crypto.SignatureHandler;
import org.apache.james.jmap.crypto.SignedTokenFactory;
import org.apache.james.jmap.crypto.SignedTokenManager;
import org.apache.james.jmap.model.MailboxFactory;
-import org.apache.james.jmap.model.MessageContentExtractor;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessagePreviewGenerator;
import org.apache.james.jmap.send.MailFactory;
@@ -39,6 +38,7 @@ import org.apache.james.jmap.send.MailSpool;
import org.apache.james.jmap.utils.HeadersAuthenticationExtractor;
import org.apache.james.util.date.DefaultZonedDateTimeProvider;
import org.apache.james.util.date.ZonedDateTimeProvider;
+import org.apache.james.util.mime.MessageContentExtractor;
import org.apache.mailet.base.AutomaticallySentMailDetector;
import org.apache.mailet.base.AutomaticallySentMailDetectorImpl;
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/container/util-java8/pom.xml
----------------------------------------------------------------------
diff --git a/server/container/util-java8/pom.xml b/server/container/util-java8/pom.xml
index a9a455c..3640006 100644
--- a/server/container/util-java8/pom.xml
+++ b/server/container/util-java8/pom.xml
@@ -127,6 +127,14 @@
</activation>
<dependencies>
<dependency>
+ <groupId>org.apache.james</groupId>
+ <artifactId>apache-mime4j-dom</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.fge</groupId>
+ <artifactId>throwing-lambdas</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.github.steveash.guavate</groupId>
<artifactId>guavate</artifactId>
</dependency>
@@ -135,6 +143,14 @@
<artifactId>guava</artifactId>
</dependency>
<dependency>
+ <groupId>com.sun.mail</groupId>
+ <artifactId>javax.mail</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/container/util-java8/src/main/java/org/apache/james/util/mime/MessageContentExtractor.java
----------------------------------------------------------------------
diff --git a/server/container/util-java8/src/main/java/org/apache/james/util/mime/MessageContentExtractor.java b/server/container/util-java8/src/main/java/org/apache/james/util/mime/MessageContentExtractor.java
new file mode 100644
index 0000000..7f819aa
--- /dev/null
+++ b/server/container/util-java8/src/main/java/org/apache/james/util/mime/MessageContentExtractor.java
@@ -0,0 +1,226 @@
+/****************************************************************
+ * 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.util.mime;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mime4j.dom.Body;
+import org.apache.james.mime4j.dom.Entity;
+import org.apache.james.mime4j.dom.Multipart;
+import org.apache.james.mime4j.dom.TextBody;
+
+import com.github.fge.lambdas.Throwing;
+import com.github.fge.lambdas.functions.ThrowingFunction;
+import com.google.common.base.Charsets;
+
+public class MessageContentExtractor {
+
+ public static final String CONTENT_ID = "Content-ID";
+ public static final String MULTIPART_ALTERNATIVE = "multipart/alternative";
+ public static final String TEXT_HTML = "text/html";
+ public static final String TEXT_PLAIN = "text/plain";
+
+ public MessageContent extract(org.apache.james.mime4j.dom.Message message) throws IOException {
+ Body body = message.getBody();
+ if (body instanceof TextBody) {
+ return parseTextBody(message, (TextBody)body);
+ }
+ if (body instanceof Multipart){
+ return parseMultipart(message, (Multipart)body);
+ }
+ return MessageContent.empty();
+ }
+
+ private MessageContent parseTextBody(Entity entity, TextBody textBody) throws IOException {
+ Optional<String> bodyContent = asString(textBody);
+ if (TEXT_HTML.equals(entity.getMimeType())) {
+ return MessageContent.ofHtmlOnly(bodyContent);
+ }
+ return MessageContent.ofTextOnly(bodyContent);
+ }
+
+ private MessageContent parseMultipart(Entity entity, Multipart multipart) throws IOException {
+ MessageContent messageContent = parseMultipartContent(entity, multipart);
+ if (!messageContent.isEmpty()) {
+ return messageContent;
+ }
+ return parseFirstFoundMultipart(multipart);
+ }
+
+ private MessageContent parseMultipartContent(Entity entity, Multipart multipart) throws IOException {
+ switch(entity.getMimeType()) {
+ case MULTIPART_ALTERNATIVE:
+ return retrieveHtmlAndPlainTextContent(multipart);
+ default:
+ return retrieveFirstReadablePart(multipart);
+ }
+ }
+
+ private MessageContent parseFirstFoundMultipart(Multipart multipart) throws IOException {
+ ThrowingFunction<Entity, MessageContent> parseMultipart = firstPart -> parseMultipart(firstPart, (Multipart)firstPart.getBody());
+ return multipart.getBodyParts()
+ .stream()
+ .filter(part -> part.getBody() instanceof Multipart)
+ .findFirst()
+ .map(Throwing.function(parseMultipart).sneakyThrow())
+ .orElse(MessageContent.empty());
+ }
+
+ private Optional<String> asString(TextBody textBody) throws IOException {
+ return Optional.ofNullable(IOUtils.toString(textBody.getInputStream(), Charsets.UTF_8));
+ }
+
+ private MessageContent retrieveHtmlAndPlainTextContent(Multipart multipart) throws IOException {
+ Optional<String> textBody = getFirstMatchingTextBody(multipart, TEXT_PLAIN);
+ Optional<String> htmlBody = getFirstMatchingTextBody(multipart, TEXT_HTML);
+ MessageContent directChildTextBodies = new MessageContent(textBody, htmlBody);
+ if (!directChildTextBodies.isComplete()) {
+ MessageContent fromInnerMultipart = parseFirstFoundMultipart(multipart);
+ return directChildTextBodies.merge(fromInnerMultipart);
+ }
+ return directChildTextBodies;
+ }
+
+ private MessageContent retrieveFirstReadablePart(Multipart multipart) throws IOException {
+ return retrieveFirstReadablePartMatching(multipart, this::isNotAttachment)
+ .orElseGet(() -> retrieveFirstReadablePartMatching(multipart, this::isInlinedWithoutCid)
+ .orElse(MessageContent.empty()));
+ }
+
+ private Optional<MessageContent> retrieveFirstReadablePartMatching(Multipart multipart, Predicate<Entity> predicate) {
+ return multipart.getBodyParts()
+ .stream()
+ .filter(predicate)
+ .flatMap(Throwing.function(this::extractContentIfReadable).sneakyThrow())
+ .findFirst();
+ }
+
+ private Stream<MessageContent> extractContentIfReadable(Entity entity) throws IOException {
+ if (TEXT_HTML.equals(entity.getMimeType()) && entity.getBody() instanceof TextBody) {
+ return Stream.of(
+ MessageContent.ofHtmlOnly(asString((TextBody)entity.getBody())));
+ }
+ if (TEXT_PLAIN.equals(entity.getMimeType()) && entity.getBody() instanceof TextBody) {
+ return Stream.of(
+ MessageContent.ofTextOnly(asString((TextBody)entity.getBody())));
+ }
+ if (entity.isMultipart() && entity.getBody() instanceof Multipart) {
+ MessageContent innerMultipartContent = parseMultipart(entity, (Multipart)entity.getBody());
+ if (!innerMultipartContent.isEmpty()) {
+ return Stream.of(innerMultipartContent);
+ }
+ }
+ return Stream.empty();
+ }
+
+ private Optional<String> getFirstMatchingTextBody(Multipart multipart, String mimeType) throws IOException {
+ Optional<String> firstMatchingTextBody = getFirstMatchingTextBody(multipart, mimeType, this::isNotAttachment);
+ if (firstMatchingTextBody.isPresent()) {
+ return firstMatchingTextBody;
+ }
+ Optional<String> fallBackInlinedBodyWithoutCid = getFirstMatchingTextBody(multipart, mimeType, this::isInlinedWithoutCid);
+ return fallBackInlinedBodyWithoutCid;
+ }
+
+ private Optional<String> getFirstMatchingTextBody(Multipart multipart, String mimeType, Predicate<Entity> condition) {
+ Function<TextBody, Optional<String>> textBodyOptionalFunction = Throwing
+ .<TextBody, Optional<String>>function(textBody -> asString(textBody)).sneakyThrow();
+
+ return multipart.getBodyParts()
+ .stream()
+ .filter(part -> mimeType.equals(part.getMimeType()))
+ .filter(condition)
+ .map(Entity::getBody)
+ .filter(TextBody.class::isInstance)
+ .map(TextBody.class::cast)
+ .findFirst()
+ .flatMap(textBodyOptionalFunction);
+ }
+
+ private boolean isNotAttachment(Entity part) {
+ return part.getDispositionType() == null;
+ }
+
+ private boolean isInlinedWithoutCid(Entity part) {
+ return part.getDispositionType().equals(MimeMessage.INLINE) && part.getHeader().getField(CONTENT_ID) == null;
+ }
+
+ public static class MessageContent {
+ private final Optional<String> textBody;
+ private final Optional<String> htmlBody;
+
+ public MessageContent(Optional<String> textBody, Optional<String> htmlBody) {
+ this.textBody = textBody;
+ this.htmlBody = htmlBody;
+ }
+
+ public static MessageContent ofTextOnly(Optional<String> textBody) {
+ return new MessageContent(textBody, Optional.empty());
+ }
+
+ public static MessageContent ofHtmlOnly(Optional<String> htmlBody) {
+ return new MessageContent(Optional.empty(), htmlBody);
+ }
+
+ public static MessageContent empty() {
+ return new MessageContent(Optional.empty(), Optional.empty());
+ }
+
+ public Optional<String> getTextBody() {
+ return textBody;
+ }
+
+ public Optional<String> getHtmlBody() {
+ return htmlBody;
+ }
+
+ public boolean isEmpty() {
+ return equals(empty());
+ }
+
+ public boolean isComplete() {
+ return textBody.isPresent() && htmlBody.isPresent();
+ }
+
+ public MessageContent merge(MessageContent fromInnerMultipart) {
+ return new MessageContent(
+ textBody.map(Optional::of).orElse(fromInnerMultipart.getTextBody()),
+ htmlBody.map(Optional::of).orElse(fromInnerMultipart.getHtmlBody()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !(other instanceof MessageContent)) {
+ return false;
+ }
+ MessageContent otherMessageContent = (MessageContent)other;
+ return Objects.equals(this.textBody, otherMessageContent.textBody)
+ && Objects.equals(this.htmlBody, otherMessageContent.htmlBody);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/container/util-java8/src/test/java/org/apache/james/util/mime/MessageContentExtractorTest.java
----------------------------------------------------------------------
diff --git a/server/container/util-java8/src/test/java/org/apache/james/util/mime/MessageContentExtractorTest.java b/server/container/util-java8/src/test/java/org/apache/james/util/mime/MessageContentExtractorTest.java
new file mode 100644
index 0000000..9607564
--- /dev/null
+++ b/server/container/util-java8/src/test/java/org/apache/james/util/mime/MessageContentExtractorTest.java
@@ -0,0 +1,491 @@
+/****************************************************************
+ * 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.util.mime;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
+import org.apache.james.mime4j.field.Fields;
+import org.apache.james.mime4j.message.BasicBodyFactory;
+import org.apache.james.mime4j.message.BodyPart;
+import org.apache.james.mime4j.message.BodyPartBuilder;
+import org.apache.james.mime4j.message.HeaderImpl;
+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.util.ByteSequence;
+import org.apache.james.util.mime.MessageContentExtractor;
+import org.apache.james.util.mime.MessageContentExtractor.MessageContent;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+
+public class MessageContentExtractorTest {
+ private static final String BINARY_CONTENT = "binary";
+ private static final String TEXT_CONTENT = "text content";
+ private static final String HTML_CONTENT = "<b>html</b> content";
+ private static final String TEXT_CONTENT2 = "other text content";
+ private static final String HTML_CONTENT2 = "other <b>html</b> content";
+ private static final String ATTACHMENT_CONTENT = "attachment content";
+ private static final String ANY_VALUE = "anyValue";
+ private static final Field CONTENT_ID_FIELD = new Field() {
+ @Override
+ public String getName() {
+ return MessageContentExtractor.CONTENT_ID;
+ }
+
+ @Override
+ public String getBody() {
+ return ANY_VALUE;
+ }
+
+ @Override
+ public ByteSequence getRaw() {
+ return ByteSequence.EMPTY;
+ }
+ };
+
+ private MessageContentExtractor testee;
+
+ private BodyPart htmlPart;
+ private BodyPart textPart;
+ private BodyPart textAttachment;
+ private BodyPart inlineText;
+ private BodyPart inlineImage;
+
+ @Before
+ public void setup() throws IOException {
+ testee = new MessageContentExtractor();
+ textPart = BodyPartBuilder.create().setBody(TEXT_CONTENT, "plain", Charsets.UTF_8).build();
+ htmlPart = BodyPartBuilder.create().setBody(HTML_CONTENT, "html", Charsets.UTF_8).build();
+ textAttachment = BodyPartBuilder.create()
+ .setBody(ATTACHMENT_CONTENT, "plain", Charsets.UTF_8)
+ .setContentDisposition("attachment")
+ .build();
+ inlineText = BodyPartBuilder.create()
+ .setBody(ATTACHMENT_CONTENT, "plain", Charsets.UTF_8)
+ .setContentDisposition("inline")
+ .build();
+ inlineImage = BodyPartBuilder.create()
+ .setBody(new byte[0], "image/png")
+ .setContentDisposition("inline")
+ .build();
+ }
+
+ @Test
+ public void extractShouldReturnEmptyWhenBinaryContentOnly() throws IOException {
+ Message message = MessageBuilder.create()
+ .setBody(BasicBodyFactory.INSTANCE.binaryBody(BINARY_CONTENT, Charsets.UTF_8))
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).isEmpty();
+ assertThat(actual.getHtmlBody()).isEmpty();
+ }
+
+ @Test
+ public void extractShouldReturnTextOnlyWhenTextOnlyBody() throws IOException {
+ Message message = MessageBuilder.create()
+ .setBody(TEXT_CONTENT, Charsets.UTF_8)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ assertThat(actual.getHtmlBody()).isEmpty();
+ }
+
+ @Test
+ public void extractShouldReturnHtmlOnlyWhenHtmlOnlyBody() throws IOException {
+ Message message = MessageBuilder.create()
+ .setBody(HTML_CONTENT, "html", Charsets.UTF_8)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).isEmpty();
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldReturnHtmlAndTextWhenMultipartAlternative() throws IOException {
+ Multipart multipart = MultipartBuilder.create("alternative")
+ .addBodyPart(textPart)
+ .addBodyPart(htmlPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldReturnHtmlWhenMultipartAlternativeWithoutPlainPart() throws IOException {
+ Multipart multipart = MultipartBuilder.create("alternative")
+ .addBodyPart(htmlPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).isEmpty();
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldReturnTextWhenMultipartAlternativeWithoutHtmlPart() throws IOException {
+ Multipart multipart = MultipartBuilder.create("alternative")
+ .addBodyPart(textPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ assertThat(actual.getHtmlBody()).isEmpty();
+ }
+
+ @Test
+ public void extractShouldReturnFirstNonAttachmentPartWhenMultipartMixed() throws IOException {
+ Multipart multipart = MultipartBuilder.create("mixed")
+ .addBodyPart(textAttachment)
+ .addBodyPart(htmlPart)
+ .addBodyPart(textPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ assertThat(actual.getTextBody()).isEmpty();
+ }
+
+ @Test
+ public void extractShouldReturnInlinedTextBodyWithoutCIDWhenNoOtherValidParts() throws IOException {
+ String textBody = "body 1";
+ Multipart multipart = MultipartBuilder.create("report")
+ .addBodyPart(BodyPartBuilder.create()
+ .setBody(textBody, "plain", Charsets.UTF_8)
+ .setContentDisposition("inline")
+ .build())
+ .addBodyPart(BodyPartBuilder.create()
+ .setBody("body 2", "rfc822-headers", Charsets.UTF_8)
+ .setContentDisposition("inline")
+ .build())
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+
+ MessageContent actual = testee.extract(message);
+
+ assertThat(actual.getTextBody()).contains(textBody);
+ }
+
+ @Test
+ public void extractShouldReturnEmptyWhenMultipartMixedAndFirstPartIsATextAttachment() throws IOException {
+ Multipart multipart = MultipartBuilder.create("mixed")
+ .addBodyPart(textAttachment)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).isEmpty();
+ assertThat(actual.getHtmlBody()).isEmpty();
+ }
+
+ @Test
+ public void extractShouldReturnFirstPartOnlyWhenMultipartMixedAndFirstPartIsHtml() throws IOException {
+ Multipart multipart = MultipartBuilder.create("mixed")
+ .addBodyPart(htmlPart)
+ .addBodyPart(textPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).isEmpty();
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldReturnHtmlAndTextWhenMultipartMixedAndFirstPartIsMultipartAlternative() throws IOException {
+ BodyPart multipartAlternative = BodyPartBuilder.create()
+ .setBody(MultipartBuilder.create("alternative")
+ .addBodyPart(htmlPart)
+ .addBodyPart(textPart)
+ .build())
+ .build();
+ Multipart multipartMixed = MultipartBuilder.create("mixed")
+ .addBodyPart(multipartAlternative)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipartMixed)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldReturnHtmlWhenMultipartRelated() throws IOException {
+ Multipart multipart = MultipartBuilder.create("related")
+ .addBodyPart(htmlPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipart)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).isEmpty();
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldReturnHtmlAndTextWhenMultipartAlternativeAndFirstPartIsMultipartRelated() throws IOException {
+ BodyPart multipartRelated = BodyPartBuilder.create()
+ .setBody(MultipartBuilder.create("related")
+ .addBodyPart(htmlPart)
+ .build())
+ .build();
+ Multipart multipartAlternative = MultipartBuilder.create("alternative")
+ .addBodyPart(multipartRelated)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipartAlternative)
+ .build();
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldRetrieveHtmlBodyWithOneInlinedHTMLAttachmentWithoutCid() throws IOException {
+ //Given
+ BodyPart inlinedHTMLPart = BodyPartBuilder.create()
+ .setBody(HTML_CONTENT, "html", Charsets.UTF_8)
+ .build();
+ HeaderImpl inlinedHeader = new HeaderImpl();
+ inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
+ inlinedHeader.addField(Fields.contentType("text/html; charset=utf-8"));
+ inlinedHTMLPart.setHeader(inlinedHeader);
+ Multipart multipartAlternative = MultipartBuilder.create("alternative")
+ .addBodyPart(inlinedHTMLPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipartAlternative)
+ .build();
+
+ //When
+ MessageContent actual = testee.extract(message);
+
+ //Then
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldNotRetrieveHtmlBodyWithOneInlinedHTMLAttachmentWithCid() throws IOException {
+ //Given
+ BodyPart inlinedHTMLPart = BodyPartBuilder.create()
+ .setBody(HTML_CONTENT, "html", Charsets.UTF_8)
+ .build();
+ HeaderImpl inlinedHeader = new HeaderImpl();
+ inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
+ inlinedHeader.addField(Fields.contentType("text/html; charset=utf-8"));
+ inlinedHeader.addField(CONTENT_ID_FIELD);
+ inlinedHTMLPart.setHeader(inlinedHeader);
+ Multipart multipartAlternative = MultipartBuilder.create("alternative")
+ .addBodyPart(inlinedHTMLPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipartAlternative)
+ .build();
+
+ //When
+ MessageContent actual = testee.extract(message);
+
+ //Then
+ assertThat(actual.getHtmlBody()).isEmpty();
+ }
+
+
+ @Test
+ public void extractShouldRetrieveTextBodyWithOneInlinedTextAttachmentWithoutCid() throws IOException {
+ //Given
+ BodyPart inlinedTextPart = BodyPartBuilder.create()
+ .setBody(TEXT_CONTENT, "text", Charsets.UTF_8)
+ .build();
+ HeaderImpl inlinedHeader = new HeaderImpl();
+ inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
+ inlinedHeader.addField(Fields.contentType("text/plain; charset=utf-8"));
+ inlinedTextPart.setHeader(inlinedHeader);
+ Multipart multipartAlternative = MultipartBuilder.create("alternative")
+ .addBodyPart(inlinedTextPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipartAlternative)
+ .build();
+
+ //When
+ MessageContent actual = testee.extract(message);
+
+ //Then
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ }
+
+ @Test
+ public void extractShouldNotRetrieveTextBodyWithOneInlinedTextAttachmentWithCid() throws IOException {
+ //Given
+ BodyPart inlinedTextPart = BodyPartBuilder.create()
+ .setBody(TEXT_CONTENT, "text", Charsets.UTF_8)
+ .build();
+ HeaderImpl inlinedHeader = new HeaderImpl();
+ inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
+ inlinedHeader.addField(Fields.contentType("text/plain; charset=utf-8"));
+ inlinedHeader.addField(CONTENT_ID_FIELD);
+ inlinedTextPart.setHeader(inlinedHeader);
+ Multipart multipartAlternative = MultipartBuilder.create("alternative")
+ .addBodyPart(inlinedTextPart)
+ .build();
+ Message message = MessageBuilder.create()
+ .setBody(multipartAlternative)
+ .build();
+
+ //When
+ MessageContent actual = testee.extract(message);
+
+ //Then
+ assertThat(actual.getTextBody()).isEmpty();
+ }
+
+ @Test
+ public void extractShouldRetrieveTextAndHtmlBodyWhenOneInlinedTextAttachmentAndMainContentInMultipart() throws IOException {
+ BodyPart multipartAlternative = BodyPartBuilder.create()
+ .setBody(MultipartBuilder.create("alternative")
+ .addBodyPart(textPart)
+ .addBodyPart(htmlPart)
+ .build())
+ .build();
+
+ Multipart multipartMixed = MultipartBuilder.create("mixed")
+ .addBodyPart(multipartAlternative)
+ .addBodyPart(inlineText)
+ .build();
+
+ Message message = MessageBuilder.create()
+ .setBody(multipartMixed)
+ .build();
+
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void extractShouldRetrieveTextBodyAndHtmlBodyWhenTextBodyInMainMultipartAndHtmlBodyInInnerMultipart() throws IOException {
+ BodyPart multipartRelated = BodyPartBuilder.create()
+ .setBody(MultipartBuilder.create("related")
+ .addBodyPart(htmlPart)
+ .addBodyPart(inlineImage)
+ .build())
+ .build();
+
+ Multipart multipartAlternative = MultipartBuilder.create("alternative")
+ .addBodyPart(textPart)
+ .addBodyPart(multipartRelated)
+ .build();
+
+ Message message = MessageBuilder.create()
+ .setBody(multipartAlternative)
+ .build();
+
+ MessageContent actual = testee.extract(message);
+ assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
+ assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
+ }
+
+ @Test
+ public void mergeMessageContentShouldReturnEmptyWhenAllEmpty() {
+ MessageContent messageContent1 = MessageContent.empty();
+ MessageContent messageContent2 = MessageContent.empty();
+ MessageContent expected = MessageContent.empty();
+
+ MessageContent actual = messageContent1.merge(messageContent2);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void mergeMessageContentShouldReturnFirstWhenSecondEmpty() {
+ MessageContent messageContent1 = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
+ MessageContent messageContent2 = MessageContent.empty();
+ MessageContent expected = messageContent1;
+
+ MessageContent actual = messageContent1.merge(messageContent2);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void mergeMessageContentShouldReturnSecondWhenFirstEmpty() {
+ MessageContent messageContent1 = MessageContent.empty();
+ MessageContent messageContent2 = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
+ MessageContent expected = messageContent2;
+
+ MessageContent actual = messageContent1.merge(messageContent2);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void mergeMessageContentShouldReturnMixWhenFirstTextOnlyAndSecondHtmlOnly() {
+ MessageContent messageContent1 = MessageContent.ofTextOnly(Optional.of(TEXT_CONTENT));
+ MessageContent messageContent2 = MessageContent.ofHtmlOnly(Optional.of(HTML_CONTENT));
+ MessageContent expected = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
+
+ MessageContent actual = messageContent1.merge(messageContent2);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void mergeMessageContentShouldReturnMixWhenFirstHtmlOnlyAndSecondTextOnly() {
+ MessageContent messageContent1 = MessageContent.ofHtmlOnly(Optional.of(HTML_CONTENT));
+ MessageContent messageContent2 = MessageContent.ofTextOnly(Optional.of(TEXT_CONTENT));
+ MessageContent expected = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
+
+ MessageContent actual = messageContent1.merge(messageContent2);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void mergeMessageContentShouldReturnFirstWhenTwiceAreComplete() {
+ MessageContent messageContent1 = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
+ MessageContent messageContent2 = new MessageContent(Optional.of(TEXT_CONTENT2), Optional.of(HTML_CONTENT2));
+ MessageContent expected = messageContent1;
+
+ MessageContent actual = messageContent1.merge(messageContent2);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageContentExtractor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageContentExtractor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageContentExtractor.java
deleted file mode 100644
index 306f8c3..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageContentExtractor.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/****************************************************************
- * 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;
-
-import java.io.IOException;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-
-import javax.mail.internet.MimeMessage;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.james.mime4j.dom.Body;
-import org.apache.james.mime4j.dom.Entity;
-import org.apache.james.mime4j.dom.Multipart;
-import org.apache.james.mime4j.dom.TextBody;
-
-import com.github.fge.lambdas.Throwing;
-import com.github.fge.lambdas.functions.ThrowingFunction;
-import com.google.common.base.Charsets;
-
-public class MessageContentExtractor {
-
- public static final String CONTENT_ID = "Content-ID";
- public static final String MULTIPART_ALTERNATIVE = "multipart/alternative";
- public static final String TEXT_HTML = "text/html";
- public static final String TEXT_PLAIN = "text/plain";
-
- public MessageContent extract(org.apache.james.mime4j.dom.Message message) throws IOException {
- Body body = message.getBody();
- if (body instanceof TextBody) {
- return parseTextBody(message, (TextBody)body);
- }
- if (body instanceof Multipart){
- return parseMultipart(message, (Multipart)body);
- }
- return MessageContent.empty();
- }
-
- private MessageContent parseTextBody(Entity entity, TextBody textBody) throws IOException {
- Optional<String> bodyContent = asString(textBody);
- if (TEXT_HTML.equals(entity.getMimeType())) {
- return MessageContent.ofHtmlOnly(bodyContent);
- }
- return MessageContent.ofTextOnly(bodyContent);
- }
-
- private MessageContent parseMultipart(Entity entity, Multipart multipart) throws IOException {
- MessageContent messageContent = parseMultipartContent(entity, multipart);
- if (!messageContent.isEmpty()) {
- return messageContent;
- }
- return parseFirstFoundMultipart(multipart);
- }
-
- private MessageContent parseMultipartContent(Entity entity, Multipart multipart) throws IOException {
- switch(entity.getMimeType()) {
- case MULTIPART_ALTERNATIVE:
- return retrieveHtmlAndPlainTextContent(multipart);
- default:
- return retrieveFirstReadablePart(multipart);
- }
- }
-
- private MessageContent parseFirstFoundMultipart(Multipart multipart) throws IOException {
- ThrowingFunction<Entity, MessageContent> parseMultipart = firstPart -> parseMultipart(firstPart, (Multipart)firstPart.getBody());
- return multipart.getBodyParts()
- .stream()
- .filter(part -> part.getBody() instanceof Multipart)
- .findFirst()
- .map(Throwing.function(parseMultipart).sneakyThrow())
- .orElse(MessageContent.empty());
- }
-
- private Optional<String> asString(TextBody textBody) throws IOException {
- return Optional.ofNullable(IOUtils.toString(textBody.getInputStream(), Charsets.UTF_8));
- }
-
- private MessageContent retrieveHtmlAndPlainTextContent(Multipart multipart) throws IOException {
- Optional<String> textBody = getFirstMatchingTextBody(multipart, TEXT_PLAIN);
- Optional<String> htmlBody = getFirstMatchingTextBody(multipart, TEXT_HTML);
- MessageContent directChildTextBodies = new MessageContent(textBody, htmlBody);
- if (!directChildTextBodies.isComplete()) {
- MessageContent fromInnerMultipart = parseFirstFoundMultipart(multipart);
- return directChildTextBodies.merge(fromInnerMultipart);
- }
- return directChildTextBodies;
- }
-
- private MessageContent retrieveFirstReadablePart(Multipart multipart) throws IOException {
- return retrieveFirstReadablePartMatching(multipart, this::isNotAttachment)
- .orElseGet(() -> retrieveFirstReadablePartMatching(multipart, this::isInlinedWithoutCid)
- .orElse(MessageContent.empty()));
- }
-
- private Optional<MessageContent> retrieveFirstReadablePartMatching(Multipart multipart, Predicate<Entity> predicate) {
- return multipart.getBodyParts()
- .stream()
- .filter(predicate)
- .flatMap(Throwing.function(this::extractContentIfReadable).sneakyThrow())
- .findFirst();
- }
-
- private Stream<MessageContent> extractContentIfReadable(Entity entity) throws IOException {
- if (TEXT_HTML.equals(entity.getMimeType()) && entity.getBody() instanceof TextBody) {
- return Stream.of(
- MessageContent.ofHtmlOnly(asString((TextBody)entity.getBody())));
- }
- if (TEXT_PLAIN.equals(entity.getMimeType()) && entity.getBody() instanceof TextBody) {
- return Stream.of(
- MessageContent.ofTextOnly(asString((TextBody)entity.getBody())));
- }
- if (entity.isMultipart() && entity.getBody() instanceof Multipart) {
- MessageContent innerMultipartContent = parseMultipart(entity, (Multipart)entity.getBody());
- if (!innerMultipartContent.isEmpty()) {
- return Stream.of(innerMultipartContent);
- }
- }
- return Stream.empty();
- }
-
- private Optional<String> getFirstMatchingTextBody(Multipart multipart, String mimeType) throws IOException {
- Optional<String> firstMatchingTextBody = getFirstMatchingTextBody(multipart, mimeType, this::isNotAttachment);
- if (firstMatchingTextBody.isPresent()) {
- return firstMatchingTextBody;
- }
- Optional<String> fallBackInlinedBodyWithoutCid = getFirstMatchingTextBody(multipart, mimeType, this::isInlinedWithoutCid);
- return fallBackInlinedBodyWithoutCid;
- }
-
- private Optional<String> getFirstMatchingTextBody(Multipart multipart, String mimeType, Predicate<Entity> condition) {
- Function<TextBody, Optional<String>> textBodyOptionalFunction = Throwing
- .<TextBody, Optional<String>>function(textBody -> asString(textBody)).sneakyThrow();
-
- return multipart.getBodyParts()
- .stream()
- .filter(part -> mimeType.equals(part.getMimeType()))
- .filter(condition)
- .map(Entity::getBody)
- .filter(TextBody.class::isInstance)
- .map(TextBody.class::cast)
- .findFirst()
- .flatMap(textBodyOptionalFunction);
- }
-
- private boolean isNotAttachment(Entity part) {
- return part.getDispositionType() == null;
- }
-
- private boolean isInlinedWithoutCid(Entity part) {
- return part.getDispositionType().equals(MimeMessage.INLINE) && part.getHeader().getField(CONTENT_ID) == null;
- }
-
- public static class MessageContent {
- private final Optional<String> textBody;
- private final Optional<String> htmlBody;
-
- public MessageContent(Optional<String> textBody, Optional<String> htmlBody) {
- this.textBody = textBody;
- this.htmlBody = htmlBody;
- }
-
- public static MessageContent ofTextOnly(Optional<String> textBody) {
- return new MessageContent(textBody, Optional.empty());
- }
-
- public static MessageContent ofHtmlOnly(Optional<String> htmlBody) {
- return new MessageContent(Optional.empty(), htmlBody);
- }
-
- public static MessageContent empty() {
- return new MessageContent(Optional.empty(), Optional.empty());
- }
-
- public Optional<String> getTextBody() {
- return textBody;
- }
-
- public Optional<String> getHtmlBody() {
- return htmlBody;
- }
-
- public boolean isEmpty() {
- return equals(empty());
- }
-
- public boolean isComplete() {
- return textBody.isPresent() && htmlBody.isPresent();
- }
-
- public MessageContent merge(MessageContent fromInnerMultipart) {
- return new MessageContent(
- textBody.map(Optional::of).orElse(fromInnerMultipart.getTextBody()),
- htmlBody.map(Optional::of).orElse(fromInnerMultipart.getHtmlBody()));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == null || !(other instanceof MessageContent)) {
- return false;
- }
- MessageContent otherMessageContent = (MessageContent)other;
- return Objects.equals(this.textBody, otherMessageContent.textBody)
- && Objects.equals(this.htmlBody, otherMessageContent.htmlBody);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
index 53c9d7f..e7b1f2c 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
@@ -37,7 +37,6 @@ import javax.inject.Inject;
import javax.mail.Flags;
import javax.mail.internet.SharedInputStream;
-import org.apache.james.jmap.model.MessageContentExtractor.MessageContent;
import org.apache.james.jmap.utils.HtmlTextExtractor;
import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.exception.MailboxException;
@@ -52,6 +51,8 @@ import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.message.MessageBuilder;
import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.util.mime.MessageContentExtractor;
+import org.apache.james.util.mime.MessageContentExtractor.MessageContent;
import com.github.steveash.guavate.Guavate;
import com.google.common.base.Preconditions;
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
index 15873ee..db400db 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
@@ -37,7 +37,6 @@ import org.apache.james.jmap.model.ClientId;
import org.apache.james.jmap.model.GetMessagesRequest;
import org.apache.james.jmap.model.GetMessagesResponse;
import org.apache.james.jmap.model.Message;
-import org.apache.james.jmap.model.MessageContentExtractor;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessagePreviewGenerator;
import org.apache.james.jmap.model.MessageProperties.MessageProperty;
@@ -57,6 +56,7 @@ import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.tika.extractor.TikaTextExtractor;
import org.apache.james.metrics.logger.DefaultMetricFactory;
+import org.apache.james.util.mime.MessageContentExtractor;
import org.assertj.core.api.Condition;
import org.assertj.core.data.MapEntry;
import org.assertj.core.groups.Tuple;
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
index 6a0dd63..7a36140 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
@@ -42,7 +42,6 @@ import org.apache.james.jmap.model.BlobId;
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.jmap.model.MessageContentExtractor;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessagePreviewGenerator;
import org.apache.james.jmap.model.MessageProperties.MessageProperty;
@@ -68,6 +67,7 @@ import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.model.TestMessageId;
import org.apache.james.metrics.api.NoopMetricFactory;
+import org.apache.james.util.mime.MessageContentExtractor;
import org.apache.mailet.Mail;
import org.junit.Before;
import org.junit.Ignore;
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageContentExtractorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageContentExtractorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageContentExtractorTest.java
deleted file mode 100644
index f40b94f..0000000
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageContentExtractorTest.java
+++ /dev/null
@@ -1,490 +0,0 @@
-/****************************************************************
- * 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;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.IOException;
-import java.util.Optional;
-
-import javax.mail.internet.MimeMessage;
-
-import org.apache.james.jmap.model.MessageContentExtractor.MessageContent;
-import org.apache.james.mime4j.dom.Message;
-import org.apache.james.mime4j.dom.Multipart;
-import org.apache.james.mime4j.field.Fields;
-import org.apache.james.mime4j.message.BasicBodyFactory;
-import org.apache.james.mime4j.message.BodyPart;
-import org.apache.james.mime4j.message.BodyPartBuilder;
-import org.apache.james.mime4j.message.HeaderImpl;
-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.util.ByteSequence;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.base.Charsets;
-
-public class MessageContentExtractorTest {
- private static final String BINARY_CONTENT = "binary";
- private static final String TEXT_CONTENT = "text content";
- private static final String HTML_CONTENT = "<b>html</b> content";
- private static final String TEXT_CONTENT2 = "other text content";
- private static final String HTML_CONTENT2 = "other <b>html</b> content";
- private static final String ATTACHMENT_CONTENT = "attachment content";
- private static final String ANY_VALUE = "anyValue";
- private static final Field CONTENT_ID_FIELD = new Field() {
- @Override
- public String getName() {
- return MessageContentExtractor.CONTENT_ID;
- }
-
- @Override
- public String getBody() {
- return ANY_VALUE;
- }
-
- @Override
- public ByteSequence getRaw() {
- return ByteSequence.EMPTY;
- }
- };
-
- private MessageContentExtractor testee;
-
- private BodyPart htmlPart;
- private BodyPart textPart;
- private BodyPart textAttachment;
- private BodyPart inlineText;
- private BodyPart inlineImage;
-
- @Before
- public void setup() throws IOException {
- testee = new MessageContentExtractor();
- textPart = BodyPartBuilder.create().setBody(TEXT_CONTENT, "plain", Charsets.UTF_8).build();
- htmlPart = BodyPartBuilder.create().setBody(HTML_CONTENT, "html", Charsets.UTF_8).build();
- textAttachment = BodyPartBuilder.create()
- .setBody(ATTACHMENT_CONTENT, "plain", Charsets.UTF_8)
- .setContentDisposition("attachment")
- .build();
- inlineText = BodyPartBuilder.create()
- .setBody(ATTACHMENT_CONTENT, "plain", Charsets.UTF_8)
- .setContentDisposition("inline")
- .build();
- inlineImage = BodyPartBuilder.create()
- .setBody(new byte[0], "image/png")
- .setContentDisposition("inline")
- .build();
- }
-
- @Test
- public void extractShouldReturnEmptyWhenBinaryContentOnly() throws IOException {
- Message message = MessageBuilder.create()
- .setBody(BasicBodyFactory.INSTANCE.binaryBody(BINARY_CONTENT, Charsets.UTF_8))
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).isEmpty();
- assertThat(actual.getHtmlBody()).isEmpty();
- }
-
- @Test
- public void extractShouldReturnTextOnlyWhenTextOnlyBody() throws IOException {
- Message message = MessageBuilder.create()
- .setBody(TEXT_CONTENT, Charsets.UTF_8)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- assertThat(actual.getHtmlBody()).isEmpty();
- }
-
- @Test
- public void extractShouldReturnHtmlOnlyWhenHtmlOnlyBody() throws IOException {
- Message message = MessageBuilder.create()
- .setBody(HTML_CONTENT, "html", Charsets.UTF_8)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).isEmpty();
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldReturnHtmlAndTextWhenMultipartAlternative() throws IOException {
- Multipart multipart = MultipartBuilder.create("alternative")
- .addBodyPart(textPart)
- .addBodyPart(htmlPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldReturnHtmlWhenMultipartAlternativeWithoutPlainPart() throws IOException {
- Multipart multipart = MultipartBuilder.create("alternative")
- .addBodyPart(htmlPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).isEmpty();
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldReturnTextWhenMultipartAlternativeWithoutHtmlPart() throws IOException {
- Multipart multipart = MultipartBuilder.create("alternative")
- .addBodyPart(textPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- assertThat(actual.getHtmlBody()).isEmpty();
- }
-
- @Test
- public void extractShouldReturnFirstNonAttachmentPartWhenMultipartMixed() throws IOException {
- Multipart multipart = MultipartBuilder.create("mixed")
- .addBodyPart(textAttachment)
- .addBodyPart(htmlPart)
- .addBodyPart(textPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- assertThat(actual.getTextBody()).isEmpty();
- }
-
- @Test
- public void extractShouldReturnInlinedTextBodyWithoutCIDWhenNoOtherValidParts() throws IOException {
- String textBody = "body 1";
- Multipart multipart = MultipartBuilder.create("report")
- .addBodyPart(BodyPartBuilder.create()
- .setBody(textBody, "plain", Charsets.UTF_8)
- .setContentDisposition("inline")
- .build())
- .addBodyPart(BodyPartBuilder.create()
- .setBody("body 2", "rfc822-headers", Charsets.UTF_8)
- .setContentDisposition("inline")
- .build())
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
-
- MessageContent actual = testee.extract(message);
-
- assertThat(actual.getTextBody()).contains(textBody);
- }
-
- @Test
- public void extractShouldReturnEmptyWhenMultipartMixedAndFirstPartIsATextAttachment() throws IOException {
- Multipart multipart = MultipartBuilder.create("mixed")
- .addBodyPart(textAttachment)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).isEmpty();
- assertThat(actual.getHtmlBody()).isEmpty();
- }
-
- @Test
- public void extractShouldReturnFirstPartOnlyWhenMultipartMixedAndFirstPartIsHtml() throws IOException {
- Multipart multipart = MultipartBuilder.create("mixed")
- .addBodyPart(htmlPart)
- .addBodyPart(textPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).isEmpty();
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldReturnHtmlAndTextWhenMultipartMixedAndFirstPartIsMultipartAlternative() throws IOException {
- BodyPart multipartAlternative = BodyPartBuilder.create()
- .setBody(MultipartBuilder.create("alternative")
- .addBodyPart(htmlPart)
- .addBodyPart(textPart)
- .build())
- .build();
- Multipart multipartMixed = MultipartBuilder.create("mixed")
- .addBodyPart(multipartAlternative)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipartMixed)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldReturnHtmlWhenMultipartRelated() throws IOException {
- Multipart multipart = MultipartBuilder.create("related")
- .addBodyPart(htmlPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipart)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).isEmpty();
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldReturnHtmlAndTextWhenMultipartAlternativeAndFirstPartIsMultipartRelated() throws IOException {
- BodyPart multipartRelated = BodyPartBuilder.create()
- .setBody(MultipartBuilder.create("related")
- .addBodyPart(htmlPart)
- .build())
- .build();
- Multipart multipartAlternative = MultipartBuilder.create("alternative")
- .addBodyPart(multipartRelated)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipartAlternative)
- .build();
- MessageContent actual = testee.extract(message);
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldRetrieveHtmlBodyWithOneInlinedHTMLAttachmentWithoutCid() throws IOException {
- //Given
- BodyPart inlinedHTMLPart = BodyPartBuilder.create()
- .setBody(HTML_CONTENT, "html", Charsets.UTF_8)
- .build();
- HeaderImpl inlinedHeader = new HeaderImpl();
- inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
- inlinedHeader.addField(Fields.contentType("text/html; charset=utf-8"));
- inlinedHTMLPart.setHeader(inlinedHeader);
- Multipart multipartAlternative = MultipartBuilder.create("alternative")
- .addBodyPart(inlinedHTMLPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipartAlternative)
- .build();
-
- //When
- MessageContent actual = testee.extract(message);
-
- //Then
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldNotRetrieveHtmlBodyWithOneInlinedHTMLAttachmentWithCid() throws IOException {
- //Given
- BodyPart inlinedHTMLPart = BodyPartBuilder.create()
- .setBody(HTML_CONTENT, "html", Charsets.UTF_8)
- .build();
- HeaderImpl inlinedHeader = new HeaderImpl();
- inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
- inlinedHeader.addField(Fields.contentType("text/html; charset=utf-8"));
- inlinedHeader.addField(CONTENT_ID_FIELD);
- inlinedHTMLPart.setHeader(inlinedHeader);
- Multipart multipartAlternative = MultipartBuilder.create("alternative")
- .addBodyPart(inlinedHTMLPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipartAlternative)
- .build();
-
- //When
- MessageContent actual = testee.extract(message);
-
- //Then
- assertThat(actual.getHtmlBody()).isEmpty();
- }
-
-
- @Test
- public void extractShouldRetrieveTextBodyWithOneInlinedTextAttachmentWithoutCid() throws IOException {
- //Given
- BodyPart inlinedTextPart = BodyPartBuilder.create()
- .setBody(TEXT_CONTENT, "text", Charsets.UTF_8)
- .build();
- HeaderImpl inlinedHeader = new HeaderImpl();
- inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
- inlinedHeader.addField(Fields.contentType("text/plain; charset=utf-8"));
- inlinedTextPart.setHeader(inlinedHeader);
- Multipart multipartAlternative = MultipartBuilder.create("alternative")
- .addBodyPart(inlinedTextPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipartAlternative)
- .build();
-
- //When
- MessageContent actual = testee.extract(message);
-
- //Then
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- }
-
- @Test
- public void extractShouldNotRetrieveTextBodyWithOneInlinedTextAttachmentWithCid() throws IOException {
- //Given
- BodyPart inlinedTextPart = BodyPartBuilder.create()
- .setBody(TEXT_CONTENT, "text", Charsets.UTF_8)
- .build();
- HeaderImpl inlinedHeader = new HeaderImpl();
- inlinedHeader.addField(Fields.contentDisposition(MimeMessage.INLINE));
- inlinedHeader.addField(Fields.contentType("text/plain; charset=utf-8"));
- inlinedHeader.addField(CONTENT_ID_FIELD);
- inlinedTextPart.setHeader(inlinedHeader);
- Multipart multipartAlternative = MultipartBuilder.create("alternative")
- .addBodyPart(inlinedTextPart)
- .build();
- Message message = MessageBuilder.create()
- .setBody(multipartAlternative)
- .build();
-
- //When
- MessageContent actual = testee.extract(message);
-
- //Then
- assertThat(actual.getTextBody()).isEmpty();
- }
-
- @Test
- public void extractShouldRetrieveTextAndHtmlBodyWhenOneInlinedTextAttachmentAndMainContentInMultipart() throws IOException {
- BodyPart multipartAlternative = BodyPartBuilder.create()
- .setBody(MultipartBuilder.create("alternative")
- .addBodyPart(textPart)
- .addBodyPart(htmlPart)
- .build())
- .build();
-
- Multipart multipartMixed = MultipartBuilder.create("mixed")
- .addBodyPart(multipartAlternative)
- .addBodyPart(inlineText)
- .build();
-
- Message message = MessageBuilder.create()
- .setBody(multipartMixed)
- .build();
-
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void extractShouldRetrieveTextBodyAndHtmlBodyWhenTextBodyInMainMultipartAndHtmlBodyInInnerMultipart() throws IOException {
- BodyPart multipartRelated = BodyPartBuilder.create()
- .setBody(MultipartBuilder.create("related")
- .addBodyPart(htmlPart)
- .addBodyPart(inlineImage)
- .build())
- .build();
-
- Multipart multipartAlternative = MultipartBuilder.create("alternative")
- .addBodyPart(textPart)
- .addBodyPart(multipartRelated)
- .build();
-
- Message message = MessageBuilder.create()
- .setBody(multipartAlternative)
- .build();
-
- MessageContent actual = testee.extract(message);
- assertThat(actual.getTextBody()).contains(TEXT_CONTENT);
- assertThat(actual.getHtmlBody()).contains(HTML_CONTENT);
- }
-
- @Test
- public void mergeMessageContentShouldReturnEmptyWhenAllEmpty() {
- MessageContent messageContent1 = MessageContent.empty();
- MessageContent messageContent2 = MessageContent.empty();
- MessageContent expected = MessageContent.empty();
-
- MessageContent actual = messageContent1.merge(messageContent2);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void mergeMessageContentShouldReturnFirstWhenSecondEmpty() {
- MessageContent messageContent1 = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
- MessageContent messageContent2 = MessageContent.empty();
- MessageContent expected = messageContent1;
-
- MessageContent actual = messageContent1.merge(messageContent2);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void mergeMessageContentShouldReturnSecondWhenFirstEmpty() {
- MessageContent messageContent1 = MessageContent.empty();
- MessageContent messageContent2 = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
- MessageContent expected = messageContent2;
-
- MessageContent actual = messageContent1.merge(messageContent2);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void mergeMessageContentShouldReturnMixWhenFirstTextOnlyAndSecondHtmlOnly() {
- MessageContent messageContent1 = MessageContent.ofTextOnly(Optional.of(TEXT_CONTENT));
- MessageContent messageContent2 = MessageContent.ofHtmlOnly(Optional.of(HTML_CONTENT));
- MessageContent expected = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
-
- MessageContent actual = messageContent1.merge(messageContent2);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void mergeMessageContentShouldReturnMixWhenFirstHtmlOnlyAndSecondTextOnly() {
- MessageContent messageContent1 = MessageContent.ofHtmlOnly(Optional.of(HTML_CONTENT));
- MessageContent messageContent2 = MessageContent.ofTextOnly(Optional.of(TEXT_CONTENT));
- MessageContent expected = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
-
- MessageContent actual = messageContent1.merge(messageContent2);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void mergeMessageContentShouldReturnFirstWhenTwiceAreComplete() {
- MessageContent messageContent1 = new MessageContent(Optional.of(TEXT_CONTENT), Optional.of(HTML_CONTENT));
- MessageContent messageContent2 = new MessageContent(Optional.of(TEXT_CONTENT2), Optional.of(HTML_CONTENT2));
- MessageContent expected = messageContent1;
-
- MessageContent actual = messageContent1.merge(messageContent2);
-
- assertThat(actual).isEqualTo(expected);
- }
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
index 1e0130c..af0abd2 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
@@ -41,6 +41,7 @@ import org.apache.james.mailbox.model.Cid;
import org.apache.james.mailbox.model.MessageAttachment;
import org.apache.james.mailbox.model.TestMessageId;
import org.apache.james.mailbox.tika.extractor.TikaTextExtractor;
+import org.apache.james.util.mime.MessageContentExtractor;
import org.junit.Before;
import org.junit.Test;
http://git-wip-us.apache.org/repos/asf/james-project/blob/e1c486e2/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
index c45574c..12a7ebc 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
@@ -31,7 +31,6 @@ import javax.mail.Flags;
import javax.mail.util.SharedByteArrayInputStream;
import org.apache.james.jmap.model.Message;
-import org.apache.james.jmap.model.MessageContentExtractor;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent;
import org.apache.james.jmap.model.MessagePreviewGenerator;
@@ -41,6 +40,7 @@ import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.inmemory.InMemoryId;
import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.util.mime.MessageContentExtractor;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.junit.Before;
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org