Author: matthieu
Date: Fri Dec 11 12:34:26 2015
New Revision: 1719389
URL: http://svn.apache.org/viewvc?rev=1719389&view=rev
Log:
JAMES-1644 Add JMAP Message objects
duped classes from backend-elasticsearch
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Attachment.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Emailer.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SubMessage.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/DateResolutionFormater.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/EMailer.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/HeaderCollection.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/JsonMessageConstants.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MessageUpdateJson.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartContainerBuilder.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartParser.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/RootMimePartContainerBuilder.java
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/AttachmentTest.java
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/EmailerTest.java
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SubMessageTest.java
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Attachment.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Attachment.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Attachment.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Attachment.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,148 @@
+/****************************************************************
+ * 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.util.Optional;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+@JsonDeserialize(builder = Attachment.Builder.class)
+public class Attachment {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @JsonPOJOBuilder(withPrefix = "")
+ public static class Builder {
+ private String blobId;
+ private String type;
+ private String name;
+ private Long size;
+ private String cid;
+ private boolean isInline;
+ private Long width;
+ private Long height;
+
+ public Builder blobId(String blobId) {
+ this.blobId = blobId;
+ return this;
+ }
+
+ public Builder type(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder size(long size) {
+ this.size = size;
+ return this;
+ }
+
+ public Builder cid(String cid) {
+ this.cid = cid;
+ return this;
+ }
+
+ public Builder isInline(boolean isInline) {
+ this.isInline = isInline;
+ return this;
+ }
+
+ public Builder width(long width) {
+ this.width = width;
+ return this;
+ }
+
+ public Builder height(long height) {
+ this.height = height;
+ return this;
+ }
+
+ public Attachment build() {
+ Preconditions.checkState(!Strings.isNullOrEmpty(blobId), "'blobId'
is mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(type), "'type' is
mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(name), "'name' is
mandatory");
+ Preconditions.checkState(size != null, "'size' is mandatory");
+ return new Attachment(blobId, type, name, size,
Optional.ofNullable(cid), isInline, Optional.ofNullable(width),
Optional.ofNullable(height));
+ }
+ }
+
+ private final String blobId;
+ private final String type;
+ private final String name;
+ private final long size;
+ private final Optional<String> cid;
+ private final boolean isInline;
+ private final Optional<Long> width;
+ private final Optional<Long> height;
+
+ @VisibleForTesting Attachment(String blobId, String type, String name,
long size, Optional<String> cid, boolean isInline, Optional<Long> width,
Optional<Long> height) {
+ this.blobId = blobId;
+ this.type = type;
+ this.name = name;
+ this.size = size;
+ this.cid = cid;
+ this.isInline = isInline;
+ this.width = width;
+ this.height = height;
+ }
+
+ public String getBlobId() {
+ return blobId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public Optional<String> getCid() {
+ return cid;
+ }
+
+ public boolean isInline() {
+ return isInline;
+ }
+
+ public Optional<Long> getWidth() {
+ return width;
+ }
+
+ public Optional<Long> getHeight() {
+ return height;
+ }
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Emailer.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Emailer.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Emailer.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Emailer.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,73 @@
+/****************************************************************
+ * 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 com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+@JsonDeserialize(builder = Emailer.Builder.class)
+public class Emailer {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @JsonPOJOBuilder(withPrefix = "")
+ public static class Builder {
+ private String name;
+ private String email;
+
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder email(String email) {
+ this.email = email;
+ return this;
+ }
+
+ public Emailer build() {
+ Preconditions.checkState(!Strings.isNullOrEmpty(name), "'name' is
mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(email), "'email'
is mandatory");
+ Preconditions.checkState(email.contains("@"), "'email' must
contain '@' character");
+ return new Emailer(name, email);
+ }
+ }
+
+ private final String name;
+ private final String email;
+
+ @VisibleForTesting Emailer(String name, String email) {
+ this.name = name;
+ this.email = email;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,372 @@
+/****************************************************************
+ * 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.time.ZonedDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+@JsonDeserialize(builder = Message.Builder.class)
+public class Message {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @JsonPOJOBuilder(withPrefix = "")
+ public static class Builder {
+ private String id;
+ private String blobId;
+ private String threadId;
+ private ImmutableList<String> mailboxIds;
+ private String inReplyToMessageId;
+ private boolean isUnread;
+ private boolean isFlagged;
+ private boolean isAnswered;
+ private boolean isDraft;
+ private boolean hasAttachment;
+ private ImmutableMap<String, String> headers;
+ private Emailer from;
+ private ImmutableList.Builder<Emailer> to;
+ private ImmutableList.Builder<Emailer> cc;
+ private ImmutableList.Builder<Emailer> bcc;
+ private ImmutableList.Builder<Emailer> replyTo;
+ private String subject;
+ private ZonedDateTime date;
+ private Long size;
+ private String preview;
+ private String textBody;
+ private String htmlBody;
+ private ImmutableList.Builder<Attachment> attachments;
+ private ImmutableMap.Builder<String, SubMessage> attachedMessages;
+
+ private Builder() {
+ to = ImmutableList.builder();
+ cc = ImmutableList.builder();
+ bcc = ImmutableList.builder();
+ replyTo = ImmutableList.builder();
+ attachments = ImmutableList.builder();
+ attachedMessages = ImmutableMap.builder();
+ }
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder blobId(String blobId) {
+ this.blobId = blobId;
+ return this;
+ }
+
+ public Builder threadId(String threadId) {
+ this.threadId = threadId;
+ return this;
+ }
+
+ public Builder mailboxIds(ImmutableList<String> mailboxIds) {
+ this.mailboxIds = mailboxIds;
+ return this;
+ }
+
+ public Builder inReplyToMessageId(String inReplyToMessageId) {
+ this.inReplyToMessageId = inReplyToMessageId;
+ return this;
+ }
+
+ public Builder isUnread(boolean isUnread) {
+ this.isUnread = isUnread;
+ return this;
+ }
+
+ public Builder isFlagged(boolean isFlagged) {
+ this.isFlagged = isFlagged;
+ return this;
+ }
+
+ public Builder isAnswered(boolean isAnswered) {
+ this.isAnswered = isAnswered;
+ return this;
+ }
+
+ public Builder isDraft(boolean isDraft) {
+ this.isDraft = isDraft;
+ return this;
+ }
+
+ public Builder hasAttachment(boolean hasAttachment) {
+ this.hasAttachment = hasAttachment;
+ return this;
+ }
+
+ public Builder headers(ImmutableMap<String, String> headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ public Builder from(Emailer from) {
+ this.from = from;
+ return this;
+ }
+
+ public Builder to(List<Emailer> to) {
+ this.to.addAll(to);
+ return this;
+ }
+
+ public Builder cc(List<Emailer> cc) {
+ this.cc.addAll(cc);
+ return this;
+ }
+
+ public Builder bcc(List<Emailer> bcc) {
+ this.bcc.addAll(bcc);
+ return this;
+ }
+
+ public Builder replyTo(List<Emailer> replyTo) {
+ this.replyTo.addAll(replyTo);
+ return this;
+ }
+
+ public Builder subject(String subject) {
+ this.subject = subject;
+ return this;
+ }
+
+ public Builder date(ZonedDateTime date) {
+ this.date = date;
+ return this;
+ }
+
+ public Builder size(long size) {
+ this.size = size;
+ return this;
+ }
+
+ public Builder preview(String preview) {
+ this.preview = preview;
+ return this;
+ }
+
+ public Builder textBody(String textBody) {
+ this.textBody = textBody;
+ return this;
+ }
+
+ public Builder htmlBody(String htmlBody) {
+ this.htmlBody = htmlBody;
+ return this;
+ }
+
+ public Builder attachments(List<Attachment> attachments) {
+ this.attachments.addAll(attachments);
+ return this;
+ }
+
+ public Builder attachedMessages(Map<String, SubMessage>
attachedMessages) {
+ this.attachedMessages.putAll(attachedMessages);
+ return this;
+ }
+
+ public Message build() {
+ Preconditions.checkState(!Strings.isNullOrEmpty(id), "'id' is
mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(blobId), "'blobId'
is mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(threadId),
"'threadId' is mandatory");
+ Preconditions.checkState(mailboxIds != null, "'mailboxIds' is
mandatory");
+ Preconditions.checkState(headers != null, "'headers' is
mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(subject),
"'subject' is mandatory");
+ Preconditions.checkState(size != null, "'size' is mandatory");
+ Preconditions.checkState(date != null, "'date' is mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(preview),
"'preview' is mandatory");
+ ImmutableList<Attachment> attachments = this.attachments.build();
+ ImmutableMap<String, SubMessage> attachedMessages =
this.attachedMessages.build();
+
Preconditions.checkState(areAttachedMessagesKeysInAttachments(attachments,
attachedMessages), "'attachedMessages' keys must be in 'attachements'");
+
+ return new Message(id, blobId, threadId, mailboxIds,
Optional.ofNullable(inReplyToMessageId), isUnread, isFlagged, isAnswered,
isDraft, hasAttachment, headers, Optional.ofNullable(from),
+ to.build(), cc.build(), bcc.build(), replyTo.build(),
subject, date, size, preview, Optional.ofNullable(textBody),
Optional.ofNullable(htmlBody), attachments, attachedMessages);
+ }
+ }
+
+ protected static boolean
areAttachedMessagesKeysInAttachments(ImmutableList<Attachment> attachments,
ImmutableMap<String, SubMessage> attachedMessages) {
+ return attachments.stream()
+ .map(Attachment::getBlobId)
+ .allMatch(attachedMessages::containsKey);
+ }
+
+ private final String id;
+ private final String blobId;
+ private final String threadId;
+ private final ImmutableList<String> mailboxIds;
+ private final Optional<String> inReplyToMessageId;
+ private final boolean isUnread;
+ private final boolean isFlagged;
+ private final boolean isAnswered;
+ private final boolean isDraft;
+ private final boolean hasAttachment;
+ private final ImmutableMap<String, String> headers;
+ private final Optional<Emailer> from;
+ private final ImmutableList<Emailer> to;
+ private final ImmutableList<Emailer> cc;
+ private final ImmutableList<Emailer> bcc;
+ private final ImmutableList<Emailer> replyTo;
+ private final String subject;
+ private final ZonedDateTime date;
+ private final long size;
+ private final String preview;
+ private final Optional<String> textBody;
+ private final Optional<String> htmlBody;
+ private final ImmutableList<Attachment> attachments;
+ private final ImmutableMap<String, SubMessage> attachedMessages;
+
+ @VisibleForTesting Message(String id, String blobId, String threadId,
ImmutableList<String> mailboxIds, Optional<String> inReplyToMessageId, boolean
isUnread, boolean isFlagged, boolean isAnswered, boolean isDraft, boolean
hasAttachment, ImmutableMap<String, String> headers, Optional<Emailer> from,
+ ImmutableList<Emailer> to, ImmutableList<Emailer> cc,
ImmutableList<Emailer> bcc, ImmutableList<Emailer> replyTo, String subject,
ZonedDateTime date, long size, String preview, Optional<String> textBody,
Optional<String> htmlBody, ImmutableList<Attachment> attachments,
+ ImmutableMap<String, SubMessage> attachedMessages) {
+ this.id = id;
+ this.blobId = blobId;
+ this.threadId = threadId;
+ this.mailboxIds = mailboxIds;
+ this.inReplyToMessageId = inReplyToMessageId;
+ this.isUnread = isUnread;
+ this.isFlagged = isFlagged;
+ this.isAnswered = isAnswered;
+ this.isDraft = isDraft;
+ this.hasAttachment = hasAttachment;
+ this.headers = headers;
+ this.from = from;
+ this.to = to;
+ this.cc = cc;
+ this.bcc = bcc;
+ this.replyTo = replyTo;
+ this.subject = subject;
+ this.date = date;
+ this.size = size;
+ this.preview = preview;
+ this.textBody = textBody;
+ this.htmlBody = htmlBody;
+ this.attachments = attachments;
+ this.attachedMessages = attachedMessages;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getBlobId() {
+ return blobId;
+ }
+
+ public String getThreadId() {
+ return threadId;
+ }
+
+ public ImmutableList<String> getMailboxIds() {
+ return mailboxIds;
+ }
+
+ public Optional<String> getInReplyToMessageId() {
+ return inReplyToMessageId;
+ }
+
+ public boolean isIsUnread() {
+ return isUnread;
+ }
+
+ public boolean isIsFlagged() {
+ return isFlagged;
+ }
+
+ public boolean isIsAnswered() {
+ return isAnswered;
+ }
+
+ public boolean isIsDraft() {
+ return isDraft;
+ }
+
+ public boolean isHasAttachment() {
+ return hasAttachment;
+ }
+
+ public ImmutableMap<String, String> getHeaders() {
+ return headers;
+ }
+
+ public Optional<Emailer> getFrom() {
+ return from;
+ }
+
+ public ImmutableList<Emailer> getTo() {
+ return to;
+ }
+
+ public ImmutableList<Emailer> getCc() {
+ return cc;
+ }
+
+ public ImmutableList<Emailer> getBcc() {
+ return bcc;
+ }
+
+ public ImmutableList<Emailer> getReplyTo() {
+ return replyTo;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public ZonedDateTime getDate() {
+ return date;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public String getPreview() {
+ return preview;
+ }
+
+ public Optional<String> getTextBody() {
+ return textBody;
+ }
+
+ public Optional<String> getHtmlBody() {
+ return htmlBody;
+ }
+
+ public ImmutableList<Attachment> getAttachments() {
+ return attachments;
+ }
+
+ public ImmutableMap<String, SubMessage> getAttachedMessages() {
+ return attachedMessages;
+ }
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SubMessage.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SubMessage.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SubMessage.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SubMessage.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,219 @@
+/****************************************************************
+ * 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.time.ZonedDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+@JsonDeserialize(builder = SubMessage.Builder.class)
+public class SubMessage {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @JsonPOJOBuilder(withPrefix = "")
+ public static class Builder {
+ private ImmutableMap<String, String> headers;
+ private Emailer from;
+ private ImmutableList.Builder<Emailer> to;
+ private ImmutableList.Builder<Emailer> cc;
+ private ImmutableList.Builder<Emailer> bcc;
+ private ImmutableList.Builder<Emailer> replyTo;
+ private String subject;
+ private ZonedDateTime date;
+ private String textBody;
+ private String htmlBody;
+ private ImmutableList.Builder<Attachment> attachments;
+ private ImmutableMap.Builder<String, SubMessage> attachedMessages;
+
+ private Builder() {
+ to = ImmutableList.builder();
+ cc = ImmutableList.builder();
+ bcc = ImmutableList.builder();
+ replyTo = ImmutableList.builder();
+ attachments = ImmutableList.builder();
+ attachedMessages = ImmutableMap.builder();
+ }
+
+ public Builder headers(ImmutableMap<String, String> headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ public Builder from(Emailer from) {
+ this.from = from;
+ return this;
+ }
+
+ public Builder to(List<Emailer> to) {
+ this.to.addAll(to);
+ return this;
+ }
+
+ public Builder cc(List<Emailer> cc) {
+ this.cc.addAll(cc);
+ return this;
+ }
+
+ public Builder bcc(List<Emailer> bcc) {
+ this.bcc.addAll(bcc);
+ return this;
+ }
+
+ public Builder replyTo(List<Emailer> replyTo) {
+ this.replyTo.addAll(replyTo);
+ return this;
+ }
+
+ public Builder subject(String subject) {
+ this.subject = subject;
+ return this;
+ }
+
+ public Builder date(ZonedDateTime date) {
+ this.date = date;
+ return this;
+ }
+
+ public Builder textBody(String textBody) {
+ this.textBody = textBody;
+ return this;
+ }
+
+ public Builder htmlBody(String htmlBody) {
+ this.htmlBody = htmlBody;
+ return this;
+ }
+
+ public Builder attachments(List<Attachment> attachments) {
+ this.attachments.addAll(attachments);
+ return this;
+ }
+
+ public Builder attachedMessages(Map<String, SubMessage>
attachedMessages) {
+ this.attachedMessages.putAll(attachedMessages);
+ return this;
+ }
+
+ public SubMessage build() {
+ Preconditions.checkState(headers != null, "'headers' is
mandatory");
+ Preconditions.checkState(!Strings.isNullOrEmpty(subject),
"'subject' is mandatory");
+ Preconditions.checkState(date != null, "'date' is mandatory");
+ ImmutableList<Attachment> attachments = this.attachments.build();
+ ImmutableMap<String, SubMessage> attachedMessages =
this.attachedMessages.build();
+
Preconditions.checkState(Message.areAttachedMessagesKeysInAttachments(attachments,
attachedMessages), "'attachedMessages' keys must be in 'attachements'");
+ return new SubMessage(headers, Optional.ofNullable(from),
to.build(), cc.build(), bcc.build(),
+ replyTo.build(), subject, date,
Optional.ofNullable(textBody), Optional.ofNullable(htmlBody),
+ attachments, attachedMessages
+ );
+ }
+ }
+
+ private final ImmutableMap<String, String> headers;
+ private final Optional<Emailer> from;
+ private final ImmutableList<Emailer> to;
+ private final ImmutableList<Emailer> cc;
+ private final ImmutableList<Emailer> bcc;
+ private final ImmutableList<Emailer> replyTo;
+ private final String subject;
+ private final ZonedDateTime date;
+ private final Optional<String> textBody;
+ private final Optional<String> htmlBody;
+ private final ImmutableList<Attachment> attachments;
+ private final ImmutableMap<String, SubMessage> attachedMessages;
+
+ @VisibleForTesting SubMessage(ImmutableMap<String, String> headers,
Optional<Emailer> from, ImmutableList<Emailer> to, ImmutableList<Emailer> cc,
ImmutableList<Emailer> bcc, ImmutableList<Emailer> replyTo, String subject,
ZonedDateTime date, Optional<String> textBody,
+ Optional<String> htmlBody, ImmutableList<Attachment> attachments,
ImmutableMap<String, SubMessage> attachedMessages) {
+ super();
+ this.headers = headers;
+ this.from = from;
+ this.to = to;
+ this.cc = cc;
+ this.bcc = bcc;
+ this.replyTo = replyTo;
+ this.subject = subject;
+ this.date = date;
+ this.textBody = textBody;
+ this.htmlBody = htmlBody;
+ this.attachments = attachments;
+ this.attachedMessages = attachedMessages;
+ }
+
+ public ImmutableMap<String, String> getHeaders() {
+ return headers;
+ }
+
+ public Optional<Emailer> getFrom() {
+ return from;
+ }
+
+ public ImmutableList<Emailer> getTo() {
+ return to;
+ }
+
+ public ImmutableList<Emailer> getCc() {
+ return cc;
+ }
+
+ public ImmutableList<Emailer> getBcc() {
+ return bcc;
+ }
+
+ public ImmutableList<Emailer> getReplyTo() {
+ return replyTo;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public ZonedDateTime getDate() {
+ return date;
+ }
+
+ public Optional<String> getTextBody() {
+ return textBody;
+ }
+
+ public Optional<String> getHtmlBody() {
+ return htmlBody;
+ }
+
+ public ImmutableList<Attachment> getAttachments() {
+ return attachments;
+ }
+
+ public ImmutableMap<String, SubMessage> getAttachedMessages() {
+ return attachedMessages;
+ }
+
+
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/DateResolutionFormater.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/DateResolutionFormater.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/DateResolutionFormater.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/DateResolutionFormater.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,66 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import org.apache.james.mailbox.model.SearchQuery;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+public class DateResolutionFormater {
+
+ public static DateTimeFormatter DATE_TIME_FOMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
+
+ public static ZonedDateTime computeUpperDate(ZonedDateTime date,
SearchQuery.DateResolution resolution) {
+ return
date.truncatedTo(convertDateResolutionField(resolution)).plus(1,convertDateResolutionField(resolution));
+ }
+
+ public static ZonedDateTime computeLowerDate(ZonedDateTime date,
SearchQuery.DateResolution resolution) {
+ return date.truncatedTo(convertDateResolutionField(resolution));
+ }
+
+ private static TemporalUnit
convertDateResolutionField(SearchQuery.DateResolution resolution) {
+ switch(resolution) {
+ case Year:
+ return ChronoUnit.YEARS;
+ case Month:
+ return ChronoUnit.MONTHS;
+ case Day:
+ return ChronoUnit.DAYS;
+ case Hour:
+ return ChronoUnit.HOURS;
+ case Minute:
+ return ChronoUnit.MINUTES;
+ case Second:
+ return ChronoUnit.SECONDS;
+ default:
+ throw new RuntimeException("Unknown Date resolution used");
+ }
+ }
+
+ public static ZonedDateTime convertDateToZonedDateTime(Date date) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()),
ZoneId.systemDefault());
+ }
+}
\ No newline at end of file
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/EMailer.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/EMailer.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/EMailer.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/EMailer.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,68 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Objects;
+
+public class EMailer {
+
+ private final String name;
+ private final String address;
+
+ public EMailer(String name, String address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ @JsonProperty(JsonMessageConstants.EMailer.NAME)
+ public String getName() {
+ return name;
+ }
+
+ @JsonProperty(JsonMessageConstants.EMailer.ADDRESS)
+ public String getAddress() {
+ return address;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof EMailer) {
+ EMailer otherEMailer = (EMailer) o;
+ return Objects.equals(name, otherEMailer.name)
+ && Objects.equals(address, otherEMailer.address);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, address);
+ }
+
+ @Override
+ public String toString() {
+ return com.google.common.base.Objects.toStringHelper(this)
+ .add("name", name)
+ .add("address", address)
+ .toString();
+ }
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/HeaderCollection.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/HeaderCollection.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/HeaderCollection.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/HeaderCollection.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,245 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import org.apache.james.mailbox.store.search.SearchUtil;
+import org.apache.james.mime4j.dom.address.Address;
+import org.apache.james.mime4j.dom.address.Group;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.field.address.LenientAddressParser;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.util.MimeUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class HeaderCollection {
+
+ public static class Builder {
+
+ // Some sent e-mail have this form : Wed, 3 Jun 2015 09:05:46 +0000
(UTC)
+ // Java 8 Time library RFC_1123_DATE_TIME corresponds to Wed, 3 Jun
2015 09:05:46 +0000 only
+ // This REGEXP is here to match ( in order to remove ) the possible
invalid end of a header date
+ // Example of matching patterns :
+ // (UTC)
+ // (CEST)
+ private static final Pattern DATE_SANITIZING_PATTERN =
Pattern.compile(" *\\(.*\\) *");
+
+ private final Set<EMailer> toAddressSet;
+ private final Set<EMailer> fromAddressSet;
+ private final Set<EMailer> ccAddressSet;
+ private final Set<EMailer> bccAddressSet;
+ private final Set<EMailer> replyToAddressSet;
+ private final Set<String> subjectSet;
+ private final Multimap<String, String> headers;
+ private Optional<ZonedDateTime> sentDate;
+
+ private Builder() {
+ toAddressSet = new HashSet<>();
+ fromAddressSet = new HashSet<>();
+ ccAddressSet = new HashSet<>();
+ bccAddressSet = new HashSet<>();
+ replyToAddressSet = new HashSet<>();
+ subjectSet = new HashSet<>();
+ headers = ArrayListMultimap.create();
+ sentDate = Optional.empty();
+ }
+
+ public Builder add(Field field) {
+ Preconditions.checkNotNull(field);
+ String headerName = field.getName().toLowerCase();
+ String headerValue = field.getBody();
+ headers.put(headerName, headerValue);
+ handleSpecificHeader(headerName, headerValue);
+ return this;
+ }
+
+ public HeaderCollection build() {
+ return new HeaderCollection(
+ ImmutableSet.copyOf(toAddressSet),
+ ImmutableSet.copyOf(fromAddressSet),
+ ImmutableSet.copyOf(ccAddressSet),
+ ImmutableSet.copyOf(bccAddressSet),
+ ImmutableSet.copyOf(replyToAddressSet),
+ ImmutableSet.copyOf(subjectSet),
+ ImmutableMultimap.copyOf(headers),
+ sentDate);
+ }
+
+ private void handleSpecificHeader(String headerName, String
headerValue) {
+ switch (headerName) {
+ case TO:
+ case FROM:
+ case CC:
+ case BCC:
+ case REPLY_TO:
+ manageAddressField(headerName, headerValue);
+ break;
+ case SUBJECT:
+ subjectSet.add(headerValue);
+ break;
+ case DATE:
+ sentDate = toISODate(headerValue);
+ break;
+ }
+ }
+
+ private void manageAddressField(String headerName, String headerValue)
{
+ LenientAddressParser.DEFAULT
+ .parseAddressList(MimeUtil.unfold(headerValue))
+ .stream()
+ .flatMap(this::convertAddressToMailboxStream)
+ .map((mailbox) -> new
EMailer(SearchUtil.getDisplayAddress(mailbox) , mailbox.getAddress()))
+ .collect(Collectors.toCollection(() ->
getAddressSet(headerName)));
+ }
+
+ private Stream<Mailbox> convertAddressToMailboxStream(Address address)
{
+ if (address instanceof Mailbox) {
+ return Stream.of((Mailbox) address);
+ } else if (address instanceof Group) {
+ return ((Group) address).getMailboxes().stream();
+ }
+ return Stream.empty();
+ }
+
+ private Set<EMailer> getAddressSet(String headerName) {
+ switch (headerName) {
+ case TO:
+ return toAddressSet;
+ case FROM:
+ return fromAddressSet;
+ case CC:
+ return ccAddressSet;
+ case BCC:
+ return bccAddressSet;
+ case REPLY_TO:
+ return replyToAddressSet;
+ }
+ throw new RuntimeException(headerName + " is not a address header
name");
+ }
+
+ private Optional<ZonedDateTime> toISODate(String value) {
+ try {
+ return Optional.of(ZonedDateTime.parse(
+ sanitizeDateStringHeaderValue(value),
+ DateTimeFormatter.RFC_1123_DATE_TIME));
+ } catch (Exception e) {
+ LOGGER.info("Can not parse receive date " + value);
+ return Optional.empty();
+ }
+ }
+
+ @VisibleForTesting String sanitizeDateStringHeaderValue(String value) {
+ // Some sent e-mail have this form : Wed, 3 Jun 2015 09:05:46
+0000 (UTC)
+ // Java 8 Time library RFC_1123_DATE_TIME corresponds to Wed, 3
Jun 2015 09:05:46 +0000 only
+ // This method is here to convert the first date into something
parsable by RFC_1123_DATE_TIME DateTimeFormatter
+ Matcher sanitizerMatcher = DATE_SANITIZING_PATTERN.matcher(value);
+ if (sanitizerMatcher.find()) {
+ return value.substring(0 , sanitizerMatcher.start());
+ }
+ return value;
+ }
+
+ }
+
+ public static final String TO = "to";
+ public static final String FROM = "from";
+ public static final String CC = "cc";
+ public static final String BCC = "bcc";
+ public static final String REPLY_TO = "reply-to";
+ public static final String SUBJECT = "subject";
+ public static final String DATE = "date";
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(HeaderCollection.class);
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final ImmutableSet<EMailer> toAddressSet;
+ private final ImmutableSet<EMailer> fromAddressSet;
+ private final ImmutableSet<EMailer> ccAddressSet;
+ private final ImmutableSet<EMailer> bccAddressSet;
+ private final ImmutableSet<EMailer> replyToAddressSet;
+ private final ImmutableSet<String> subjectSet;
+ private final ImmutableMultimap<String, String> headers;
+ private Optional<ZonedDateTime> sentDate;
+
+ private HeaderCollection(ImmutableSet<EMailer> toAddressSet,
ImmutableSet<EMailer> fromAddressSet,
+ ImmutableSet<EMailer> ccAddressSet, ImmutableSet<EMailer>
bccAddressSet, ImmutableSet<EMailer> replyToAddressSet, ImmutableSet<String>
subjectSet,
+ ImmutableMultimap<String, String> headers, Optional<ZonedDateTime>
sentDate) {
+ this.toAddressSet = toAddressSet;
+ this.fromAddressSet = fromAddressSet;
+ this.ccAddressSet = ccAddressSet;
+ this.bccAddressSet = bccAddressSet;
+ this.replyToAddressSet = replyToAddressSet;
+ this.subjectSet = subjectSet;
+ this.headers = headers;
+ this.sentDate = sentDate;
+ }
+
+ public Set<EMailer> getToAddressSet() {
+ return toAddressSet;
+ }
+
+ public Set<EMailer> getFromAddressSet() {
+ return fromAddressSet;
+ }
+
+ public Set<EMailer> getCcAddressSet() {
+ return ccAddressSet;
+ }
+
+ public Set<EMailer> getBccAddressSet() {
+ return bccAddressSet;
+ }
+
+ public Set<EMailer> getReplyToAddressSet() {
+ return replyToAddressSet;
+ }
+
+ public Set<String> getSubjectSet() {
+ return subjectSet;
+ }
+
+ public Optional<ZonedDateTime> getSentDate() {
+ return sentDate;
+ }
+
+ public Multimap<String, String> getHeaders() {
+ return headers;
+ }
+
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/IndexableMessage.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,257 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Multimap;
+import org.apache.james.mailbox.store.extractor.TextExtractor;
+import org.apache.james.mailbox.store.mail.model.MailboxId;
+import org.apache.james.mailbox.store.mail.model.Message;
+import org.apache.james.mailbox.store.mail.model.Property;
+import org.apache.james.mime4j.MimeException;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class IndexableMessage {
+
+ public static IndexableMessage from(Message<? extends MailboxId> message,
TextExtractor textExtractor, ZoneId zoneId) {
+ Preconditions.checkNotNull(message.getMailboxId());
+ IndexableMessage indexableMessage = new IndexableMessage();
+ try {
+ MimePart parsingResult = new MimePartParser(message,
textExtractor).parse();
+ indexableMessage.bodyText = parsingResult.locateFirstTextualBody();
+ indexableMessage.setFlattenedAttachments(parsingResult);
+
indexableMessage.copyHeaderFields(parsingResult.getHeaderCollection(),
getSanitizedInternalDate(message, zoneId));
+ } catch (IOException | MimeException e) {
+ throw Throwables.propagate(e);
+ }
+ indexableMessage.copyMessageFields(message, zoneId);
+ return indexableMessage;
+ }
+
+ private void setFlattenedAttachments(MimePart parsingResult) {
+ attachments = parsingResult.getAttachmentsStream()
+ .collect(Collectors.toList());
+ }
+
+ private void copyHeaderFields(HeaderCollection headerCollection,
ZonedDateTime internalDate) {
+ this.headers = headerCollection.getHeaders();
+ this.subjects = headerCollection.getSubjectSet();
+ this.from = headerCollection.getFromAddressSet();
+ this.to = headerCollection.getToAddressSet();
+ this.replyTo = headerCollection.getReplyToAddressSet();
+ this.cc = headerCollection.getCcAddressSet();
+ this.bcc = headerCollection.getBccAddressSet();
+ this.sentDate =
DateResolutionFormater.DATE_TIME_FOMATTER.format(headerCollection.getSentDate().orElse(internalDate));
+ }
+
+ private void copyMessageFields(Message<? extends MailboxId> message,
ZoneId zoneId) {
+ this.id = message.getUid();
+ this.mailboxId = message.getMailboxId().serialize();
+ this.modSeq = message.getModSeq();
+ this.size = message.getFullContentOctets();
+ this.date =
DateResolutionFormater.DATE_TIME_FOMATTER.format(getSanitizedInternalDate(message,
zoneId));
+ this.mediaType = message.getMediaType();
+ this.subType = message.getSubType();
+ this.isAnswered = message.isAnswered();
+ this.isDeleted = message.isDeleted();
+ this.isDraft = message.isDraft();
+ this.isFlagged = message.isFlagged();
+ this.isRecent = message.isRecent();
+ this.isUnRead = ! message.isSeen();
+ this.userFlags = message.createFlags().getUserFlags();
+ this.properties = message.getProperties();
+ }
+
+ private static ZonedDateTime getSanitizedInternalDate(Message<? extends
MailboxId> message, ZoneId zoneId) {
+ if (message.getInternalDate() == null) {
+ return ZonedDateTime.now();
+ }
+ return ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(message.getInternalDate().getTime()),
+ zoneId);
+ }
+
+ private Long id;
+ private String mailboxId;
+ private long modSeq;
+ private long size;
+ private String date;
+ private String mediaType;
+ private String subType;
+ private boolean isUnRead;
+ private boolean isRecent;
+ private boolean isFlagged;
+ private boolean isDeleted;
+ private boolean isDraft;
+ private boolean isAnswered;
+ private String[] userFlags;
+ private Multimap<String, String> headers;
+ private Set<EMailer> from;
+ private Set<EMailer> to;
+ private Set<EMailer> cc;
+ private Set<EMailer> bcc;
+ private Set<EMailer> replyTo;
+ private Set<String> subjects;
+ private String sentDate;
+ private List<Property> properties;
+ private List<MimePart> attachments;
+ private Optional<String> bodyText;
+
+ @JsonProperty(JsonMessageConstants.ID)
+ public Long getId() {
+ return id;
+ }
+
+ @JsonProperty(JsonMessageConstants.MAILBOX_ID)
+ public String getMailboxId() {
+ return mailboxId;
+ }
+
+ @JsonProperty(JsonMessageConstants.MODSEQ)
+ public long getModSeq() {
+ return modSeq;
+ }
+
+ @JsonProperty(JsonMessageConstants.SIZE)
+ public long getSize() {
+ return size;
+ }
+
+ @JsonProperty(JsonMessageConstants.DATE)
+ public String getDate() {
+ return date;
+ }
+
+ @JsonProperty(JsonMessageConstants.MEDIA_TYPE)
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ @JsonProperty(JsonMessageConstants.SUBTYPE)
+ public String getSubType() {
+ return subType;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_UNREAD)
+ public boolean isUnRead() {
+ return isUnRead;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_RECENT)
+ public boolean isRecent() {
+ return isRecent;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_FLAGGED)
+ public boolean isFlagged() {
+ return isFlagged;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_DELETED)
+ public boolean isDeleted() {
+ return isDeleted;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_DRAFT)
+ public boolean isDraft() {
+ return isDraft;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_ANSWERED)
+ public boolean isAnswered() {
+ return isAnswered;
+ }
+
+ @JsonProperty(JsonMessageConstants.USER_FLAGS)
+ public String[] getUserFlags() {
+ return userFlags;
+ }
+
+ @JsonProperty(JsonMessageConstants.HEADERS)
+ public Multimap<String, String> getHeaders() {
+ return headers;
+ }
+
+ @JsonProperty(JsonMessageConstants.SUBJECT)
+ public Set<String> getSubjects() {
+ return subjects;
+ }
+
+ @JsonProperty(JsonMessageConstants.FROM)
+ public Set<EMailer> getFrom() {
+ return from;
+ }
+
+ @JsonProperty(JsonMessageConstants.TO)
+ public Set<EMailer> getTo() {
+ return to;
+ }
+
+ @JsonProperty(JsonMessageConstants.CC)
+ public Set<EMailer> getCc() {
+ return cc;
+ }
+
+ @JsonProperty(JsonMessageConstants.BCC)
+ public Set<EMailer> getBcc() {
+ return bcc;
+ }
+
+ @JsonProperty(JsonMessageConstants.REPLY_TO)
+ public Set<EMailer> getReplyTo() {
+ return replyTo;
+ }
+
+ @JsonProperty(JsonMessageConstants.SENT_DATE)
+ public String getSentDate() {
+ return sentDate;
+ }
+
+ @JsonProperty(JsonMessageConstants.PROPERTIES)
+ public List<Property> getProperties() {
+ return properties;
+ }
+
+ @JsonProperty(JsonMessageConstants.ATTACHMENTS)
+ public List<MimePart> getAttachments() {
+ return attachments;
+ }
+
+ @JsonProperty(JsonMessageConstants.TEXT_BODY)
+ public Optional<String> getBodyText() {
+ return bodyText;
+ }
+
+ @JsonProperty(JsonMessageConstants.HAS_ATTACHMENT)
+ public boolean getHasAttachment() {
+ return attachments.size() > 0;
+ }
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/JsonMessageConstants.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/JsonMessageConstants.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/JsonMessageConstants.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/JsonMessageConstants.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,79 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+public interface JsonMessageConstants {
+
+ /*
+ Properties defined by JMAP
+ */
+ String ID = "id";
+ String MAILBOX_ID = "mailboxId";
+ String IS_UNREAD = "isUnread";
+ String IS_FLAGGED = "isFlagged";
+ String IS_ANSWERED = "isAnswered";
+ String IS_DRAFT = "isDraft";
+ String HEADERS = "headers";
+ String FROM = "from";
+ String TO = "to";
+ String CC = "cc";
+ String BCC = "bcc";
+ String REPLY_TO = "replyTo";
+ String SUBJECT = "subject";
+ String DATE = "date";
+ String SIZE = "size";
+ String TEXT_BODY = "textBody";
+ String SENT_DATE = "sentDate";
+ String ATTACHMENTS = "attachments";
+
+ /*
+ James properties we can easily get
+ */
+ String PROPERTIES = "properties";
+ String MODSEQ = "modSeq";
+ String USER_FLAGS = "userFlags";
+ String IS_RECENT = "isRecent";
+ String IS_DELETED = "isDeleted";
+ String MEDIA_TYPE = "mediaType";
+ String SUBTYPE = "subtype";
+ String HAS_ATTACHMENT = "hasAttachment";
+
+ interface EMailer {
+ String NAME = "name";
+ String ADDRESS = "address";
+ }
+
+ interface Attachment {
+ String TEXT_CONTENT = "textContent";
+ String MEDIA_TYPE = "mediaType";
+ String SUBTYPE = "subtype";
+ String CONTENT_DISPOSITION = "contentDisposition";
+ String FILENAME = "fileName";
+ String FILE_EXTENSION = "fileExtension";
+ String FILE_METADATA = "fileMetadata";
+ }
+
+ interface Property {
+ String NAMESPACE = "namespace";
+ String NAME = "name";
+ String VALUE = "value";
+ }
+
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MessageUpdateJson.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MessageUpdateJson.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MessageUpdateJson.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MessageUpdateJson.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,75 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jmap.model.message;
+
+import javax.mail.Flags;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MessageUpdateJson {
+
+ private final Flags flags;
+ private final long modSeq;
+
+ public MessageUpdateJson(Flags flags, long modSeq) {
+ this.flags = flags;
+ this.modSeq = modSeq;
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_ANSWERED)
+ public boolean isAnswered() {
+ return flags.contains(Flags.Flag.ANSWERED);
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_DELETED)
+ public boolean isDeleted() {
+ return flags.contains(Flags.Flag.DELETED);
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_DRAFT)
+ public boolean isDraft() {
+ return flags.contains(Flags.Flag.DRAFT);
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_FLAGGED)
+ public boolean isFlagged() {
+ return flags.contains(Flags.Flag.FLAGGED);
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_RECENT)
+ public boolean isRecent() {
+ return flags.contains(Flags.Flag.RECENT);
+ }
+
+ @JsonProperty(JsonMessageConstants.IS_UNREAD)
+ public boolean isUnRead() {
+ return ! flags.contains(Flags.Flag.SEEN);
+ }
+
+
+ @JsonProperty(JsonMessageConstants.USER_FLAGS)
+ public String[] getUserFlags() {
+ return flags.getUserFlags();
+ }
+
+ @JsonProperty(JsonMessageConstants.MODSEQ)
+ public long getModSeq() {
+ return modSeq;
+ }
+
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePart.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,263 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
+import org.apache.james.mailbox.store.extractor.ParsedContent;
+import org.apache.james.mailbox.store.extractor.TextExtractor;
+import org.apache.james.mime4j.stream.Field;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+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 TextExtractor textExtractor;
+
+ 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();
+ this.textExtractor = new DefaultTextExtractor();
+ }
+
+ @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 MimePartContainerBuilder using(TextExtractor textExtractor) {
+ Preconditions.checkArgument(textExtractor != null, "Provided text
extractor should not be null");
+ this.textExtractor = textExtractor;
+ return this;
+ }
+
+ @Override
+ public MimePart build() {
+ Optional<ParsedContent> parsedContent =
parseContent(textExtractor);
+ return new MimePart(
+ headerCollectionBuilder.build(),
+ parsedContent.map( x ->
Optional.ofNullable(x.getTextualContent()))
+ .orElse(Optional.empty())
+ ,
+ mediaType,
+ subType,
+ fileName,
+ fileExtension,
+ contentDisposition,
+ children,
+ parsedContent
+ .map(x -> x.getMetadata()
+ .entrySet()
+ .stream()
+ .reduce(ImmutableMultimap.<String, String>builder(),
+ (builder, entry) -> builder.putAll(entry.getKey(),
entry.getValue()),
+ (builder1, builder2) ->
builder1.putAll(builder2.build())).build())
+ .orElse(ImmutableMultimap.of())
+ );
+ }
+
+ private Optional<ParsedContent> parseContent(TextExtractor
textExtractor) {
+ if (bodyContent.isPresent()) {
+ try {
+ return Optional.of(textExtractor.extractContent(
+ bodyContent.get(),
+ computeContentType().orElse(null),
+ fileName.orElse(null)));
+ } catch (Exception e) {
+ LOGGER.warn("Failed parsing attachment", e);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Optional<String> computeContentType() {
+ if (mediaType.isPresent() && subType.isPresent()) {
+ return Optional.of(mediaType.get() + "/" + subType.get());
+ } else {
+ 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 final ImmutableMultimap<String, String> metadata;
+
+ 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, Multimap<String, String> metadata) {
+ this.headerCollection = headerCollection;
+ this.mediaType = mediaType;
+ this.subType = subType;
+ this.fileName = fileName;
+ this.fileExtension = fileExtension;
+ this.contentDisposition = contentDisposition;
+ this.attachments = attachments;
+ this.bodyTextContent = bodyTextContent;
+ this.metadata = ImmutableMultimap.copyOf(metadata);
+ }
+
+ @JsonIgnore
+ public List<MimePart> getAttachments() {
+ return attachments;
+ }
+
+ @JsonIgnore
+ public HeaderCollection getHeaderCollection() {
+ return headerCollection;
+ }
+
+ @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;
+ }
+
+ @JsonProperty(JsonMessageConstants.Attachment.FILE_METADATA)
+ public ImmutableMultimap<String, String> getMetadata() {
+ return metadata;
+ }
+
+ @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/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartContainerBuilder.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartContainerBuilder.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartContainerBuilder.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartContainerBuilder.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,47 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import org.apache.james.mailbox.store.extractor.TextExtractor;
+import org.apache.james.mime4j.stream.Field;
+
+import java.io.InputStream;
+
+public interface MimePartContainerBuilder {
+
+ MimePart build();
+
+ MimePartContainerBuilder using(TextExtractor textExtractor);
+
+ 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/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartParser.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartParser.java?rev=1719389&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartParser.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/message/MimePartParser.java
Fri Dec 11 12:34:26 2015
@@ -0,0 +1,122 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.message;
+
+import com.google.common.base.Preconditions;
+import org.apache.james.mailbox.store.extractor.TextExtractor;
+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 TextExtractor textExtractor;
+ private final MimeTokenStream stream;
+ private final Deque<MimePartContainerBuilder> builderStack;
+ private MimePart result;
+ private MimePartContainerBuilder currentlyBuildMimePart;
+
+ public MimePartParser(Message<? extends MailboxId> message, TextExtractor
textExtractor) {
+ this.message = message;
+ this.textExtractor = textExtractor;
+ 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.using(textExtractor).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());
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]