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 2015/06/29 10:25:02 UTC
svn commit: r1688115 - in /james/mailbox/trunk/elasticsearch: ./
src/main/java/org/apache/james/mailbox/elasticsearch/json/
Author: btellier
Date: Mon Jun 29 08:25:02 2015
New Revision: 1688115
URL: http://svn.apache.org/r1688115
Log:
MAILBOX-234 Provide mime parsing utilities - Patch I contributed with the help of Matthieu Baechlor
Added:
james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePart.java
james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartContainerBuilder.java
james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartParser.java
james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/RootMimePartContainerBuilder.java
Modified:
james/mailbox/trunk/elasticsearch/pom.xml
Modified: james/mailbox/trunk/elasticsearch/pom.xml
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/pom.xml?rev=1688115&r1=1688114&r2=1688115&view=diff
==============================================================================
--- james/mailbox/trunk/elasticsearch/pom.xml (original)
+++ james/mailbox/trunk/elasticsearch/pom.xml Mon Jun 29 08:25:02 2015
@@ -45,6 +45,12 @@
<groupId>${project.groupId}</groupId>
<artifactId>apache-james-mailbox-store</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>apache-james-mailbox-store</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePart.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePart.java?rev=1688115&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePart.java (added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePart.java Mon Jun 29 08:25:02 2015
@@ -0,0 +1,223 @@
+/****************************************************************
+ * 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.mailbox.elasticsearch.json;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mime4j.stream.Field;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+public class MimePart {
+
+ public static class Builder implements MimePartContainerBuilder {
+
+ private HeaderCollection.Builder headerCollectionBuilder;
+ private Optional<InputStream> bodyContent;
+ private List<MimePart> children;
+ private Optional<String> mediaType;
+ private Optional<String> subType;
+ private Optional<String> fileName;
+ private Optional<String> fileExtension;
+ private Optional<String> contentDisposition;
+
+
+ private Builder() {
+ children = Lists.newArrayList();
+ headerCollectionBuilder = HeaderCollection.builder();
+ this.bodyContent = Optional.empty();
+ this.mediaType = Optional.empty();
+ this.subType = Optional.empty();
+ this.fileName = Optional.empty();
+ this.fileExtension = Optional.empty();
+ this.contentDisposition = Optional.empty();
+ }
+
+ @Override
+ public Builder addToHeaders(Field field) {
+ headerCollectionBuilder.add(field);
+ return this;
+ }
+
+ @Override
+ public Builder addBodyContent(InputStream bodyContent) {
+ this.bodyContent = Optional.of(bodyContent);
+ return this;
+ }
+
+ @Override
+ public Builder addChild(MimePart mimePart) {
+ children.add(mimePart);
+ return this;
+ }
+
+ @Override
+ public Builder addFileName(String fileName) {
+ this.fileName = Optional.ofNullable(fileName);
+ this.fileExtension = this.fileName.map(FilenameUtils::getExtension);
+ return this;
+ }
+
+ @Override
+ public Builder addMediaType(String mediaType) {
+ this.mediaType = Optional.ofNullable(mediaType);
+ return this;
+ }
+
+ @Override
+ public Builder addSubType(String subType) {
+ this.subType = Optional.ofNullable(subType);
+ return this;
+ }
+
+ @Override
+ public Builder addContentDisposition(String contentDisposition) {
+ this.contentDisposition = Optional.ofNullable(contentDisposition);
+ return this;
+ }
+
+ @Override
+ public MimePart build() {
+ return new MimePart(
+ headerCollectionBuilder.build(),
+ decodeContent(),
+ mediaType,
+ subType,
+ fileName,
+ fileExtension,
+ contentDisposition,
+ children
+ );
+ }
+
+ private boolean isTextualMimePart() {
+ return mediaType.isPresent()
+ && mediaType.get().equalsIgnoreCase("text");
+ }
+
+ private Optional<String> decodeContent() {
+ if (bodyContent.isPresent() && isTextualMimePart()) {
+ try {
+ return Optional.of(IOUtils.toString(bodyContent.get()));
+ } catch (IOException e) {
+ LOGGER.warn("Can not decode body content", e);
+ }
+ }
+ return Optional.empty();
+ }
+
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MimePart.class);
+
+ private final HeaderCollection headerCollection;
+ private final Optional<String> bodyTextContent;
+ private final Optional<String> mediaType;
+ private final Optional<String> subType;
+ private final Optional<String> fileName;
+ private final Optional<String> fileExtension;
+ private final Optional<String> contentDisposition;
+ private final List<MimePart> attachments;
+
+ private MimePart(HeaderCollection headerCollection, Optional<String> bodyTextContent, Optional<String> mediaType,
+ Optional<String> subType, Optional<String> fileName, Optional<String> fileExtension,
+ Optional<String> contentDisposition, List<MimePart> attachments) {
+ this.headerCollection = headerCollection;
+ this.mediaType = mediaType;
+ this.subType = subType;
+ this.fileName = fileName;
+ this.fileExtension = fileExtension;
+ this.contentDisposition = contentDisposition;
+ this.attachments = attachments;
+ this.bodyTextContent = bodyTextContent;
+ }
+
+ @JsonIgnore
+ public List<MimePart> getAttachments() {
+ return attachments;
+ }
+
+ @JsonProperty(JsonMessageConstants.HEADERS)
+ public Multimap<String, String> getHeaders() {
+ return headerCollection.getHeaders();
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.FILENAME)
+ public Optional<String> getFileName() {
+ return fileName;
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.FILE_EXTENSION)
+ public Optional<String> getFileExtension() {
+ return fileExtension;
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.MEDIA_TYPE)
+ public Optional<String> getMediaType() {
+ return mediaType;
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.SUBTYPE)
+ public Optional<String> getSubType() {
+ return subType;
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.CONTENT_DISPOSITION)
+ public Optional<String> getContentDisposition() {
+ return contentDisposition;
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.TEXT_CONTENT)
+ public Optional<String> getTextualBody() {
+ return bodyTextContent;
+ }
+
+ @JsonIgnore
+ public Optional<String> locateFirstTextualBody() {
+ return Stream.concat(
+ Stream.of(this),
+ attachments.stream())
+ .map((mimePart) -> mimePart.bodyTextContent)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst();
+ }
+
+ @JsonIgnore
+ public Stream<MimePart> getAttachmentsStream() {
+ return attachments.stream()
+ .flatMap((mimePart) -> Stream.concat(Stream.of(mimePart), mimePart.getAttachmentsStream()));
+ }
+
+}
Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartContainerBuilder.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartContainerBuilder.java?rev=1688115&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartContainerBuilder.java (added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartContainerBuilder.java Mon Jun 29 08:25:02 2015
@@ -0,0 +1,44 @@
+/****************************************************************
+ * 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.mailbox.elasticsearch.json;
+
+import org.apache.james.mime4j.stream.Field;
+
+import java.io.InputStream;
+
+public interface MimePartContainerBuilder {
+
+ MimePart build();
+
+ MimePartContainerBuilder addToHeaders(Field field);
+
+ MimePartContainerBuilder addBodyContent(InputStream bodyContent);
+
+ MimePartContainerBuilder addChild(MimePart mimePart);
+
+ MimePartContainerBuilder addFileName(String fileName);
+
+ MimePartContainerBuilder addMediaType(String mediaType);
+
+ MimePartContainerBuilder addSubType(String subType);
+
+ MimePartContainerBuilder addContentDisposition(String contentDisposition);
+
+}
Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartParser.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartParser.java?rev=1688115&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartParser.java (added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/MimePartParser.java Mon Jun 29 08:25:02 2015
@@ -0,0 +1,119 @@
+/****************************************************************
+ * 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.mailbox.elasticsearch.json;
+
+import com.google.common.base.Preconditions;
+import org.apache.james.mailbox.store.mail.model.MailboxId;
+import org.apache.james.mailbox.store.mail.model.Message;
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.message.DefaultBodyDescriptorBuilder;
+import org.apache.james.mime4j.message.MaximalBodyDescriptor;
+import org.apache.james.mime4j.stream.EntityState;
+import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.mime4j.stream.MimeTokenStream;
+
+import java.io.IOException;
+import java.util.Deque;
+import java.util.LinkedList;
+
+public class MimePartParser {
+
+ private final Message<? extends MailboxId> message;
+ private final MimeTokenStream stream;
+ private final Deque<MimePartContainerBuilder> builderStack;
+ private MimePart result;
+ private MimePartContainerBuilder currentlyBuildMimePart;
+
+ public MimePartParser(Message<? extends MailboxId> message) {
+ this.message = message;
+ this.builderStack = new LinkedList<>();
+ this.currentlyBuildMimePart = new RootMimePartContainerBuilder();
+ this.stream = new MimeTokenStream(
+ MimeConfig.custom().setMaxLineLen(-1).setMaxHeaderLen(-1).build(),
+ new DefaultBodyDescriptorBuilder());
+ }
+
+ public MimePart parse() throws IOException, MimeException {
+ stream.parse(message.getFullContent());
+ for (EntityState state = stream.getState(); state != EntityState.T_END_OF_STREAM; state = stream.next()) {
+ processMimePart(stream, state);
+ }
+ return result;
+ }
+
+ private void processMimePart(MimeTokenStream stream, EntityState state) throws IOException {
+ switch (state) {
+ case T_START_MULTIPART:
+ case T_START_MESSAGE:
+ stackCurrent();
+ break;
+ case T_START_HEADER:
+ currentlyBuildMimePart = MimePart.builder();
+ break;
+ case T_FIELD:
+ currentlyBuildMimePart.addToHeaders(stream.getField());
+ break;
+ case T_BODY:
+ manageBodyExtraction(stream);
+ closeMimePart();
+ break;
+ case T_END_MULTIPART:
+ case T_END_MESSAGE:
+ unstackToCurrent();
+ closeMimePart();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void stackCurrent() {
+ builderStack.push(currentlyBuildMimePart);
+ currentlyBuildMimePart = null;
+ }
+
+ private void unstackToCurrent() {
+ currentlyBuildMimePart = builderStack.pop();
+ }
+
+ private void closeMimePart() {
+ MimePart bodyMimePart = currentlyBuildMimePart.build();
+ if (!builderStack.isEmpty()) {
+ builderStack.peek().addChild(bodyMimePart);
+ } else {
+ Preconditions.checkState(result == null);
+ result = bodyMimePart;
+ }
+ }
+
+ private void manageBodyExtraction(MimeTokenStream stream) throws IOException {
+ extractMimePartBodyDescription(stream);
+ currentlyBuildMimePart.addBodyContent(stream.getDecodedInputStream());
+ }
+
+ private void extractMimePartBodyDescription(MimeTokenStream stream) {
+ final MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) stream.getBodyDescriptor();
+ currentlyBuildMimePart.addMediaType(descriptor.getMediaType())
+ .addSubType(descriptor.getSubType())
+ .addContentDisposition(descriptor.getContentDispositionType())
+ .addFileName(descriptor.getContentDispositionFilename());
+ }
+
+}
Added: james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/RootMimePartContainerBuilder.java
URL: http://svn.apache.org/viewvc/james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/RootMimePartContainerBuilder.java?rev=1688115&view=auto
==============================================================================
--- james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/RootMimePartContainerBuilder.java (added)
+++ james/mailbox/trunk/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/RootMimePartContainerBuilder.java Mon Jun 29 08:25:02 2015
@@ -0,0 +1,84 @@
+/****************************************************************
+ * 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.mailbox.elasticsearch.json;
+
+import org.apache.james.mime4j.stream.Field;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+
+public class RootMimePartContainerBuilder implements MimePartContainerBuilder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RootMimePartContainerBuilder.class);
+
+ private MimePart rootMimePart;
+
+ @Override
+ public MimePart build() {
+ return rootMimePart;
+ }
+
+ @Override
+ public MimePartContainerBuilder addToHeaders(Field field) {
+ LOGGER.warn("Trying to add headers to the Root MimePart container");
+ return this;
+ }
+
+ @Override
+ public MimePartContainerBuilder addBodyContent(InputStream bodyContent) {
+ LOGGER.warn("Trying to add body content to the Root MimePart container");
+ return this;
+ }
+
+ @Override
+ public MimePartContainerBuilder addChild(MimePart mimePart) {
+ if (rootMimePart == null) {
+ rootMimePart = mimePart;
+ } else {
+ LOGGER.warn("Trying to add several children to the Root MimePart container");
+ }
+ return this;
+ }
+
+ @Override
+ public MimePartContainerBuilder addFileName(String fileName) {
+ LOGGER.warn("Trying to add fineName to the Root MimePart container");
+ return this;
+ }
+
+ @Override
+ public MimePartContainerBuilder addMediaType(String mediaType) {
+ LOGGER.warn("Trying to add media type to the Root MimePart container");
+ return this;
+ }
+
+ @Override
+ public MimePartContainerBuilder addSubType(String subType) {
+ LOGGER.warn("Trying to add sub type to the Root MimePart container");
+ return this;
+ }
+
+ @Override
+ public MimePartContainerBuilder addContentDisposition(String contentDisposition) {
+ LOGGER.warn("Trying to add content disposition to the Root MimePart container");
+ return this;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org