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 2017/11/28 13:50:28 UTC

[1/5] james-project git commit: JAMES-2236 Ignore headers with dots for ElasticSearch

Repository: james-project
Updated Branches:
  refs/heads/master 5f8f0d59b -> 1b49712c5


JAMES-2236 Ignore headers with dots for ElasticSearch


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/ec5bfaa1
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ec5bfaa1
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ec5bfaa1

Branch: refs/heads/master
Commit: ec5bfaa1779943c486b31d8da2c9d96be4ddb97d
Parents: ab677d0
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Thu Nov 23 17:11:35 2017 +0100
Committer: Raphael Ouazana <ra...@linagora.com>
Committed: Thu Nov 23 17:44:41 2017 +0100

----------------------------------------------------------------------
 .../elasticsearch/json/HeaderCollection.java       |  4 +++-
 .../elasticsearch/json/HeaderCollectionTest.java   | 10 ++++++++++
 .../search/AbstractMessageSearchIndexTest.java     | 17 +++++++++++++++++
 .../store/src/test/resources/eml/headerWithDot.eml |  7 +++++++
 4 files changed, 37 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ec5bfaa1/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java
----------------------------------------------------------------------
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java
index 85782ad..c4c5a5a 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollection.java
@@ -71,7 +71,9 @@ public class HeaderCollection {
             String headerName = field.getName().toLowerCase(Locale.US);
             String sanitizedValue = MimeUtil.unscrambleHeaderValue(field.getBody());
 
-            headers.put(headerName, sanitizedValue);
+            if (!headerName.contains(".")) {
+                headers.put(headerName, sanitizedValue);
+            }
             handleSpecificHeader(headerName, sanitizedValue);
             return this;
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/ec5bfaa1/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java
----------------------------------------------------------------------
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java
index 93a7b02..52b4002 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/HeaderCollectionTest.java
@@ -96,6 +96,16 @@ public class HeaderCollectionTest {
     }
 
     @Test
+    public void getHeadersShouldIgnoreHeadersWithDots() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("a.b.c", "value"))
+            .build();
+
+        assertThat(headerCollection.getHeaders().get("a.b.c"))
+            .isEmpty();
+    }
+
+    @Test
     public void addressWithTwoDisplayNamesOnTheSameFieldShouldBeRetrieved() {
         HeaderCollection headerCollection = HeaderCollection.builder()
             .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>, Graham CROSMARIE <gr...@linagora.com>"))

http://git-wip-us.apache.org/repos/asf/james-project/blob/ec5bfaa1/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
index 7b4676e..ad89eac 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
@@ -405,6 +405,23 @@ public abstract class AbstractMessageSearchIndexTest {
     }
 
     @Test
+    public void messageWithDotsInHeaderShouldBeIndexed() throws MailboxException {
+
+        ComposedMessageId mailWithDotsInHeader = myFolderMessageManager.appendMessage(
+                ClassLoader.getSystemResourceAsStream("eml/headerWithDot.eml"),
+                new Date(1409608900000L),
+                session,
+                RECENT,
+                new Flags());
+        await();
+        
+        SearchQuery searchQuery = new SearchQuery(SearchQuery.all());
+
+        assertThat(messageSearchIndex.search(session, mailbox2, searchQuery))
+            .contains(mailWithDotsInHeader.getUid());
+    }
+
+    @Test
     public void hasNoAttachmenShouldOnlyReturnMessageThatHasNoAttachmentWhichAreNotInline() throws MailboxException {
         SearchQuery searchQuery = new SearchQuery(SearchQuery.hasNoAttachment());
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/ec5bfaa1/mailbox/store/src/test/resources/eml/headerWithDot.eml
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/resources/eml/headerWithDot.eml b/mailbox/store/src/test/resources/eml/headerWithDot.eml
new file mode 100644
index 0000000..b9a41d9
--- /dev/null
+++ b/mailbox/store/src/test/resources/eml/headerWithDot.eml
@@ -0,0 +1,7 @@
+From: me@example.com
+To: you@example.com
+X-normal-header: normal header
+X-header.with.dots: header with dots
+Subject: Test me
+
+Please!


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[3/5] james-project git commit: Merge remote-tracking branch 'rouazana/JAMES-2236'

Posted by ad...@apache.org.
Merge remote-tracking branch 'rouazana/JAMES-2236'


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/28453d49
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/28453d49
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/28453d49

Branch: refs/heads/master
Commit: 28453d497f6a32b1323b8d2bc286d9853c8bcb7c
Parents: 022b216 ec5bfaa
Author: Antoine Duprat <ad...@linagora.com>
Authored: Tue Nov 28 14:49:11 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Tue Nov 28 14:49:11 2017 +0100

----------------------------------------------------------------------
 .../elasticsearch/json/HeaderCollection.java       |  4 +++-
 .../elasticsearch/json/HeaderCollectionTest.java   | 10 ++++++++++
 .../search/AbstractMessageSearchIndexTest.java     | 17 +++++++++++++++++
 .../store/src/test/resources/eml/headerWithDot.eml |  7 +++++++
 4 files changed, 37 insertions(+), 1 deletion(-)
----------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[5/5] james-project git commit: Merge remote-tracking branch 'mine/JAMES-2232'

Posted by ad...@apache.org.
Merge remote-tracking branch 'mine/JAMES-2232'


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/1b49712c
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/1b49712c
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/1b49712c

Branch: refs/heads/master
Commit: 1b49712c501ce7d3ddaee0b4313d01fdec762aab
Parents: 28453d4 35a6e92
Author: Antoine Duprat <ad...@linagora.com>
Authored: Tue Nov 28 14:49:55 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Tue Nov 28 14:49:55 2017 +0100

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 357 +++++++++++++++++++
 .../cucumber/SetMessagesMethodStepdefs.java     |  18 +
 .../test/resources/cucumber/SetMessages.feature |  11 +
 .../james/jmap/methods/MessageAppender.java     |  22 +-
 .../methods/SetMessagesCreationProcessor.java   |  21 +-
 .../methods/SetMessagesUpdateProcessor.java     |  36 +-
 .../james/jmap/model/CreationMessage.java       |  23 +-
 .../org/apache/james/jmap/model/Keywords.java   |  70 ++--
 .../org/apache/james/jmap/model/OldKeyword.java |  46 +++
 .../james/jmap/model/UpdateMessagePatch.java    |  25 +-
 .../james/jmap/utils/KeywordsCombiner.java      |   6 +-
 .../james/jmap/json/ParsingWritingObjects.java  |   2 +-
 .../james/jmap/model/CreationMessageTest.java   |  15 +
 .../apache/james/jmap/model/KeywordsTest.java   | 178 +--------
 .../apache/james/jmap/model/OldKeywordTest.java |  59 ++-
 .../jmap/model/UpdateMessagePatchTest.java      |  35 --
 16 files changed, 641 insertions(+), 283 deletions(-)
----------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[4/5] james-project git commit: JAMES-2232 Flags in JMAP should be handled by Keywords & is attributes

Posted by ad...@apache.org.
JAMES-2232 Flags in JMAP should be handled by Keywords & is attributes


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/35a6e923
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/35a6e923
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/35a6e923

Branch: refs/heads/master
Commit: 35a6e92304605fb95d2a9cbec607de22cef053d3
Parents: 5f8f0d5
Author: Antoine Duprat <ad...@linagora.com>
Authored: Thu Nov 23 15:12:25 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Tue Nov 28 14:49:41 2017 +0100

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 357 +++++++++++++++++++
 .../cucumber/SetMessagesMethodStepdefs.java     |  18 +
 .../test/resources/cucumber/SetMessages.feature |  11 +
 .../james/jmap/methods/MessageAppender.java     |  22 +-
 .../methods/SetMessagesCreationProcessor.java   |  21 +-
 .../methods/SetMessagesUpdateProcessor.java     |  36 +-
 .../james/jmap/model/CreationMessage.java       |  23 +-
 .../org/apache/james/jmap/model/Keywords.java   |  70 ++--
 .../org/apache/james/jmap/model/OldKeyword.java |  46 +++
 .../james/jmap/model/UpdateMessagePatch.java    |  25 +-
 .../james/jmap/utils/KeywordsCombiner.java      |   6 +-
 .../james/jmap/json/ParsingWritingObjects.java  |   2 +-
 .../james/jmap/model/CreationMessageTest.java   |  15 +
 .../apache/james/jmap/model/KeywordsTest.java   | 178 +--------
 .../apache/james/jmap/model/OldKeywordTest.java |  59 ++-
 .../jmap/model/UpdateMessagePatchTest.java      |  35 --
 16 files changed, 641 insertions(+), 283 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 00a28d1..2470b51 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
@@ -1429,6 +1429,47 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
+    public void setMessagesShouldMarkAsDraftWhenIsDraftPassed() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"isDraft\": true," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String messageId = given()
+            .header("Authorization", accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .extract()
+            .body()
+            .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id");
+
+        with()
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]")
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(ARGUMENTS + ".list[0].isDraft", equalTo(true));
+    }
+
+    @Test
     public void setMessagesShouldRejectCreateInDraftAndOutboxForASingleMessage() {
         String messageCreationId = "creationId1337";
         String fromAddress = USERNAME;
@@ -4852,4 +4893,320 @@ public abstract class SetMessagesMethodTest {
             .body(SECOND_ARGUMENTS + ".list", hasSize(1))
             .body(SECOND_ARGUMENTS + ".list[0].keywords.$Seen", equalTo(true));
     }
+
+    @Test
+    public void setMessagesShouldCreateMessageWithFlagsWhenFlagsAttributesAreGiven() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"isUnread\": true," +
+            "        \"isFlagged\": true," +
+            "        \"isAnswered\": true," +
+            "        \"isDraft\": true," +
+            "        \"isForwarded\": true," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String messageId = given()
+            .header("Authorization", accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .extract()
+            .body()
+            .<String>path(ARGUMENTS + ".created."+ messageCreationId +".id");
+
+        with()
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]")
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(ARGUMENTS + ".list[0].isUnread", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isDraft", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true));
+    }
+
+    @Test
+    public void setMessagesShouldUpdateFlagsWhenSomeAreAlreadySet() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"isDraft\": true," +
+            "        \"isForwarded\": true," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String messageId = given()
+            .header("Authorization", accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .extract()
+            .body()
+            .<String>path(ARGUMENTS + ".created."+ messageCreationId +".id");
+
+        String updateRequestBody = "[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"update\": { \"" + messageId  + "\" : {" +
+                "        \"isUnread\": true," +
+                "        \"isFlagged\": true," +
+                "        \"isAnswered\": true," +
+                "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(updateRequestBody)
+        .when()
+            .post("/jmap");
+
+        with()
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]")
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(ARGUMENTS + ".list[0].isUnread", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isDraft", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true));
+    }
+
+    @Test
+    public void setMessagesShouldRemoveFlagsWhenAskedFor() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"isUnread\": true," +
+            "        \"isFlagged\": true," +
+            "        \"isAnswered\": true," +
+            "        \"isDraft\": true," +
+            "        \"isForwarded\": true," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String messageId = given()
+            .header("Authorization", accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .extract()
+            .body()
+            .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id");
+
+        String updateRequestBody = "[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"update\": { \"" + messageId  + "\" : {" +
+                "        \"isUnread\": false," +
+                "        \"isFlagged\": false," +
+                "        \"isAnswered\": false," +
+                "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(updateRequestBody)
+        .when()
+            .post("/jmap");
+
+        with()
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]")
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(ARGUMENTS + ".list[0].isUnread", equalTo(false))
+            .body(ARGUMENTS + ".list[0].isFlagged", equalTo(false))
+            .body(ARGUMENTS + ".list[0].isAnswered", equalTo(false))
+            .body(ARGUMENTS + ".list[0].isDraft", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true));
+    }
+
+    @Test
+    public void setMessagesShouldReturnErrorWhenTryingToChangeDraftFlagAmongOthers() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"isUnread\": true," +
+            "        \"isFlagged\": true," +
+            "        \"isAnswered\": true," +
+            "        \"isDraft\": true," +
+            "        \"isForwarded\": true," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String messageId = given()
+            .header("Authorization", accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .extract()
+            .body()
+            .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id");
+
+        String updateRequestBody = "[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"update\": { \"" + messageId  + "\" : {" +
+                "        \"isUnread\": false," +
+                "        \"isFlagged\": false," +
+                "        \"isAnswered\": false," +
+                "        \"isDraft\": false," +
+                "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(updateRequestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(400);
+    }
+
+    @Test
+    public void setMessagesShouldNotModifyTheMessageWhenTryingToChangeDraftFlagAmongOthers() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"isUnread\": true," +
+            "        \"isFlagged\": true," +
+            "        \"isAnswered\": true," +
+            "        \"isDraft\": true," +
+            "        \"isForwarded\": true," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String messageId = given()
+            .header("Authorization", accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .extract()
+            .body()
+            .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id");
+
+        String updateRequestBody = "[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"update\": { \"" + messageId  + "\" : {" +
+                "        \"isUnread\": false," +
+                "        \"isFlagged\": false," +
+                "        \"isAnswered\": false," +
+                "        \"isDraft\": false," +
+                "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(updateRequestBody)
+        .when()
+            .post("/jmap");
+
+        with()
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]")
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(ARGUMENTS + ".list[0].isUnread", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isDraft", equalTo(true))
+            .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true));
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
index 8cdd65a..026ac90 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
@@ -202,6 +202,24 @@ public class SetMessagesMethodStepdefs {
         userStepdefs.execWithUser(username, () -> setFlags(keywords, message));
     }
 
+    @When("^\"([^\"]*)\" marks the message \"([^\"]*)\" as flagged$")
+    public void flag(String username, String message) throws Throwable {
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
+
+        httpClient.post("[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"update\": { \"" + messageId.serialize() + "\" : {" +
+            "        \"isFlagged\": true" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]");
+        mainStepdefs.awaitMethod.run();
+    }
+
     @When("^\"([^\"]*)\" destroys message \"([^\"]*)\"$")
     public void destroyMessage(String username, String message) throws Throwable {
         MessageId messageId = messageIdStepdefs.getMessageId(message);

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
index 46ede38..f3ac1bf 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
@@ -80,6 +80,11 @@ Feature: SetMessages method on shared folders
 
 # Flags update
 
+  Scenario: A user can update the flags on a message
+    Given "alice@domain.tld" sets flags "$Flagged,$Seen" on message "mAlice"
+    When "alice@domain.tld" sets flags "$Flagged,$Forwarded" on message "mAlice"
+    Then "alice@domain.tld" should see message "mAlice" with keywords $Flagged,$Forwarded
+
   Scenario: A delegated user add keywords on a delegated message when having "write" right
     Given "bob@domain.tld" shares his mailbox "shared" with "alice@domain.tld" with "lrw" rights
     When "alice@domain.tld" sets flags "$Flagged" on message "mBob"
@@ -117,6 +122,12 @@ Feature: SetMessages method on shared folders
     Then message "mDraft" is not updated
     And "bob@domain.tld" should see message "mDraft" with keywords $Draft
 
+  Scenario: A user can add a flag on a draft
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"
+    When "bob@domain.tld" marks the message "mDraft" as flagged
+    Then "bob@domain.tld" should see message "mDraft" with keywords $Draft,$Flagged
+
   Scenario: A user can destroy a draft
     Given "bob@domain.tld" has a mailbox "Drafts"
     And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
index 4d09810..763ba27 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
@@ -23,12 +23,15 @@ import java.util.Date;
 import java.util.Optional;
 
 import javax.inject.Inject;
+import javax.mail.Flags;
 import javax.mail.util.SharedByteArrayInputStream;
 
 import org.apache.james.jmap.methods.ValueWithId.CreationMessageEntry;
 import org.apache.james.jmap.model.Attachment;
+import org.apache.james.jmap.model.CreationMessage;
 import org.apache.james.jmap.model.Keywords;
 import org.apache.james.jmap.model.MessageFactory;
+import org.apache.james.jmap.model.OldKeyword;
 import org.apache.james.mailbox.AttachmentManager;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
@@ -71,16 +74,13 @@ public class MessageAppender {
         SharedByteArrayInputStream content = new SharedByteArrayInputStream(messageContent);
         Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
 
-        Keywords keywords = createdEntry.getValue()
-            .getKeywords()
-            .orElse(Keywords.DEFAULT_VALUE);
         boolean notRecent = false;
 
-        ComposedMessageId message = mailbox.appendMessage(content, internalDate, session, notRecent, keywords.asFlags());
+        ComposedMessageId message = mailbox.appendMessage(content, internalDate, session, notRecent, getFlags(createdEntry.getValue()));
 
         return MessageFactory.MetaDataWithContent.builder()
             .uid(message.getUid())
-            .keywords(keywords)
+            .keywords(keywordsOrDefault(createdEntry.getValue()))
             .internalDate(internalDate.toInstant())
             .sharedContent(content)
             .size(messageContent.length)
@@ -90,6 +90,18 @@ public class MessageAppender {
             .build();
     }
 
+    private Flags getFlags(CreationMessage message) {
+        return message.getOldKeyword()
+                .map(OldKeyword::asFlags)
+                .orElseGet(() -> keywordsOrDefault(message)
+                                    .asFlags());
+    }
+
+    private Keywords keywordsOrDefault(CreationMessage message) {
+        return message.getKeywords()
+                .orElse(Keywords.DEFAULT_VALUE);
+    }
+
     public MessageFactory.MetaDataWithContent appendMessageInMailbox(CreationMessageEntry createdEntry,
                                                                      MailboxId mailboxId,
                                                                      MailboxSession session) throws MailboxException {

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 964ceeb..2f60b82 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
@@ -189,32 +189,37 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
 
     private void sendMailViaOutbox(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws AttachmentsNotFoundException, MailboxException, MessagingException {
         validateArguments(entry, session);
-        assertNoDraftKeywords(entry);
+        assertNoDraftKeywords(entry.getValue());
         MessageWithId created = handleOutboxMessages(entry, session);
         responseBuilder.created(created.getCreationId(), created.getValue());
     }
 
     private void saveDraft(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws AttachmentsNotFoundException, MailboxException, MessagingException {
         attachmentChecker.assertAttachmentsExist(entry, session);
-        assertDraftKeywords(entry);
+        assertDraftKeywords(entry.getValue());
         MessageWithId created = handleDraftMessages(entry, session);
         responseBuilder.created(created.getCreationId(), created.getValue());
     }
 
-    private void assertDraftKeywords(CreationMessageEntry entry) {
-        if (!isDraft(entry)) {
+    private void assertDraftKeywords(CreationMessage creationMessage) {
+        if (!isDraft(creationMessage)) {
             throw new InvalidDraftKeywordsException("A draft message should be flagged as Draft");
         }
     }
 
-    private void assertNoDraftKeywords(CreationMessageEntry entry) {
-        if (isDraft(entry)) {
+    private void assertNoDraftKeywords(CreationMessage creationMessage) {
+        if (isDraft(creationMessage)) {
             throw new InvalidDraftKeywordsException("A draft message can not be created out of draft mailbox");
         }
     }
 
-    private Boolean isDraft(CreationMessageEntry entry) {
-        return entry.getValue()
+    private Boolean isDraft(CreationMessage creationMessage) {
+        if (creationMessage.getOldKeyword().isPresent()) {
+            return creationMessage.getOldKeyword().get()
+                        .isDraft()
+                        .orElse(false);
+        }
+        return creationMessage
             .getKeywords()
             .map(keywords -> keywords.contains(Keyword.DRAFT))
             .orElse(false);

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
index e9de2f9..f7cbe62 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
@@ -40,6 +40,7 @@ import org.apache.james.core.MailAddress;
 import org.apache.james.jmap.exceptions.DraftMessageMailboxUpdateException;
 import org.apache.james.jmap.exceptions.InvalidOutboxMoveException;
 import org.apache.james.jmap.model.MessageProperties;
+import org.apache.james.jmap.model.OldKeyword;
 import org.apache.james.jmap.model.SetError;
 import org.apache.james.jmap.model.SetMessagesRequest;
 import org.apache.james.jmap.model.SetMessagesResponse;
@@ -49,6 +50,7 @@ import org.apache.james.jmap.utils.SystemMailboxesProvider;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageIdManager;
 import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.MessageManager.FlagsUpdateMode;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.FetchGroupImpl;
@@ -242,10 +244,23 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
     }
 
     private List<Flags> patchFlags(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) {
+        return updateMessagePatch.getOldKeyword()
+                .map(oldKeyword -> flagsFromOldKeyword(messagesToBeUpdated, oldKeyword))
+                .orElse(flagsFromKeywords(messagesToBeUpdated, updateMessagePatch));
+    }
+
+    private List<Flags> flagsFromOldKeyword(List<MessageResult> messagesToBeUpdated, OldKeyword oldKeyword) {
         return messagesToBeUpdated.stream()
-            .map(MessageResult::getFlags)
-            .map(updateMessagePatch::applyToState)
-            .collect(Guavate.toImmutableList());
+                .map(MessageResult::getFlags)
+                .map(flags -> oldKeyword.applyToState(flags))
+                .collect(Guavate.toImmutableList());
+    }
+
+    private ImmutableList<Flags> flagsFromKeywords(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) {
+        return messagesToBeUpdated.stream()
+                .map(MessageResult::getFlags)
+                .map(updateMessagePatch::applyToState)
+                .collect(Guavate.toImmutableList());
     }
 
     private List<MailboxId> mailboxIdFor(Role role, MailboxSession session) throws MailboxException {
@@ -285,8 +300,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
     private Stream<MailboxException> updateFlags(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, MessageResult messageResult) {
         try {
             if (!updateMessagePatch.isFlagsIdentity()) {
-                Flags newState = updateMessagePatch.applyToState(messageResult.getFlags());
-                messageIdManager.setFlags(newState, MessageManager.FlagsUpdateMode.REPLACE, messageId, ImmutableList.of(messageResult.getMailboxId()), mailboxSession);
+                messageIdManager.setFlags(newState(messageResult, updateMessagePatch), FlagsUpdateMode.REPLACE, messageId, ImmutableList.of(messageResult.getMailboxId()), mailboxSession);
             }
             return Stream.of();
         } catch (MailboxException e) {
@@ -294,6 +308,18 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
         }
     }
 
+    private Flags newState(MessageResult messageResult, UpdateMessagePatch updateMessagePatch) {
+        return updateMessagePatch.getOldKeyword()
+                .map(oldKeyword -> firstFlagsFromOldKeyword(ImmutableList.of(messageResult), oldKeyword))
+                .orElse(updateMessagePatch.applyToState(messageResult.getFlags()));
+    }
+
+    private Flags firstFlagsFromOldKeyword(List<MessageResult> messagesToBeUpdated, OldKeyword oldKeyword) {
+        return flagsFromOldKeyword(messagesToBeUpdated, oldKeyword).stream()
+                .findFirst()
+                .orElse(null);
+    }
+
     private void setInMailboxes(MessageId messageId, UpdateMessagePatch updateMessagePatch, Stream<Flags> originalFlags, MailboxSession mailboxSession) throws MailboxException {
         Optional<List<String>> serializedMailboxIds = updateMessagePatch.getMailboxIds();
         if (serializedMailboxIds.isPresent()) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 097d172..1fca744 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
@@ -218,17 +218,22 @@ public class CreationMessage {
             ImmutableMap<BlobId, SubMessage> attachedMessages = this.attachedMessages.build();
             Preconditions.checkState(areAttachedMessagesKeysInAttachments(attachments, attachedMessages), "'attachedMessages' keys must be in 'attachments'");
 
-            Optional<Keywords> creationKeywords = Keywords.factory()
-                .throwOnImapNonExposedKeywords()
-                .fromMapOrOldKeyword(keywords, getOldKeywords());
-
             if (date == null) {
                 date = ZonedDateTime.now();
             }
 
+            Optional<Keywords> maybeKeywords = creationKeywords();
+            Optional<OldKeyword> oldKeywords = getOldKeywords();
+            Preconditions.checkArgument(!(maybeKeywords.isPresent() && oldKeywords.isPresent()), "Does not support keyword and is* at the same time");
             return new CreationMessage(mailboxIds, Optional.ofNullable(inReplyToMessageId), headers.build(), from,
                     to.build(), cc.build(), bcc.build(), replyTo.build(), subject, date, Optional.ofNullable(textBody), Optional.ofNullable(htmlBody),
-                    attachments, attachedMessages, creationKeywords);
+                    attachments, attachedMessages, maybeKeywords, oldKeywords);
+        }
+
+        private Optional<Keywords> creationKeywords() {
+            return keywords.map(map -> Keywords.factory()
+                    .throwOnImapNonExposedKeywords()
+                    .fromMap(map));
         }
 
         private Optional<OldKeyword> getOldKeywords() {
@@ -254,11 +259,12 @@ public class CreationMessage {
     private final ImmutableList<Attachment> attachments;
     private final ImmutableMap<BlobId, SubMessage> attachedMessages;
     private final Optional<Keywords> keywords;
+    private final Optional<OldKeyword> oldKeyword;
 
     @VisibleForTesting
     CreationMessage(ImmutableList<String> mailboxIds, Optional<String> inReplyToMessageId, 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<BlobId, SubMessage> attachedMessages, Optional<Keywords> keywords) {
+                    ImmutableMap<BlobId, SubMessage> attachedMessages, Optional<Keywords> keywords, Optional<OldKeyword> oldKeyword) {
         this.mailboxIds = mailboxIds;
         this.inReplyToMessageId = inReplyToMessageId;
         this.headers = headers;
@@ -274,6 +280,7 @@ public class CreationMessage {
         this.attachments = attachments;
         this.attachedMessages = attachedMessages;
         this.keywords = keywords;
+        this.oldKeyword = oldKeyword;
     }
 
     public ImmutableList<String> getMailboxIds() {
@@ -340,6 +347,10 @@ public class CreationMessage {
         return keywords;
     }
 
+    public Optional<OldKeyword> getOldKeyword() {
+        return oldKeyword;
+    }
+
     public List<ValidationResult> validate() {
         ImmutableList.Builder<ValidationResult> errors = ImmutableList.builder();
         assertValidFromProvided(errors);

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
index 286468f..5df79fa 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
@@ -43,7 +43,7 @@ import com.google.common.collect.ImmutableSet;
 
 public class Keywords {
 
-    public static final Keywords DEFAULT_VALUE = factory().fromSet(ImmutableSet.of());
+    public static final Keywords DEFAULT_VALUE = factory().fromSet(ImmutableSet.of(), Optional.empty());
     private static final Logger LOGGER = LoggerFactory.getLogger(Keywords.class);
 
     public interface KeywordsValidator {
@@ -78,24 +78,27 @@ public class Keywords {
             return this;
         }
 
-        public Keywords fromSet(Set<Keyword> setKeywords) {
+        public Keywords fromSet(Set<Keyword> setKeywords, Optional<OldKeyword> oldKeyword) {
             validator.orElse(keywords -> {})
                 .validate(setKeywords);
 
             return new Keywords(setKeywords.stream()
-                .filter(filter.orElse(keyword -> true))
-                .collect(Guavate.toImmutableSet()));
+                    .filter(filter.orElse(keyword -> true))
+                    .collect(Guavate.toImmutableSet()),
+                oldKeyword);
         }
 
         public Keywords from(Keyword... keywords) {
             return fromSet(Arrays.stream(keywords)
-                .collect(Guavate.toImmutableSet()));
+                    .collect(Guavate.toImmutableSet()),
+                Optional.empty());
         }
 
         public Keywords fromList(List<String> keywords) {
             return fromSet(keywords.stream()
-                .map(Keyword::new)
-                .collect(Guavate.toImmutableSet()));
+                    .map(Keyword::new)
+                    .collect(Guavate.toImmutableSet()),
+                Optional.empty());
         }
 
         @VisibleForTesting
@@ -108,37 +111,17 @@ public class Keywords {
                 .map(Keyword::new)
                 .collect(Guavate.toImmutableSet());
 
-            return fromSet(setKeywords);
-        }
-
-        @VisibleForTesting
-        Keywords fromOldKeyword(OldKeyword oldKeyword) {
-            ImmutableSet.Builder<Keyword> builder = ImmutableSet.builder();
-            if (oldKeyword.isAnswered().orElse(false)) {
-                builder.add(Keyword.ANSWERED);
-            }
-            if (oldKeyword.isDraft().orElse(false)) {
-                builder.add(Keyword.DRAFT);
-            }
-            if (oldKeyword.isFlagged().orElse(false)) {
-                builder.add(Keyword.FLAGGED);
-            }
-            if (oldKeyword.isUnread().isPresent() && oldKeyword.isUnread().get() == false) {
-                builder.add(Keyword.SEEN);
-            }
-            if (oldKeyword.isForwarded().orElse(false)) {
-                builder.add(Keyword.FORWARDED);
-            }
-            return fromSet(builder.build());
+            return fromSet(setKeywords, Optional.empty());
         }
 
         public Keywords fromFlags(Flags flags) {
             return fromSet(Stream.concat(
-                    Stream.of(flags.getUserFlags())
-                        .flatMap(this::asKeyword),
-                    Stream.of(flags.getSystemFlags())
-                        .map(Keyword::fromFlag))
-                .collect(Guavate.toImmutableSet()));
+                        Stream.of(flags.getUserFlags())
+                            .flatMap(this::asKeyword),
+                        Stream.of(flags.getSystemFlags())
+                            .map(Keyword::fromFlag))
+                    .collect(Guavate.toImmutableSet()),
+                Optional.empty());
         }
 
         private Stream<Keyword> asKeyword(String flagName) {
@@ -149,15 +132,6 @@ public class Keywords {
                 return Stream.of();
             }
         }
-
-        public Optional<Keywords> fromMapOrOldKeyword(Optional<Map<String, Boolean>> mapKeyword, Optional<OldKeyword> oldKeyword) {
-            Preconditions.checkArgument(!(mapKeyword.isPresent() && oldKeyword.isPresent()), "Does not support keyword and is* at the same time");
-
-            Keywords keywords = mapKeyword.map(this::fromMap)
-                .orElse(oldKeyword.map(this::fromOldKeyword)
-                    .orElse(null));
-            return Optional.ofNullable(keywords);
-        }
     }
 
     public static KeywordsFactory factory() {
@@ -165,9 +139,11 @@ public class Keywords {
     }
 
     private final ImmutableSet<Keyword> keywords;
+    private final Optional<OldKeyword> oldKeyword;
 
-    private Keywords(ImmutableSet<Keyword> keywords) {
+    private Keywords(ImmutableSet<Keyword> keywords, Optional<OldKeyword> oldKeyword) {
         this.keywords = keywords;
+        this.oldKeyword = oldKeyword;
     }
 
     public Flags asFlags() {
@@ -205,20 +181,22 @@ public class Keywords {
     public final boolean equals(Object other) {
         if (other instanceof Keywords) {
             Keywords otherKeyword = (Keywords) other;
-            return Objects.equal(keywords, otherKeyword.keywords);
+            return Objects.equal(keywords, otherKeyword.keywords)
+                && Objects.equal(oldKeyword, otherKeyword.oldKeyword);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hashCode(keywords);
+        return Objects.hashCode(keywords, oldKeyword);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
             .add("keywords", keywords)
+            .add("oldKeyword", oldKeyword)
             .toString();
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
index 207b275..7620873 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
@@ -21,6 +21,8 @@ package org.apache.james.jmap.model;
 
 import java.util.Optional;
 
+import javax.mail.Flags;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
@@ -70,6 +72,50 @@ public class OldKeyword {
         return isForwarded;
     }
 
+    public Flags applyToState(Flags currentFlags) {
+        Flags newStateFlags = new Flags();
+
+        if (isFlagged().orElse(currentFlags.contains(Flags.Flag.FLAGGED))) {
+            newStateFlags.add(Flags.Flag.FLAGGED);
+        }
+        if (isAnswered().orElse(currentFlags.contains(Flags.Flag.ANSWERED))) {
+            newStateFlags.add(Flags.Flag.ANSWERED);
+        }
+        if (isDraft().orElse(currentFlags.contains(Flags.Flag.DRAFT))) {
+            newStateFlags.add(Flags.Flag.DRAFT);
+        }
+        if (isForwarded().orElse(currentFlags.contains(new Flags("$Forwarded")))) {
+            newStateFlags.add(new Flags("$Forwarded"));
+        }
+        boolean shouldMessageBeMarkSeen = isUnread().map(b -> !b).orElse(currentFlags.contains(Flags.Flag.SEEN));
+        if (shouldMessageBeMarkSeen) {
+            newStateFlags.add(Flags.Flag.SEEN);
+        }
+        return newStateFlags;
+    }
+
+    public Flags asFlags() {
+        Flags newStateFlags = new Flags();
+
+        if (isFlagged().orElse(false)) {
+            newStateFlags.add(Flags.Flag.FLAGGED);
+        }
+        if (isAnswered().orElse(false)) {
+            newStateFlags.add(Flags.Flag.ANSWERED);
+        }
+        if (isDraft().orElse(false)) {
+            newStateFlags.add(Flags.Flag.DRAFT);
+        }
+        if (isForwarded().orElse(false)) {
+            newStateFlags.add(new Flags("$Forwarded"));
+        }
+        boolean shouldMessageBeMarkSeen = isUnread().map(b -> !b).orElse(false);
+        if (shouldMessageBeMarkSeen) {
+            newStateFlags.add(Flags.Flag.SEEN);
+        }
+        return newStateFlags;
+    }
+
     @Override
     public final boolean equals(Object other) {
         if (other instanceof OldKeyword) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
index e0b73e2..e735778 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
@@ -31,6 +31,7 @@ import org.apache.james.jmap.methods.ValidationResult;
 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.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -96,11 +97,17 @@ public class UpdateMessagePatch {
                     .build()));
             }
 
-            Optional<Keywords> updateKeywords = Keywords.factory()
-                .throwOnImapNonExposedKeywords()
-                .fromMapOrOldKeyword(keywords, getOldKeywords());
+            Optional<Keywords> mayBeKeywords = creationKeywords();
+            Optional<OldKeyword> oldKeywords = getOldKeywords();
+            Preconditions.checkArgument(!(mayBeKeywords.isPresent() && oldKeywords.isPresent()), "Does not support keyword and is* at the same time");
 
-            return new UpdateMessagePatch(mailboxIds, updateKeywords, ImmutableList.copyOf(validationResult));
+            return new UpdateMessagePatch(mailboxIds, mayBeKeywords, oldKeywords, ImmutableList.copyOf(validationResult));
+        }
+
+        private Optional<Keywords> creationKeywords() {
+            return keywords.map(map -> Keywords.factory()
+                    .throwOnImapNonExposedKeywords()
+                    .fromMap(map));
         }
 
         private Optional<OldKeyword> getOldKeywords() {
@@ -115,16 +122,18 @@ public class UpdateMessagePatch {
 
     private final Optional<List<String>> mailboxIds;
     private final Optional<Keywords> keywords;
-
+    private final Optional<OldKeyword> oldKeywords;
     private final ImmutableList<ValidationResult> validationErrors;
 
     @VisibleForTesting
     UpdateMessagePatch(Optional<List<String>> mailboxIds,
                        Optional<Keywords> keywords,
+                       Optional<OldKeyword> oldKeywords,
                        ImmutableList<ValidationResult> validationResults) {
 
         this.mailboxIds = mailboxIds;
         this.keywords = keywords;
+        this.oldKeywords = oldKeywords;
         this.validationErrors = validationResults;
     }
 
@@ -136,8 +145,12 @@ public class UpdateMessagePatch {
         return keywords;
     }
 
+    public Optional<OldKeyword> getOldKeyword() {
+        return oldKeywords;
+    }
+
     public boolean isFlagsIdentity() {
-        return !keywords.isPresent();
+        return !oldKeywords.isPresent() && !keywords.isPresent();
     }
 
     public ImmutableList<ValidationResult> getValidationErrors() {

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java
index 258f5df..2dba575 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.utils;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.BinaryOperator;
 
@@ -45,8 +46,9 @@ public class KeywordsCombiner implements BinaryOperator<Keywords> {
     public Keywords apply(Keywords keywords, Keywords keywords2) {
         return keywordsFactory
             .fromSet(Sets.union(
-                union(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_NOT_TO_UNION),
-                intersect(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_TO_INTERSECT)));
+                        union(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_NOT_TO_UNION),
+                        intersect(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_TO_INTERSECT)),
+                    Optional.empty());
     }
 
     public Set<Keyword> union(Set<Keyword> set1, Set<Keyword> set2, List<Keyword> exceptKeywords) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java
index b81731a..427a6d9 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java
@@ -77,7 +77,7 @@ public interface ParsingWritingObjects {
             .threadId(Common.THREAD_ID)
             .mailboxIds(Common.MAILBOX_IDS)
             .inReplyToMessageId(Common.IN_REPLY_TO_MESSAGE_ID)
-            .keywords(Keywords.factory().fromSet(Common.KEYWORDS))
+            .keywords(Keywords.factory().fromSet(Common.KEYWORDS, Optional.empty()))
             .headers(Common.HEADERS)
             .from(Common.FROM)
             .to(Common.TO)

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java
index e7762aa..d31e0a1 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java
@@ -20,6 +20,9 @@
 package org.apache.james.jmap.model;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.Optional;
 
 import org.apache.james.jmap.methods.ValidationResult;
 import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
@@ -42,6 +45,18 @@ public class CreationMessageTest {
     }
 
     @Test
+    public void buildShouldThrowWhenBothMapAndOldKeyword() {
+        assertThatThrownBy(() -> CreationMessage.builder()
+                .mailboxIds(ImmutableList.of("ba9-0f-dead-beef"))
+                .headers(ImmutableMap.of())
+                .keywords(ImmutableMap.of("$Draft", true))
+                .isAnswered(Optional.of(true))
+                .build())
+            .isInstanceOf(IllegalArgumentException.class)
+            .hasMessage("Does not support keyword and is* at the same time");
+    }
+
+    @Test
     public void validateShouldReturnErrorWhenFromIsMissing() {
        testedBuilder = testedBuilder
                .subject("anything");

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
index 5fa1d56..4c3ca03 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
@@ -77,172 +77,16 @@ public class KeywordsTest {
     @Test
     public void fromSetShouldReturnKeywordsFromSetOfKeywords() throws Exception {
         Keywords keywords = Keywords.factory()
-            .fromSet(ImmutableSet.of(Keyword.ANSWERED));
+            .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty());
 
         assertThat(keywords.getKeywords())
             .containsOnly(Keyword.ANSWERED);
     }
 
     @Test
-    public void fromOldKeywordsShouldReturnKeywordsFromIsAnswered() throws Exception {
-        Optional<Boolean> isAnswered = Optional.of(true);
-        Optional<Boolean> isUnread = Optional.empty();
-        Optional<Boolean> isFlagged = Optional.empty();
-        Optional<Boolean> isDraft = Optional.empty();
-        Optional<Boolean> isForwarded = Optional.empty();
-        Keywords keywords = Keywords.factory()
-            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.ANSWERED);
-    }
-
-    @Test
-    public void fromOldKeywordsShouldReturnKeywordsFromIsForwarded() throws Exception {
-        Optional<Boolean> isAnswered = Optional.empty();
-        Optional<Boolean> isUnread = Optional.empty();
-        Optional<Boolean> isFlagged = Optional.empty();
-        Optional<Boolean> isDraft = Optional.empty();
-        Optional<Boolean> isForwarded = Optional.of(true);
-        Keywords keywords = Keywords.factory()
-            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.FORWARDED);
-    }
-
-    @Test
-    public void fromOldKeywordsShouldReturnKeywordsFromIsDraft() throws Exception {
-        Optional<Boolean> isAnswered = Optional.empty();
-        Optional<Boolean> isUnread = Optional.empty();
-        Optional<Boolean> isFlagged = Optional.empty();
-        Optional<Boolean> isDraft = Optional.of(true);
-        Optional<Boolean> isForwarded = Optional.empty();
-        Keywords keywords = Keywords.factory()
-            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.DRAFT);
-    }
-
-    @Test
-    public void fromOldKeywordsShouldReturnKeywordsFromIsFlagged() throws Exception {
-        Optional<Boolean> isAnswered = Optional.empty();
-        Optional<Boolean> isUnread = Optional.empty();
-        Optional<Boolean> isFlagged = Optional.of(true);
-        Optional<Boolean> isDraft = Optional.empty();
-        Optional<Boolean> isForwarded = Optional.empty();
-        Keywords keywords = Keywords.factory()
-            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.FLAGGED);
-    }
-
-    @Test
-    public void fromOldKeywordsShouldReturnKeywordsFromIsUnread() throws Exception {
-        Optional<Boolean> isAnswered = Optional.empty();
-        Optional<Boolean> isUnread = Optional.of(false);
-        Optional<Boolean> isFlagged = Optional.empty();
-        Optional<Boolean> isDraft = Optional.empty();
-        Optional<Boolean> isForwarded = Optional.empty();
-        Keywords keywords = Keywords.factory()
-            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.SEEN);
-    }
-
-    @Test
-    public void fromOldKeywordsShouldReturnKeywordsFromIsFlag() throws Exception {
-        Optional<Boolean> isAnswered = Optional.of(true);
-        Optional<Boolean> isUnread = Optional.of(true);
-        Optional<Boolean> isFlagged = Optional.of(true);
-        Optional<Boolean> isDraft = Optional.of(true);
-        Optional<Boolean> isForwarded = Optional.empty();
-        Keywords keywords = Keywords.factory()
-            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded));
-
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED);
-    }
-
-    @Test
-    public void fromMapOrOldKeywordShouldReturnEmptyWhenBothAreEmpty() throws Exception {
-        assertThat(Keywords.factory()
-            .fromMapOrOldKeyword(Optional.empty(), Optional.empty()))
-            .isEmpty();
-    }
-
-    @Test
-    public void fromMapOrOldKeywordShouldReturnKeywordsWhenMap() throws Exception {
-        assertThat(Keywords.factory()
-            .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), Optional.empty()))
-            .isPresent();
-    }
-
-    @Test
-    public void fromMapOrOldKeywordShouldReturnKeywordsWhenOldKeyword() throws Exception {
-        Optional<Boolean> isAnswered = Optional.of(true);
-        Optional<Boolean> isUnread = Optional.of(true);
-        Optional<Boolean> isFlagged = Optional.of(true);
-        Optional<Boolean> isDraft = Optional.of(true);
-        Optional<Boolean> isForwarded = Optional.of(true);
-
-        OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
-
-        assertThat(Keywords.factory()
-            .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword)))
-            .isPresent();
-    }
-
-    @Test
-    public void fromMapOrOldKeywordShouldThrownWhenBothMapAndOldKeyword() throws Exception {
-        expectedException.expect(IllegalArgumentException.class);
-        Optional<Boolean> isAnswered = Optional.of(true);
-        Optional<Boolean> isUnread = Optional.of(true);
-        Optional<Boolean> isFlagged = Optional.of(true);
-        Optional<Boolean> isDraft = Optional.of(true);
-        Optional<Boolean> isForwarded = Optional.of(true);
-
-        OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
-
-        Keywords.factory()
-            .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), Optional.of(oldKeyword));
-    }
-
-    @Test
-    public void fromMapOrOldKeywordShouldReturnKeywordsFromMap() throws Exception {
-        ImmutableMap<String, Boolean> mapKeywords = ImmutableMap.of(ANY_KEYWORD, Keyword.FLAG_VALUE);
-
-        assertThat(Keywords.factory()
-            .fromMapOrOldKeyword(Optional.of(mapKeywords), Optional.empty())
-            .get()
-            .getKeywords())
-            .containsOnly(new Keyword(ANY_KEYWORD));
-    }
-
-    @Test
-    public void fromMapOrOldKeywordShouldReturnKeywordsFromOldKeyword() throws Exception {
-        Optional<Boolean> isAnswered = Optional.of(true);
-        Optional<Boolean> isUnread = Optional.of(true);
-        Optional<Boolean> isFlagged = Optional.of(true);
-        Optional<Boolean> isDraft = Optional.of(true);
-        Optional<Boolean> isForwarded = Optional.of(true);
-
-        OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
-
-        Keywords keywords = Keywords.factory()
-            .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword))
-            .get();
-        assertThat(keywords.getKeywords())
-            .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED, Keyword.FORWARDED);
-    }
-
-    @Test
     public void asFlagsShouldBuildFlagsFromKeywords() throws Exception {
         assertThat(Keywords.factory()
-                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty())
                 .asFlags())
             .isEqualTo(new Flags(Flags.Flag.ANSWERED));
     }
@@ -258,7 +102,7 @@ public class KeywordsTest {
             .build();
 
         assertThat(Keywords.factory()
-                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty())
                 .asFlagsWithRecentAndDeletedFrom(originFlags))
             .isEqualTo(expectedFlags);
     }
@@ -274,7 +118,7 @@ public class KeywordsTest {
             .build();
 
         assertThat(Keywords.factory()
-                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty())
                 .asFlagsWithRecentAndDeletedFrom(originFlags))
             .isEqualTo(expectedFlags);
     }
@@ -282,7 +126,7 @@ public class KeywordsTest {
     @Test
     public void asMapShouldReturnEmptyWhenEmptyMapOfStringAndBoolean() throws Exception {
         assertThat(Keywords.factory()
-                .fromSet(ImmutableSet.of())
+                .fromSet(ImmutableSet.of(), Optional.empty())
                 .asMap())
             .isEmpty();;
     }
@@ -291,7 +135,7 @@ public class KeywordsTest {
     public void asMapShouldReturnMapOfStringAndBoolean() throws Exception {
         Map<String, Boolean> expectedMap = ImmutableMap.of("$Answered", Keyword.FLAG_VALUE);
         assertThat(Keywords.factory()
-                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty())
                 .asMap())
             .isEqualTo(expectedMap);
     }
@@ -302,14 +146,14 @@ public class KeywordsTest {
 
         Keywords.factory()
             .throwOnImapNonExposedKeywords()
-            .fromSet(ImmutableSet.of(Keyword.DRAFT, Keyword.DELETED));
+            .fromSet(ImmutableSet.of(Keyword.DRAFT, Keyword.DELETED), Optional.empty());
     }
 
     @Test
     public void throwWhenUnsupportedKeywordShouldNotThrowWhenHaveDraft() throws Exception {
         Keywords keywords = Keywords.factory()
             .throwOnImapNonExposedKeywords()
-            .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DRAFT));
+            .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DRAFT), Optional.empty());
 
         assertThat(keywords.getKeywords())
             .containsOnly(Keyword.ANSWERED, Keyword.DRAFT);
@@ -319,7 +163,7 @@ public class KeywordsTest {
     public void filterUnsupportedShouldFilter() throws Exception {
         Keywords keywords = Keywords.factory()
             .filterImapNonExposedKeywords()
-            .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DELETED, Keyword.RECENT, Keyword.DRAFT));
+            .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DELETED, Keyword.RECENT, Keyword.DRAFT), Optional.empty());
 
         assertThat(keywords.getKeywords())
             .containsOnly(Keyword.ANSWERED, Keyword.DRAFT);
@@ -328,7 +172,7 @@ public class KeywordsTest {
     @Test
     public void containsShouldReturnTrueWhenKeywordsContainKeyword() {
         Keywords keywords = Keywords.factory()
-            .fromSet(ImmutableSet.of(Keyword.SEEN));
+            .fromSet(ImmutableSet.of(Keyword.SEEN), Optional.empty());
 
         assertThat(keywords.contains(Keyword.SEEN)).isTrue();
     }
@@ -336,7 +180,7 @@ public class KeywordsTest {
     @Test
     public void containsShouldReturnFalseWhenKeywordsDoNotContainKeyword() {
         Keywords keywords = Keywords.factory()
-            .fromSet(ImmutableSet.of());
+            .fromSet(ImmutableSet.of(), Optional.empty());
 
         assertThat(keywords.contains(Keyword.SEEN)).isFalse();
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
index 024271a..46db6b8 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
@@ -7,7 +7,7 @@
  * "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                 *
+ *   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  *
@@ -19,13 +19,68 @@
 
 package org.apache.james.jmap.model;
 
-import nl.jqno.equalsverifier.EqualsVerifier;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+
+import javax.mail.Flags;
+import javax.mail.Flags.Flag;
+
 import org.junit.Test;
 
+import nl.jqno.equalsverifier.EqualsVerifier;
+
 public class OldKeywordTest {
     @Test
     public void shouldRespectBeanContract() {
         EqualsVerifier.forClass(OldKeyword.class).verify();
     }
 
+    @Test
+    public void applyStateShouldSetFlaggedOnlyWhenIsFlagged() {
+        Optional<Boolean> isUnread = Optional.empty();
+        Optional<Boolean> isFlagged = Optional.of(true);
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isDraft = Optional.empty();
+        Optional<Boolean> isForwarded = Optional.empty();
+        OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
+        
+        assertThat(testee.applyToState(new Flags())).isEqualTo(new Flags(Flag.FLAGGED));
+    }
+
+    @Test
+    public void applyStateShouldRemoveFlaggedWhenEmptyIsFlaggedOnFlaggedMessage() {
+        Optional<Boolean> isUnread = Optional.empty();
+        Optional<Boolean> isFlagged = Optional.of(false);
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isDraft = Optional.empty();
+        Optional<Boolean> isForwarded = Optional.empty();
+        OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
+        
+        assertThat(testee.applyToState(new Flags(Flag.FLAGGED))).isEqualTo(new Flags());
+    }
+
+    @Test
+    public void applyStateShouldReturnUnreadFlagWhenUnreadSetOnSeenMessage() {
+        Optional<Boolean> isUnread = Optional.of(true);
+        Optional<Boolean> isFlagged = Optional.empty();
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isDraft = Optional.empty();
+        Optional<Boolean> isForwarded = Optional.empty();
+        OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
+        
+        assertThat(testee.applyToState(new Flags(Flag.SEEN))).isEqualTo(new Flags());
+    }
+
+    @Test
+    public void applyStateShouldReturnSeenWhenPatchSetsSeenOnSeenMessage() {
+        Optional<Boolean> isUnread = Optional.of(false);
+        Optional<Boolean> isFlagged = Optional.empty();
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isDraft = Optional.empty();
+        Optional<Boolean> isForwarded = Optional.empty();
+        OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded);
+        
+        assertThat(testee.applyToState(new Flags(Flag.SEEN))).isEqualTo(new Flags(Flag.SEEN));
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
index c556ce6..5b8c4a1 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
@@ -21,9 +21,6 @@ package org.apache.james.jmap.model;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Arrays;
-import java.util.List;
-
 import javax.mail.Flags;
 
 import org.apache.james.mailbox.FlagsBuilder;
@@ -50,38 +47,6 @@ public class UpdateMessagePatchTest {
     }
 
     @Test
-    public void applyStateShouldSetFlaggedOnlyWhenIsFlagged() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder().isFlagged(true).build();
-        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(new Flags()).getSystemFlags());
-        assertThat(updatedFlags).containsExactly(Flags.Flag.FLAGGED);
-    }
-
-
-    @Test
-    public void applyStateShouldRemoveFlaggedWhenEmptyIsFlaggedOnFlaggedMessage() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder().isAnswered(true).build();
-        Flags isFlagged = new Flags(Flags.Flag.FLAGGED);
-        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isFlagged).getSystemFlags());
-        assertThat(updatedFlags).doesNotContain(Flags.Flag.FLAGGED);
-    }
-
-    @Test
-    public void applyStateShouldReturnUnreadFlagWhenUnreadSetOnSeenMessage() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(true).build();
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isSeen).getSystemFlags());
-        assertThat(updatedFlags).doesNotContain(Flags.Flag.SEEN);
-    }
-
-    @Test
-    public void applyStateShouldReturnSeenWhenPatchSetsSeenOnSeenMessage() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(false).build();
-        Flags isSeen = new Flags(Flags.Flag.SEEN);
-        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isSeen).getSystemFlags());
-        assertThat(updatedFlags).containsExactly(Flags.Flag.SEEN);
-    }
-
-    @Test
     public void applyStateShouldReturnNewFlagsWhenKeywords() {
         ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
                 "$Answered", true,


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[2/5] james-project git commit: Increase surefire fork timeout

Posted by ad...@apache.org.
Increase surefire fork timeout


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/022b216c
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/022b216c
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/022b216c

Branch: refs/heads/master
Commit: 022b216cd23b7ad1b100ac067428b3d05fb8206b
Parents: 2430a2c
Author: Antoine Duprat <ad...@linagora.com>
Authored: Fri Nov 24 10:12:06 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Tue Nov 28 14:49:05 2017 +0100

----------------------------------------------------------------------
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/022b216c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 6fbd7de..ddaf8c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2530,8 +2530,8 @@
                     <configuration>
                         <argLine>-Xms256m -Xmx512m</argLine>
                         <reuseForks>false</reuseForks>
-                        <!-- Fail tests longer than 1 hour, prevent form random locking tests -->
-                        <forkedProcessTimeoutInSeconds>3600</forkedProcessTimeoutInSeconds>
+                        <!-- Fail tests longer than 2 hours, prevent form random locking tests -->
+                        <forkedProcessTimeoutInSeconds>7200</forkedProcessTimeoutInSeconds>
                     </configuration>
                 </plugin>
                 <plugin>


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org