You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2016/07/08 14:46:40 UTC
[5/5] james-project git commit: JAMES-1790 check that blobids exist
when attaching them
JAMES-1790 check that blobids exist when attaching them
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/662fa4a9
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/662fa4a9
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/662fa4a9
Branch: refs/heads/master
Commit: 662fa4a954571ef6a0d7044873359211eeaae181
Parents: 7be2166
Author: Matthieu Baechler <ma...@linagora.com>
Authored: Tue Jul 5 17:09:13 2016 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Fri Jul 8 16:45:00 2016 +0200
----------------------------------------------------------------------
.../integration/SetMessagesMethodTest.java | 619 +++++++++++++------
.../AttachmentsNotFoundException.java | 40 ++
.../jmap/methods/MIMEMessageConverter.java | 127 +++-
.../methods/SetMessagesCreationProcessor.java | 90 ++-
.../james/jmap/model/CreationMessage.java | 32 +-
.../org/apache/james/jmap/model/SetError.java | 9 +-
.../james/jmap/model/SetMessagesError.java | 92 +++
.../jmap/methods/MIMEMessageConverterTest.java | 73 ++-
.../SetMessagesCreationProcessorTest.java | 84 ++-
9 files changed, 887 insertions(+), 279 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index af96309..be3297b 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -33,10 +33,12 @@ import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.isEmptyOrNullString;
import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
import static org.hamcrest.collection.IsMapWithSize.anEmptyMap;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
@@ -52,6 +54,7 @@ import org.apache.james.jmap.model.mailbox.Role;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MailboxConstants;
import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.store.mail.model.Attachment;
import org.apache.james.mailbox.store.mail.model.Mailbox;
import org.hamcrest.Matchers;
import org.junit.After;
@@ -93,12 +96,12 @@ public abstract class SetMessagesMethodTest {
jmapServer = createJmapServer();
jmapServer.start();
RestAssured.requestSpecification = new RequestSpecBuilder()
- .setContentType(ContentType.JSON)
- .setAccept(ContentType.JSON)
- .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)))
- .setPort(jmapServer.getJmapPort())
- .build();
-
+ .setContentType(ContentType.JSON)
+ .setAccept(ContentType.JSON)
+ .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)))
+ .setPort(jmapServer.getJmapPort())
+ .build();
+
username = "username@" + USERS_DOMAIN;
String password = "password";
jmapServer.serverProbe().addDomain(USERS_DOMAIN);
@@ -148,6 +151,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200)
.body(NAME, equalTo("error"))
.body(ARGUMENTS + ".type", equalTo("Not yet implemented"));
@@ -161,6 +165,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200)
.body(NAME, equalTo("error"))
.body(ARGUMENTS + ".type", equalTo("Not yet implemented"));
@@ -215,7 +220,7 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
given()
@@ -224,6 +229,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200)
.body(NAME, equalTo("messagesSet"))
.body(ARGUMENTS + ".notDestroyed", anEmptyMap())
@@ -237,7 +243,7 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
// When
@@ -247,6 +253,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200);
// Then
@@ -256,6 +263,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200)
.body(NAME, equalTo("messages"))
.body(ARGUMENTS + ".list", empty());
@@ -266,13 +274,13 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test2\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test2\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test3\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test3\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String missingMessageId = username + "|mailbox|4";
@@ -282,6 +290,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200)
.body(NAME, equalTo("messagesSet"))
.body(ARGUMENTS + ".destroyed", hasSize(2))
@@ -299,13 +308,13 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test2\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test2\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test3\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test3\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
// When
@@ -315,6 +324,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200);
// Then
@@ -324,6 +334,7 @@ public abstract class SetMessagesMethodTest {
.when()
.post("/jmap")
.then()
+ .log().ifValidationFails()
.statusCode(200)
.body(NAME, equalTo("messages"))
.body(ARGUMENTS + ".list", hasSize(1));
@@ -335,7 +346,7 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String presumedMessageId = username + "|mailbox|1";
@@ -348,8 +359,8 @@ public abstract class SetMessagesMethodTest {
.post("/jmap")
// Then
.then()
- .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId));
}
private ResponseSpecification getSetMessagesUpdateOKResponseAssertions(String messageId) {
@@ -369,27 +380,27 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String presumedMessageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : false } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : false } } }, \"#0\"]]", presumedMessageId))
// When
.when()
- .post("/jmap");
+ .post("/jmap");
// Then
with()
- .header("Authorization", accessToken.serialize())
- .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
- .post("/jmap")
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
+ .post("/jmap")
.then()
- .statusCode(200)
- .body(NAME, equalTo("messages"))
- .body(ARGUMENTS + ".list", hasSize(1))
- .body(ARGUMENTS + ".list[0].isUnread", equalTo(false))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(ARGUMENTS + ".list[0].isUnread", equalTo(false));
}
@Test
@@ -398,20 +409,20 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags(Flags.Flag.SEEN));
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags(Flags.Flag.SEEN));
await();
String presumedMessageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : true } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : true } } }, \"#0\"]]", presumedMessageId))
// When
.when()
- .post("/jmap")
+ .post("/jmap")
// Then
.then()
- .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId));
}
@Test
@@ -420,26 +431,26 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags(Flags.Flag.SEEN));
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags(Flags.Flag.SEEN));
await();
String presumedMessageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : true } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : true } } }, \"#0\"]]", presumedMessageId))
// When
.when()
- .post("/jmap");
+ .post("/jmap");
// Then
with()
- .header("Authorization", accessToken.serialize())
- .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
- .post("/jmap")
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
+ .post("/jmap")
.then()
- .body(NAME, equalTo("messages"))
- .body(ARGUMENTS + ".list", hasSize(1))
- .body(ARGUMENTS + ".list[0].isUnread", equalTo(true))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(ARGUMENTS + ".list[0].isUnread", equalTo(true));
}
@@ -449,20 +460,20 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String presumedMessageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isFlagged\" : true } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isFlagged\" : true } } }, \"#0\"]]", presumedMessageId))
// When
.when()
- .post("/jmap")
+ .post("/jmap")
// Then
.then()
- .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId));
}
@Test
@@ -471,53 +482,53 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String presumedMessageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isFlagged\" : true } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isFlagged\" : true } } }, \"#0\"]]", presumedMessageId))
// When
.when()
- .post("/jmap");
+ .post("/jmap");
// Then
with()
- .header("Authorization", accessToken.serialize())
- .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
- .post("/jmap")
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
+ .post("/jmap")
.then()
- .body(NAME, equalTo("messages"))
- .body(ARGUMENTS + ".list", hasSize(1))
- .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true));
}
@Test
public void setMessagesShouldRejectUpdateWhenPropertyHasWrongType() throws MailboxException {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String messageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : \"123\" } } }, \"#0\"]]", messageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : \"123\" } } }, \"#0\"]]", messageId))
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(NOT_UPDATED, hasKey(messageId))
- .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
- .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("isUnread"))
- .body(NOT_UPDATED + "[\""+messageId+"\"].description", equalTo("isUnread: Can not construct instance of java.lang.Boolean from String value '123': only \"true\" or \"false\" recognized\n" +
- " at [Source: {\"isUnread\":\"123\"}; line: 1, column: 2] (through reference chain: org.apache.james.jmap.model.Builder[\"isUnread\"])"))
- .body(ARGUMENTS + ".updated", hasSize(0));
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(NOT_UPDATED, hasKey(messageId))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("isUnread"))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].description", equalTo("isUnread: Can not construct instance of java.lang.Boolean from String value '123': only \"true\" or \"false\" recognized\n" +
+ " at [Source: {\"isUnread\":\"123\"}; line: 1, column: 2] (through reference chain: org.apache.james.jmap.model.Builder[\"isUnread\"])"))
+ .body(ARGUMENTS + ".updated", hasSize(0));
}
@Test
@@ -525,27 +536,27 @@ public abstract class SetMessagesMethodTest {
public void setMessagesShouldRejectUpdateWhenPropertiesHaveWrongTypes() throws MailboxException {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String messageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : \"123\", \"isFlagged\" : 456 } } }, \"#0\"]]", messageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isUnread\" : \"123\", \"isFlagged\" : 456 } } }, \"#0\"]]", messageId))
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(NOT_UPDATED, hasKey(messageId))
- .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
- .body(NOT_UPDATED + "[\""+messageId+"\"].properties", hasSize(2))
- .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("isUnread"))
- .body(NOT_UPDATED + "[\""+messageId+"\"].properties[1]", equalTo("isFlagged"))
- .body(ARGUMENTS + ".updated", hasSize(0));
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(NOT_UPDATED, hasKey(messageId))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].properties", hasSize(2))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("isUnread"))
+ .body(NOT_UPDATED + "[\""+messageId+"\"].properties[1]", equalTo("isFlagged"))
+ .body(ARGUMENTS + ".updated", hasSize(0));
}
@Test
@@ -554,20 +565,20 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String presumedMessageId = username + "|mailbox|1";
// When
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isAnswered\" : true } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isAnswered\" : true } } }, \"#0\"]]", presumedMessageId))
.when()
- .post("/jmap")
+ .post("/jmap")
// Then
.then()
- .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .spec(getSetMessagesUpdateOKResponseAssertions(presumedMessageId));
}
@Test
@@ -576,26 +587,26 @@ public abstract class SetMessagesMethodTest {
jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "mailbox");
jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "mailbox"),
- new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
+ new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
await();
String presumedMessageId = username + "|mailbox|1";
given()
- .header("Authorization", accessToken.serialize())
- .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isAnswered\" : true } } }, \"#0\"]]", presumedMessageId))
+ .header("Authorization", accessToken.serialize())
+ .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"isAnswered\" : true } } }, \"#0\"]]", presumedMessageId))
// When
.when()
- .post("/jmap");
+ .post("/jmap");
// Then
with()
- .header("Authorization", accessToken.serialize())
- .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
- .post("/jmap")
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
+ .post("/jmap")
.then()
- .body(NAME, equalTo("messages"))
- .body(ARGUMENTS + ".list", hasSize(1))
- .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true))
- .log().ifValidationFails();
+ .log().ifValidationFails()
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true));
}
@Test
@@ -640,34 +651,34 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
- // note that assertions on result message had to be split between
- // string-typed values and boolean-typed value assertions on the same .created entry
- // make sure only one creation has been processed
- .body(ARGUMENTS + ".created", aMapWithSize(1))
- // assert server-set attributes are returned
- .body(ARGUMENTS + ".created", hasEntry(equalTo(messageCreationId), Matchers.allOf(
- hasEntry(equalTo("id"), not(isEmptyOrNullString())),
- hasEntry(equalTo("blobId"), not(isEmptyOrNullString())),
- hasEntry(equalTo("threadId"), not(isEmptyOrNullString())),
- hasEntry(equalTo("size"), not(isEmptyOrNullString()))
- )))
- // assert that message flags are all unset
- .body(ARGUMENTS + ".created", hasEntry(equalTo(messageCreationId), Matchers.allOf(
- hasEntry(equalTo("isDraft"), equalTo(false)),
- hasEntry(equalTo("isUnread"), equalTo(false)),
- hasEntry(equalTo("isFlagged"), equalTo(false)),
- hasEntry(equalTo("isAnswered"), equalTo(false))
- )))
- ;
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ // note that assertions on result message had to be split between
+ // string-typed values and boolean-typed value assertions on the same .created entry
+ // make sure only one creation has been processed
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ // assert server-set attributes are returned
+ .body(ARGUMENTS + ".created", hasEntry(equalTo(messageCreationId), Matchers.allOf(
+ hasEntry(equalTo("id"), not(isEmptyOrNullString())),
+ hasEntry(equalTo("blobId"), not(isEmptyOrNullString())),
+ hasEntry(equalTo("threadId"), not(isEmptyOrNullString())),
+ hasEntry(equalTo("size"), not(isEmptyOrNullString()))
+ )))
+ // assert that message flags are all unset
+ .body(ARGUMENTS + ".created", hasEntry(equalTo(messageCreationId), Matchers.allOf(
+ hasEntry(equalTo("isDraft"), equalTo(false)),
+ hasEntry(equalTo("isUnread"), equalTo(false)),
+ hasEntry(equalTo("isFlagged"), equalTo(false)),
+ hasEntry(equalTo("isAnswered"), equalTo(false))
+ )))
+ ;
}
@Test
@@ -728,23 +739,24 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
// When
.when()
- .post("/jmap");
+ .post("/jmap");
// Then
with()
- .header("Authorization", accessToken.serialize())
- .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
.post("/jmap")
.then()
- .body(NAME, equalTo("messages"))
- .body(ARGUMENTS + ".list", hasSize(1))
- .body(ARGUMENTS + ".list[0].subject", equalTo(messageSubject))
- .body(ARGUMENTS + ".list[0].mailboxIds", contains(outboxId))
- ;
+ .log().ifValidationFails()
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(ARGUMENTS + ".list[0].subject", equalTo(messageSubject))
+ .body(ARGUMENTS + ".list[0].mailboxIds", contains(outboxId))
+ ;
}
@Test
@@ -822,19 +834,19 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
- .when()
- .post("/jmap")
- .then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
- .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("no recipient address set"))
- .body(ARGUMENTS + ".created", aMapWithSize(0));
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("no recipient address set"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
}
@Test
@@ -857,20 +869,20 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("'from' address is mandatory"))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", hasSize(1))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", contains("from"))
- .body(ARGUMENTS + ".created", aMapWithSize(0));
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("'from' address is mandatory"))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", hasSize(1))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", contains("from"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
}
@Test
@@ -895,19 +907,19 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(ARGUMENTS + ".created", aMapWithSize(1))
- .body(ARGUMENTS + ".created", hasKey(messageCreationId))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].headers.from", equalTo(fromAddress))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.name", equalTo(fromAddress))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.email", equalTo(fromAddress));
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].headers.from", equalTo(fromAddress))
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.name", equalTo(fromAddress))
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.email", equalTo(fromAddress));
}
@Test
@@ -932,19 +944,19 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(ARGUMENTS + ".created", aMapWithSize(1))
- .body(ARGUMENTS + ".created", hasKey(messageCreationId))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].headers.from", equalTo(fromAddress))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.name", equalTo(fromAddress))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.email", equalTo(fromAddress));
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].headers.from", equalTo(fromAddress))
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.name", equalTo(fromAddress))
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].from.email", equalTo(fromAddress));
}
@Test
@@ -1004,20 +1016,20 @@ public abstract class SetMessagesMethodTest {
"]";
given()
- .header("Authorization", accessToken.serialize())
- .body(requestBody)
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
.when()
- .post("/jmap")
+ .post("/jmap")
.then()
- .log().ifValidationFails()
- .statusCode(200)
- .body(NAME, equalTo("messagesSet"))
- .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", hasSize(1))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", contains("subject"))
- .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("'subject' is missing"))
- .body(ARGUMENTS + ".created", aMapWithSize(0));
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", hasSize(1))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", contains("subject"))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("'subject' is missing"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
}
@@ -1516,7 +1528,7 @@ public abstract class SetMessagesMethodTest {
ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z");
jmapServer.serverProbe().appendMessage(username, new MailboxPath("#private", username, "inbox"),
- new ByteArrayInputStream("Subject: my test subject\r\n\r\ntestmail".getBytes()), Date.from(dateTime.toInstant()), false, new Flags());
+ new ByteArrayInputStream("Subject: my test subject\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), Date.from(dateTime.toInstant()), false, new Flags());
String messageToMoveId = "user|inbox|1";
@@ -1547,4 +1559,209 @@ public abstract class SetMessagesMethodTest {
+ "(through reference chain: org.apache.james.jmap.model.Builder[\"mailboxIds\"])"))
.body(ARGUMENTS + ".updated", hasSize(0));
}
+
+ @Test
+ public void setMessagesShouldReturnAttachmentsNotFoundWhenBlobIdDoesntExist() throws Exception {
+ jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
+ await();
+ String messageCreationId = "creationId";
+ String fromAddress = username;
+ String outboxId = getOutboxId(accessToken);
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"Message with a broken blobId\"," +
+ " \"textBody\": \"Test body\"," +
+ " \"mailboxIds\": [\"" + outboxId + "\"], " +
+ " \"attachments\": [" +
+ " {\"blobId\" : \"brokenId1\", \"type\" : \"image/gif\", \"size\" : 1337}," +
+ " {\"blobId\" : \"brokenId2\", \"type\" : \"image/jpeg\", \"size\" : 1337}" +
+ " ]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ String notCreatedPath = ARGUMENTS + ".notCreated[\""+messageCreationId+"\"]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(notCreatedPath + ".type", equalTo("invalidProperties"))
+ .body(notCreatedPath + ".attachmentsNotFound", contains("brokenId1", "brokenId2"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0));
+ }
+
+ @Test
+ public void setMessagesShouldReturnAttachmentsWhenMessageHasAttachment() throws Exception {
+ jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
+
+ Attachment attachment = Attachment.builder()
+ .bytes("attachment".getBytes(Charsets.UTF_8))
+ .type("application/octet-stream")
+ .build();
+ uploadAttachment(attachment);
+ Attachment attachment2 = Attachment.builder()
+ .bytes("attachment2".getBytes(Charsets.UTF_8))
+ .type("application/octet-stream")
+ .build();
+ uploadAttachment(attachment2);
+
+ String messageCreationId = "creationId";
+ String fromAddress = username;
+ String outboxId = getOutboxId(accessToken);
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"Message with two attachments\"," +
+ " \"textBody\": \"Test body\"," +
+ " \"mailboxIds\": [\"" + outboxId + "\"], " +
+ " \"attachments\": [" +
+ " {\"blobId\" : \"" + attachment.getAttachmentId().getId() + "\", " +
+ " \"type\" : \"" + attachment.getType() + "\", " +
+ " \"size\" : " + attachment.getSize() + "}," +
+ " {\"blobId\" : \"" + attachment2.getAttachmentId().getId() + "\", " +
+ " \"type\" : \"" + attachment2.getType() + "\", " +
+ " \"size\" : " + attachment2.getSize() + ", " +
+ " \"cid\" : \"123456789\", " +
+ " \"isInline\" : true }" +
+ " ]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ String createdPath = ARGUMENTS + ".created[\""+messageCreationId+"\"]";
+ String firstAttachment = createdPath + ".attachments[0]";
+ String secondAttachment = createdPath + ".attachments[1]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(createdPath + ".attachments", hasSize(2))
+ .body(firstAttachment + ".blobId", equalTo(attachment.getAttachmentId().getId()))
+ .body(firstAttachment + ".type", equalTo("application/octet-stream; charset=UTF-8"))
+ .body(firstAttachment + ".size", equalTo((int) attachment.getSize()))
+ .body(firstAttachment + ".cid", nullValue())
+ .body(firstAttachment + ".isInline", equalTo(false))
+ .body(secondAttachment + ".blobId", equalTo(attachment2.getAttachmentId().getId()))
+ .body(secondAttachment + ".type", equalTo("application/octet-stream; charset=UTF-8"))
+ .body(secondAttachment + ".size", equalTo((int) attachment2.getSize()))
+ .body(secondAttachment + ".cid", equalTo("123456789"))
+ .body(secondAttachment + ".isInline", equalTo(true));
+ }
+
+ private void uploadAttachment(Attachment attachment) throws IOException {
+ with()
+ .header("Authorization", accessToken.serialize())
+ .contentType(attachment.getType())
+ .content(attachment.getStream())
+ .post("/upload");
+ }
+
+ @Test
+ public void attachmentsShouldBeRetrievedWhenChainingSetMessagesAndGetMessages() throws Exception {
+ jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
+
+ Attachment attachment = Attachment.builder()
+ .bytes("attachment".getBytes(Charsets.UTF_8))
+ .type("application/octet-stream")
+ .build();
+ uploadAttachment(attachment);
+
+ String messageCreationId = "creationId";
+ String fromAddress = username;
+ String outboxId = getOutboxId(accessToken);
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}]," +
+ " \"subject\": \"Message with an attachment\"," +
+ " \"textBody\": \"Test body\"," +
+ " \"mailboxIds\": [\"" + outboxId + "\"], " +
+ " \"attachments\": [" +
+ " {\"blobId\" : \"" + attachment.getAttachmentId().getId() + "\", " +
+ " \"type\" : \"" + attachment.getType() + "\", " +
+ " \"size\" : " + attachment.getSize() + ", " +
+ " \"cid\" : \"123456789\", " +
+ " \"isInline\" : true }" +
+ " ]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap");
+
+ calmlyAwait.atMost(30, TimeUnit.SECONDS).until( () -> isAnyMessageFoundInInbox(accessToken));
+
+ String firstMessage = ARGUMENTS + ".list[0]";
+ String firstAttachment = firstMessage + ".attachments[0]";
+ String presumedMessageId = "username@domain.tld|INBOX|1";
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + presumedMessageId + "\"]}, \"#0\"]]")
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(firstMessage + ".attachments", hasSize(1))
+ .body(firstAttachment + ".blobId", equalTo(attachment.getAttachmentId().getId()))
+ .body(firstAttachment + ".type", equalTo("application/octet-stream"))
+ .body(firstAttachment + ".size", equalTo((int) attachment.getSize()))
+ .body(firstAttachment + ".cid", equalTo("123456789"))
+ .body(firstAttachment + ".isInline", equalTo(true));
+ }
+
+ private boolean isAnyMessageFoundInInbox(AccessToken recipientToken) {
+ try {
+ String inboxId = getMailboxId(accessToken, Role.INBOX);
+ with()
+ .header("Authorization", recipientToken.serialize())
+ .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + inboxId + "\"]}}, \"#0\"]]")
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messageList"))
+ .body(ARGUMENTS + ".messageIds", hasSize(1));
+ return true;
+
+ } catch (AssertionError e) {
+ return false;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/AttachmentsNotFoundException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/AttachmentsNotFoundException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/AttachmentsNotFoundException.java
new file mode 100644
index 0000000..1c06cb4
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/AttachmentsNotFoundException.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * 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.exceptions;
+
+import java.util.List;
+
+import org.apache.james.jmap.model.BlobId;
+
+import com.google.common.collect.ImmutableList;
+
+public class AttachmentsNotFoundException extends Exception {
+
+ private List<BlobId> attachmentIds;
+
+
+ public AttachmentsNotFoundException(List<BlobId> attachmentIds) {
+ this.attachmentIds = ImmutableList.copyOf(attachmentIds);
+ }
+
+ public List<BlobId> getAttachmentIds() {
+ return attachmentIds;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
index 25ba906..2eb52e7 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
@@ -27,9 +27,11 @@ import java.util.TimeZone;
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import org.apache.commons.io.IOUtils;
import org.apache.james.jmap.model.CreationMessage;
import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
import org.apache.james.jmap.model.CreationMessageId;
+import org.apache.james.mailbox.store.mail.model.MessageAttachment;
import org.apache.james.mime4j.Charsets;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.dom.FieldParser;
@@ -37,9 +39,13 @@ import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.TextBody;
import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.dom.field.ContentDispositionField;
+import org.apache.james.mime4j.dom.field.ContentTypeField;
import org.apache.james.mime4j.dom.field.UnstructuredField;
+import org.apache.james.mime4j.field.Fields;
import org.apache.james.mime4j.field.UnstructuredFieldImpl;
import org.apache.james.mime4j.message.BasicBodyFactory;
+import org.apache.james.mime4j.message.BodyPart;
import org.apache.james.mime4j.message.BodyPartBuilder;
import org.apache.james.mime4j.message.DefaultMessageWriter;
import org.apache.james.mime4j.message.MessageBuilder;
@@ -51,16 +57,23 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.net.MediaType;
public class MIMEMessageConverter {
+
private static final Logger LOGGER = LoggerFactory.getLogger(MIMEMessageConverter.class);
private static final String PLAIN_TEXT_MEDIA_TYPE = MediaType.PLAIN_TEXT_UTF_8.withoutParameters().toString();
private static final String HTML_MEDIA_TYPE = MediaType.HTML_UTF_8.withoutParameters().toString();
private static final NameValuePair UTF_8_CHARSET = new NameValuePair("charset", Charsets.UTF_8.name());
private static final String MIXED_SUB_TYPE = "mixed";
+ private static final String FIELD_PARAMETERS_SEPARATOR = ";";
private final BasicBodyFactory bodyFactory;
@@ -68,34 +81,34 @@ public class MIMEMessageConverter {
this.bodyFactory = new BasicBodyFactory();
}
- public byte[] convert(ValueWithId.CreationMessageEntry creationMessageEntry) {
+ public byte[] convert(ValueWithId.CreationMessageEntry creationMessageEntry, ImmutableList<MessageAttachment> messageAttachments) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DefaultMessageWriter writer = new DefaultMessageWriter();
try {
- writer.writeMessage(convertToMime(creationMessageEntry), buffer);
+ writer.writeMessage(convertToMime(creationMessageEntry, messageAttachments), buffer);
} catch (IOException e) {
throw Throwables.propagate(e);
}
return buffer.toByteArray();
}
- @VisibleForTesting Message convertToMime(ValueWithId.CreationMessageEntry creationMessageEntry) {
+ @VisibleForTesting Message convertToMime(ValueWithId.CreationMessageEntry creationMessageEntry, ImmutableList<MessageAttachment> messageAttachments) {
if (creationMessageEntry == null || creationMessageEntry.getValue() == null) {
throw new IllegalArgumentException("creationMessageEntry is either null or has null message");
}
MessageBuilder messageBuilder = MessageBuilder.create();
- if (mixedTextAndHtml(creationMessageEntry.getValue())) {
- messageBuilder.setBody(createMultipartBody(creationMessageEntry.getValue()));
+ if (isMultipart(creationMessageEntry.getValue(), messageAttachments)) {
+ messageBuilder.setBody(createMultipartBody(creationMessageEntry.getValue(), messageAttachments));
} else {
messageBuilder.setBody(createTextBody(creationMessageEntry.getValue()));
}
- buildMimeHeaders(messageBuilder, creationMessageEntry.getCreationId(), creationMessageEntry.getValue());
+ buildMimeHeaders(messageBuilder, creationMessageEntry.getCreationId(), creationMessageEntry.getValue(), messageAttachments);
return messageBuilder.build();
}
- private void buildMimeHeaders(MessageBuilder messageBuilder, CreationMessageId creationId, CreationMessage newMessage) {
+ private void buildMimeHeaders(MessageBuilder messageBuilder, CreationMessageId creationId, CreationMessage newMessage, ImmutableList<MessageAttachment> messageAttachments) {
Optional<Mailbox> fromAddress = newMessage.getFrom().filter(DraftEmailer::hasValidEmail).map(this::convertEmailToMimeHeader);
fromAddress.ifPresent(messageBuilder::setFrom);
fromAddress.ifPresent(messageBuilder::setSender);
@@ -121,7 +134,7 @@ public class MIMEMessageConverter {
// note that date conversion probably lose milliseconds!
messageBuilder.setDate(Date.from(newMessage.getDate().toInstant()), TimeZone.getTimeZone(newMessage.getDate().getZone()));
newMessage.getInReplyToMessageId().ifPresent(addInReplyToHeader(messageBuilder::addField));
- if (!mixedTextAndHtml(newMessage)) {
+ if (!isMultipart(newMessage, messageAttachments)) {
newMessage.getHtmlBody().ifPresent(x -> messageBuilder.setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET));
}
}
@@ -134,8 +147,9 @@ public class MIMEMessageConverter {
};
}
- private boolean mixedTextAndHtml(CreationMessage newMessage) {
- return newMessage.getTextBody().isPresent() && newMessage.getHtmlBody().isPresent();
+ private boolean isMultipart(CreationMessage newMessage, ImmutableList<MessageAttachment> messageAttachments) {
+ return (newMessage.getTextBody().isPresent() && newMessage.getHtmlBody().isPresent())
+ || !messageAttachments.isEmpty();
}
private TextBody createTextBody(CreationMessage newMessage) {
@@ -145,26 +159,93 @@ public class MIMEMessageConverter {
return bodyFactory.textBody(body, Charsets.UTF_8);
}
- private Multipart createMultipartBody(CreationMessage newMessage) {
+ private Multipart createMultipartBody(CreationMessage newMessage, ImmutableList<MessageAttachment> messageAttachments) {
try {
- return MultipartBuilder.create(MIXED_SUB_TYPE)
- .addBodyPart(BodyPartBuilder.create()
- .use(bodyFactory)
- .setBody(newMessage.getTextBody().get(), Charsets.UTF_8)
- .setContentType(PLAIN_TEXT_MEDIA_TYPE, UTF_8_CHARSET)
- .build())
- .addBodyPart(BodyPartBuilder.create()
- .use(bodyFactory)
- .setBody(newMessage.getHtmlBody().get(), Charsets.UTF_8)
- .setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET)
- .build())
- .build();
+ MultipartBuilder builder = MultipartBuilder.create(MIXED_SUB_TYPE);
+ addText(builder, newMessage.getTextBody());
+ addHtml(builder, newMessage.getHtmlBody());
+
+ Consumer<MessageAttachment> addAttachment = addAttachment(builder);
+ messageAttachments.stream()
+ .forEach(addAttachment);
+
+ return builder.build();
} catch (IOException e) {
LOGGER.error("Error while creating textBody \n"+ newMessage.getTextBody().get() +"\n or htmlBody \n" + newMessage.getHtmlBody().get(), e);
throw Throwables.propagate(e);
}
}
+ private void addText(MultipartBuilder builder, Optional<String> textBody) throws IOException {
+ if (textBody.isPresent()) {
+ builder.addBodyPart(BodyPartBuilder.create()
+ .use(bodyFactory)
+ .setBody(textBody.get(), Charsets.UTF_8)
+ .setContentType(PLAIN_TEXT_MEDIA_TYPE, UTF_8_CHARSET)
+ .build());
+ }
+ }
+
+ private void addHtml(MultipartBuilder builder, Optional<String> htmlBody) throws IOException {
+ if (htmlBody.isPresent()) {
+ builder.addBodyPart(BodyPartBuilder.create()
+ .use(bodyFactory)
+ .setBody(htmlBody.get(), Charsets.UTF_8)
+ .setContentType(HTML_MEDIA_TYPE, UTF_8_CHARSET)
+ .build());
+ }
+ }
+
+ private Consumer<MessageAttachment> addAttachment(MultipartBuilder builder) {
+ return att -> {
+ try {
+ builder.addBodyPart(attachmentBodyPart(att));
+ } catch (IOException e) {
+ LOGGER.error("Error while creating attachment", e);
+ throw Throwables.propagate(e);
+ }
+ };
+ }
+
+ private BodyPart attachmentBodyPart(MessageAttachment att) throws IOException {
+ BodyPartBuilder builder = BodyPartBuilder.create()
+ .use(bodyFactory)
+ .setBody(IOUtils.toString(att.getAttachment().getStream()), Charsets.UTF_8)
+ .setField(contentTypeField(att))
+ .setField(contentDispositionField(att.isInline()));
+ contentId(builder, att);
+ return builder.build();
+ }
+
+ private void contentId(BodyPartBuilder builder, MessageAttachment att) {
+ if (att.getCid().isPresent()) {
+ builder.setField(new RawField("Content-ID", att.getCid().get()));
+ }
+ }
+
+ private ContentTypeField contentTypeField(MessageAttachment att) {
+ Builder<String, String> parameters = ImmutableMap.<String, String> builder();
+ if (att.getName().isPresent()) {
+ parameters.put("name", att.getName().get());
+ }
+ String type = att.getAttachment().getType();
+ if (type.contains(FIELD_PARAMETERS_SEPARATOR)) {
+ return Fields.contentType(contentTypeWithoutParameters(type), parameters.build());
+ }
+ return Fields.contentType(type, parameters.build());
+ }
+
+ private String contentTypeWithoutParameters(String type) {
+ return FluentIterable.from(Splitter.on(FIELD_PARAMETERS_SEPARATOR).split(type)).get(0);
+ }
+
+ private ContentDispositionField contentDispositionField(boolean isInline) {
+ if (isInline) {
+ return Fields.contentDisposition("inline");
+ }
+ return Fields.contentDisposition("attachment");
+ }
+
private Mailbox convertEmailToMimeHeader(DraftEmailer address) {
if (!address.hasValidEmail()) {
throw new IllegalArgumentException("address");
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
index 3da428b..9d6210e 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
@@ -26,6 +26,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.inject.Inject;
import javax.mail.Flags;
@@ -33,8 +34,11 @@ import javax.mail.MessagingException;
import javax.mail.internet.SharedInputStream;
import javax.mail.util.SharedByteArrayInputStream;
+import org.apache.james.jmap.exceptions.AttachmentsNotFoundException;
import org.apache.james.jmap.methods.ValueWithId.CreationMessageEntry;
import org.apache.james.jmap.methods.ValueWithId.MessageWithId;
+import org.apache.james.jmap.model.Attachment;
+import org.apache.james.jmap.model.BlobId;
import org.apache.james.jmap.model.CreationMessage;
import org.apache.james.jmap.model.CreationMessageId;
import org.apache.james.jmap.model.Message;
@@ -43,6 +47,7 @@ import org.apache.james.jmap.model.MessageId;
import org.apache.james.jmap.model.MessageProperties;
import org.apache.james.jmap.model.MessageProperties.MessageProperty;
import org.apache.james.jmap.model.SetError;
+import org.apache.james.jmap.model.SetMessagesError;
import org.apache.james.jmap.model.SetMessagesRequest;
import org.apache.james.jmap.model.SetMessagesResponse;
import org.apache.james.jmap.model.SetMessagesResponse.Builder;
@@ -52,16 +57,22 @@ import org.apache.james.jmap.send.MailMetadata;
import org.apache.james.jmap.send.MailSpool;
import org.apache.james.jmap.utils.SystemMailboxesProvider;
import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.AttachmentNotFoundException;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.MailboxNotFoundException;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
+import org.apache.james.mailbox.store.mail.AttachmentMapper;
+import org.apache.james.mailbox.store.mail.AttachmentMapperFactory;
import org.apache.james.mailbox.store.mail.MessageMapper;
+import org.apache.james.mailbox.store.mail.model.AttachmentId;
import org.apache.james.mailbox.store.mail.model.Mailbox;
import org.apache.james.mailbox.store.mail.model.MailboxId;
import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.MessageAttachment;
import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
+import org.apache.james.util.streams.ImmutableCollectors;
import org.apache.mailet.Mail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,6 +91,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
private final MailFactory mailFactory;
private final MessageFactory messageFactory;
private final SystemMailboxesProvider systemMailboxesProvider;
+ private AttachmentMapperFactory attachmentMapperFactory;
@VisibleForTesting @Inject
@@ -88,13 +100,15 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
MailSpool mailSpool,
MailFactory mailFactory,
MessageFactory messageFactory,
- SystemMailboxesProvider systemMailboxesProvider) {
+ SystemMailboxesProvider systemMailboxesProvider,
+ AttachmentMapperFactory attachmentMapperFactory) {
this.mailboxSessionMapperFactory = mailboxSessionMapperFactory;
this.mimeMessageConverter = mimeMessageConverter;
this.mailSpool = mailSpool;
this.mailFactory = mailFactory;
this.messageFactory = messageFactory;
this.systemMailboxesProvider = systemMailboxesProvider;
+ this.attachmentMapperFactory = attachmentMapperFactory;
}
@Override
@@ -109,7 +123,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
private void handleCreate(CreationMessageEntry create, Builder responseBuilder, MailboxSession mailboxSession) {
try {
validateImplementedFeature(create, mailboxSession);
- validateArguments(create);
+ validateArguments(create, mailboxSession);
validateRights(create, mailboxSession);
MessageWithId created = handleOutboxMessages(create, mailboxSession);
responseBuilder.created(created.getCreationId(), created.getValue());
@@ -123,6 +137,15 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
Joiner.on(", ").join(e.getAllowedFroms()))
.build());
+ } catch (AttachmentsNotFoundException e) {
+ responseBuilder.notCreated(create.getCreationId(),
+ SetMessagesError.builder()
+ .type("invalidProperties")
+ .properties(MessageProperty.mailboxIds)
+ .attachmentsNotFound(e.getAttachmentIds())
+ .description("Attachment not found")
+ .build());
+
} catch (MailboxNotImplementedException e) {
responseBuilder.notCreated(create.getCreationId(),
SetError.builder()
@@ -161,13 +184,41 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
}
}
- private void validateArguments(CreationMessageEntry entry) throws MailboxInvalidMessageCreationException {
+ private void validateArguments(CreationMessageEntry entry, MailboxSession session) throws MailboxInvalidMessageCreationException, AttachmentsNotFoundException, MailboxException {
CreationMessage message = entry.getValue();
if (!message.isValid()) {
throw new MailboxInvalidMessageCreationException();
}
+ assertAttachmentsExist(entry, session);
}
+ @VisibleForTesting void assertAttachmentsExist(CreationMessageEntry entry, MailboxSession session) throws AttachmentsNotFoundException, MailboxException {
+ List<Attachment> attachments = entry.getValue().getAttachments();
+ if (!attachments.isEmpty()) {
+ AttachmentMapper attachmentMapper = attachmentMapperFactory.getAttachmentMapper(session);
+ List<BlobId> notFounds = listAttachmentsNotFound(attachments, attachmentMapper);
+ if (!notFounds.isEmpty()) {
+ throw new AttachmentsNotFoundException(notFounds);
+ }
+ }
+ }
+
+ private List<BlobId> listAttachmentsNotFound(List<Attachment> attachments, AttachmentMapper attachmentMapper) {
+ return attachments.stream()
+ .flatMap(attachment -> {
+ try {
+ attachmentMapper.getAttachment(getAttachmentId(attachment));
+ return Stream.of();
+ } catch (AttachmentNotFoundException e) {
+ return Stream.of(attachment.getBlobId());
+ }
+ }).collect(ImmutableCollectors.toImmutableList());
+ }
+
+ private AttachmentId getAttachmentId(Attachment attachment) {
+ return AttachmentId.from(attachment.getBlobId().getRawValue());
+ }
+
private void validateRights(CreationMessageEntry entry, MailboxSession session) throws MailboxSendingNotAllowedException {
List<String> allowedSenders = ImmutableList.of(session.getUser().getUserName());
if (!isAllowedFromAddress(entry.getValue(), allowedSenders)) {
@@ -200,9 +251,9 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
CreationMessageId creationId = createdEntry.getCreationId();
MessageMapper messageMapper = mailboxSessionMapperFactory.createMessageMapper(session);
- MailboxMessage newMailboxMessage = buildMailboxMessage(createdEntry, outbox);
+ MailboxMessage newMailboxMessage = buildMailboxMessage(session, createdEntry, outbox);
messageMapper.add(outbox, newMailboxMessage);
- Message jmapMessage = messageFactory.fromMailboxMessage(newMailboxMessage, ImmutableList.of(), buildMessageIdFromUid);
+ Message jmapMessage = messageFactory.fromMailboxMessage(newMailboxMessage, newMailboxMessage.getAttachments(), buildMessageIdFromUid);
sendMessage(newMailboxMessage, jmapMessage, session);
return new MessageWithId(creationId, jmapMessage);
}
@@ -248,8 +299,9 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
return new MessageId(session.getUser(), outboxPath, uid);
}
- private MailboxMessage buildMailboxMessage(MessageWithId.CreationMessageEntry createdEntry, Mailbox outbox) {
- byte[] messageContent = mimeMessageConverter.convert(createdEntry);
+ private MailboxMessage buildMailboxMessage(MailboxSession session, MessageWithId.CreationMessageEntry createdEntry, Mailbox outbox) throws MailboxException {
+ ImmutableList<MessageAttachment> messageAttachments = getMessageAttachments(session, createdEntry.getValue().getAttachments());
+ byte[] messageContent = mimeMessageConverter.convert(createdEntry, messageAttachments);
SharedInputStream content = new SharedByteArrayInputStream(messageContent);
long size = messageContent.length;
int bodyStartOctet = 0;
@@ -260,7 +312,29 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
return new SimpleMailboxMessage(internalDate, size,
- bodyStartOctet, content, flags, propertyBuilder, mailboxId);
+ bodyStartOctet, content, flags, propertyBuilder, mailboxId, messageAttachments);
+ }
+
+ private ImmutableList<MessageAttachment> getMessageAttachments(MailboxSession session, ImmutableList<Attachment> attachments) throws MailboxException {
+ AttachmentMapper attachmentMapper = attachmentMapperFactory.getAttachmentMapper(session);
+ return attachments.stream()
+ .map(att -> messageAttachment(attachmentMapper, att))
+ .collect(ImmutableCollectors.toImmutableList());
+ }
+
+ private MessageAttachment messageAttachment(AttachmentMapper attachmentMapper, Attachment attachment) {
+ try {
+ return MessageAttachment.builder()
+ .attachment(attachmentMapper.getAttachment(AttachmentId.from(attachment.getBlobId().getRawValue())))
+ .name(attachment.getName().orElse(null))
+ .cid(attachment.getCid().orElse(null))
+ .isInline(attachment.isIsInline())
+ .build();
+ } catch (AttachmentNotFoundException e) {
+ // should not happen (checked before)
+ LOG.error(String.format("Attachment %s not found", attachment.getBlobId()), e);
+ return null;
+ }
}
private PropertyBuilder buildPropertyBuilder() {
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
index 60f7b91..5202a15 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.mail.internet.AddressException;
@@ -73,7 +74,7 @@ public class CreationMessage {
private String textBody;
private String htmlBody;
private final ImmutableList.Builder<Attachment> attachments;
- private final ImmutableMap.Builder<String, SubMessage> attachedMessages;
+ private final ImmutableMap.Builder<BlobId, SubMessage> attachedMessages;
private Builder() {
to = ImmutableList.builder();
@@ -89,6 +90,7 @@ public class CreationMessage {
return mailboxIds(Arrays.asList(mailboxIds));
}
+ @JsonDeserialize
public Builder mailboxIds(List<String> mailboxIds) {
this.mailboxIds = ImmutableList.copyOf(mailboxIds);
return this;
@@ -169,27 +171,39 @@ public class CreationMessage {
return this;
}
+ public Builder attachments(Attachment... attachments) {
+ return attachments(Arrays.asList(attachments));
+ }
+
+ @JsonDeserialize
public Builder attachments(List<Attachment> attachments) {
this.attachments.addAll(attachments);
return this;
}
- public Builder attachedMessages(Map<String, SubMessage> attachedMessages) {
+ public Builder attachedMessages(Map<BlobId, SubMessage> attachedMessages) {
this.attachedMessages.putAll(attachedMessages);
return this;
}
- private static boolean areAttachedMessagesKeysInAttachments(ImmutableList<Attachment> attachments, ImmutableMap<String, SubMessage> attachedMessages) {
- return attachments.stream()
+ private static boolean areAttachedMessagesKeysInAttachments(ImmutableList<Attachment> attachments, ImmutableMap<BlobId, SubMessage> attachedMessages) {
+ return attachedMessages.isEmpty() || attachedMessages.keySet().stream()
+ .anyMatch(inAttachments(attachments));
+ }
+
+ private static Predicate<BlobId> inAttachments(ImmutableList<Attachment> attachments) {
+ return (key) -> {
+ return attachments.stream()
.map(Attachment::getBlobId)
- .allMatch(attachedMessages::containsKey);
+ .anyMatch(blobId -> blobId.equals(key));
+ };
}
public CreationMessage build() {
Preconditions.checkState(mailboxIds != null, "'mailboxIds' is mandatory");
Preconditions.checkState(headers != null, "'headers' is mandatory");
ImmutableList<Attachment> attachments = this.attachments.build();
- ImmutableMap<String, SubMessage> attachedMessages = this.attachedMessages.build();
+ ImmutableMap<BlobId, SubMessage> attachedMessages = this.attachedMessages.build();
Preconditions.checkState(areAttachedMessagesKeysInAttachments(attachments, attachedMessages), "'attachedMessages' keys must be in 'attachments'");
if (date == null) {
@@ -218,12 +232,12 @@ public class CreationMessage {
private final Optional<String> textBody;
private final Optional<String> htmlBody;
private final ImmutableList<Attachment> attachments;
- private final ImmutableMap<String, SubMessage> attachedMessages;
+ private final ImmutableMap<BlobId, SubMessage> attachedMessages;
@VisibleForTesting
CreationMessage(ImmutableList<String> mailboxIds, Optional<String> inReplyToMessageId, boolean isUnread, boolean isFlagged, boolean isAnswered, boolean isDraft, ImmutableMap<String, String> headers, Optional<DraftEmailer> from,
ImmutableList<DraftEmailer> to, ImmutableList<DraftEmailer> cc, ImmutableList<DraftEmailer> bcc, ImmutableList<DraftEmailer> replyTo, String subject, ZonedDateTime date, Optional<String> textBody, Optional<String> htmlBody, ImmutableList<Attachment> attachments,
- ImmutableMap<String, SubMessage> attachedMessages) {
+ ImmutableMap<BlobId, SubMessage> attachedMessages) {
this.mailboxIds = mailboxIds;
this.inReplyToMessageId = inReplyToMessageId;
this.isUnread = isUnread;
@@ -312,7 +326,7 @@ public class CreationMessage {
return attachments;
}
- public ImmutableMap<String, SubMessage> getAttachedMessages() {
+ public ImmutableMap<BlobId, SubMessage> getAttachedMessages() {
return attachedMessages;
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
index 658491c..cda1aef 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
@@ -49,7 +49,7 @@ public class SetError {
private String description;
private Optional<ImmutableSet<MessageProperty>> properties = Optional.empty();
- private Builder() {
+ protected Builder() {
}
public Builder type(String type) {
@@ -91,6 +91,13 @@ public class SetError {
this.properties = properties;
}
+ protected SetError(SetError setError) {
+ this.type = setError.type;
+ this.description = setError.description;
+ this.properties = setError.properties;
+ }
+
+
@JsonSerialize
public String getType() {
return type;
http://git-wip-us.apache.org/repos/asf/james-project/blob/662fa4a9/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesError.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesError.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesError.java
new file mode 100644
index 0000000..2cf19e0
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesError.java
@@ -0,0 +1,92 @@
+/****************************************************************
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.james.jmap.model.MessageProperties.MessageProperty;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.google.common.collect.ImmutableList;
+
+public class SetMessagesError extends SetError {
+
+ public static SetMessagesError.Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends SetError.Builder {
+
+ private List<BlobId> attachmentsNotFound;
+
+ private Builder() {
+ super();
+ attachmentsNotFound = new ArrayList<>();
+ }
+
+ @Override
+ public Builder description(String description) {
+ return (Builder) super.description(description);
+ }
+
+ @Override
+ public Builder properties(MessageProperty... properties) {
+ return (Builder) super.properties(properties);
+ }
+
+ @Override
+ public Builder properties(Set<MessageProperty> properties) {
+ return (Builder) super.properties(properties);
+ }
+
+ @Override
+ public Builder type(String type) {
+ return (Builder) super.type(type);
+ }
+
+ public Builder attachmentsNotFound(BlobId... attachmentIds) {
+ return attachmentsNotFound(Arrays.asList(attachmentIds));
+ }
+
+ public Builder attachmentsNotFound(List<BlobId> attachmentIds) {
+ this.attachmentsNotFound.addAll(attachmentIds);
+ return this;
+ }
+
+ @Override
+ public SetError build() {
+ return new SetMessagesError(super.build(), ImmutableList.copyOf(attachmentsNotFound));
+ }
+ }
+
+ private ImmutableList<BlobId> attachmentsNotFound;
+
+ public SetMessagesError(SetError setError, ImmutableList<BlobId> attachmentsNotFound) {
+ super(setError);
+ this.attachmentsNotFound = attachmentsNotFound;
+ }
+
+ @JsonSerialize
+ public List<BlobId> getAttachmentsNotFound() {
+ return attachmentsNotFound;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org