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 2018/08/30 13:15:43 UTC

[01/26] james-project git commit: JAMES-2529 Mailet Filter implementation

Repository: james-project
Updated Branches:
  refs/heads/master 1968a528f -> 072feaca6


JAMES-2529 Mailet Filter implementation


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

Branch: refs/heads/master
Commit: 897cb08c54341fd8c3021f0c366197494f39c718
Parents: 1968a52
Author: duc <dt...@linagora.com>
Authored: Thu Aug 23 23:09:09 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:00 2018 +0200

----------------------------------------------------------------------
 javax-mail-extension/pom.xml                    |   4 +
 .../org/apache/james/javax/AddressHelper.java   |  37 +
 pom.xml                                         |   5 +
 .../transport/matchers/dlp/DlpDomainRules.java  |  17 +-
 server/protocols/jmap/pom.xml                   |  30 +-
 .../james/jmap/mailet/filter/ActionApplier.java | 101 +++
 .../james/jmap/mailet/filter/JMAPFiltering.java |  85 +++
 .../james/jmap/mailet/filter/MailMatcher.java   | 225 ++++++
 .../james/jmap/mailet/filter/RuleMatcher.java   |  44 ++
 .../mailet/filter/JMAPFilteringExtension.java   | 151 ++++
 .../mailet/filter/JMAPFilteringFixture.java     |  59 ++
 .../jmap/mailet/filter/JMAPFilteringTest.java   | 731 +++++++++++++++++++
 12 files changed, 1471 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/javax-mail-extension/pom.xml
----------------------------------------------------------------------
diff --git a/javax-mail-extension/pom.xml b/javax-mail-extension/pom.xml
index 2e2fa74..d23866d 100644
--- a/javax-mail-extension/pom.xml
+++ b/javax-mail-extension/pom.xml
@@ -40,5 +40,9 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-mime4j-core</artifactId>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/javax-mail-extension/src/main/java/org/apache/james/javax/AddressHelper.java
----------------------------------------------------------------------
diff --git a/javax-mail-extension/src/main/java/org/apache/james/javax/AddressHelper.java b/javax-mail-extension/src/main/java/org/apache/james/javax/AddressHelper.java
new file mode 100644
index 0000000..fb0084c
--- /dev/null
+++ b/javax-mail-extension/src/main/java/org/apache/james/javax/AddressHelper.java
@@ -0,0 +1,37 @@
+/****************************************************************
+ * 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.javax;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import javax.mail.Address;
+
+import org.apache.james.mime4j.util.MimeUtil;
+
+public class AddressHelper {
+    public static Stream<String> asStringStream(Address[] addresses) {
+        return Arrays.stream(addresses).map(AddressHelper::asString);
+    }
+
+    private static String asString(Address address) {
+        return MimeUtil.unscrambleHeaderValue(address.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4368273..9c9028a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2401,6 +2401,11 @@
             </dependency>
             <dependency>
                 <groupId>org.junit.platform</groupId>
+                <artifactId>junit-platform-engine</artifactId>
+                <version>${junit.plateform.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.junit.platform</groupId>
                 <artifactId>junit-platform-launcher</artifactId>
                 <version>${junit.plateform.version}</version>
             </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
index f44a60e..d3f0eef 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
@@ -19,14 +19,14 @@
 
 package org.apache.james.transport.matchers.dlp;
 
+import static org.apache.james.javax.AddressHelper.asStringStream;
+
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
-import javax.mail.Address;
 import javax.mail.BodyPart;
 import javax.mail.Message;
 import javax.mail.MessagingException;
@@ -36,8 +36,8 @@ import javax.mail.internet.MimeMessage;
 import org.apache.james.core.MailAddress;
 import org.apache.james.dlp.api.DLPConfigurationItem;
 import org.apache.james.dlp.api.DLPConfigurationItem.Targets;
+import org.apache.james.javax.AddressHelper;
 import org.apache.james.javax.MultipartUtil;
-import org.apache.james.mime4j.util.MimeUtil;
 import org.apache.james.util.OptionalUtils;
 import org.apache.mailet.Mail;
 
@@ -71,15 +71,6 @@ public class DlpDomainRules {
 
         interface MatcherFunction extends ThrowingPredicate<Mail> { }
 
-
-        private static Stream<String> asStringStream(Address[] addresses) {
-            return Arrays.stream(addresses).map(Rule::asString);
-        }
-
-        private static String asString(Address address) {
-            return MimeUtil.unscrambleHeaderValue(address.toString());
-        }
-
         private static class ContentMatcher implements Rule.MatcherFunction {
 
             private final Pattern pattern;
@@ -153,7 +144,7 @@ public class DlpDomainRules {
             private Stream<String> listHeaderRecipients(Mail mail) throws MessagingException {
                 return Optional.ofNullable(mail.getMessage())
                     .flatMap(Throwing.function(m -> Optional.ofNullable(m.getAllRecipients())))
-                    .map(Rule::asStringStream)
+                    .map(AddressHelper::asStringStream)
                     .orElse(Stream.of());
             }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/pom.xml b/server/protocols/jmap/pom.xml
index 8818166..93495fb 100644
--- a/server/protocols/jmap/pom.xml
+++ b/server/protocols/jmap/pom.xml
@@ -78,6 +78,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-mdn</artifactId>
         </dependency>
         <dependency>
@@ -212,11 +217,6 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>net.javacrumbs.json-unit</groupId>
             <artifactId>json-unit-assertj</artifactId>
             <scope>test</scope>
@@ -264,6 +264,26 @@
             <version>1.2</version>
         </dependency>
         <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jgrapht</groupId>
             <artifactId>jgrapht-core</artifactId>
             <version>1.2.0</version>

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
new file mode 100644
index 0000000..fdb55ac
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
@@ -0,0 +1,101 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.User;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.mailet.Mail;
+
+import com.github.fge.lambdas.Throwing;
+import com.google.common.annotations.VisibleForTesting;
+
+public class ActionApplier {
+    static final String DELIVERY_PATH_PREFIX = "DeliveryPath_";
+
+    @VisibleForTesting
+    static class Factory {
+        private final MailboxManager mailboxManager;
+        private final MailboxId.Factory mailboxIdFactory;
+
+        @Inject
+        Factory(MailboxManager mailboxManager, MailboxId.Factory mailboxIdFactory) {
+            this.mailboxManager = mailboxManager;
+            this.mailboxIdFactory = mailboxIdFactory;
+        }
+
+        public RequireUser forMail(Mail mail) {
+            return new RequireUser(mail);
+        }
+
+        public class RequireUser {
+            private final Mail mail;
+
+            RequireUser(Mail mail) {
+                this.mail = mail;
+            }
+
+            public ActionApplier forUser(User user) {
+                return new ActionApplier(mailboxManager, mailboxIdFactory, mail, user);
+            }
+        }
+    }
+
+    private final MailboxManager mailboxManager;
+    private final MailboxId.Factory mailboxIdFactory;
+    private final Mail mail;
+    private final User user;
+
+    @VisibleForTesting
+    public static Factory factory(MailboxManager mailboxManager, MailboxId.Factory mailboxIdFactory) {
+        return new Factory(mailboxManager, mailboxIdFactory);
+    }
+
+    private ActionApplier(MailboxManager mailboxManager, MailboxId.Factory mailboxIdFactory, Mail mail, User user) {
+        this.mailboxManager = mailboxManager;
+        this.mailboxIdFactory = mailboxIdFactory;
+        this.mail = mail;
+        this.user = user;
+    }
+
+    public void apply(Rule.Action action) {
+        action.getAppendInMailboxes()
+                .getMailboxIds()
+                .stream()
+                .findFirst()
+                .map(mailboxIdFactory::fromString)
+                .ifPresent(Throwing.consumer(this::addStorageDirective));
+    }
+
+    private void addStorageDirective(MailboxId mailboxId) throws MailboxException {
+        MailboxSession mailboxSession = mailboxManager.createSystemSession(user.asString());
+        MessageManager messageManager = mailboxManager.getMailbox(mailboxId, mailboxSession);
+
+        String mailboxName = messageManager.getMailboxPath().getName();
+        String attributeNameForUser = DELIVERY_PATH_PREFIX + user.asString();
+        mail.setAttribute(attributeNameForUser, mailboxName);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
new file mode 100644
index 0000000..388fabe
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
@@ -0,0 +1,85 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.core.User;
+import org.apache.james.jmap.api.filtering.FilteringManagement;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMailet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JMAPFiltering extends GenericMailet {
+
+    private final Logger logger = LoggerFactory.getLogger(JMAPFiltering.class);
+
+    private final FilteringManagement filteringManagement;
+    private final UsersRepository usersRepository;
+    private final ActionApplier.Factory actionApplierFactory;
+
+    @Inject
+    public JMAPFiltering(FilteringManagement filteringManagement,
+                         UsersRepository usersRepository, ActionApplier.Factory actionApplierFactory) {
+
+        this.filteringManagement = filteringManagement;
+        this.usersRepository = usersRepository;
+        this.actionApplierFactory = actionApplierFactory;
+    }
+
+    @Override
+    public void service(Mail mail) {
+        mail.getRecipients()
+            .forEach(recipient -> filteringForRecipient(mail, recipient));
+    }
+
+    private void filteringForRecipient(Mail mail, MailAddress recipient) {
+        Optional<User> maybeUser = retrieveUser(recipient);
+        maybeUser
+            .ifPresent(user -> findFirstApplicableRule(user, mail));
+    }
+
+    private void findFirstApplicableRule(User user, Mail mail) {
+        List<Rule> filteringRules = filteringManagement.listRulesForUser(user);
+        RuleMatcher ruleMatcher = new RuleMatcher(filteringRules);
+        Optional<Rule> maybeMatchingRule = ruleMatcher.findApplicableRule(mail);
+
+        maybeMatchingRule.ifPresent(rule -> actionApplierFactory.forMail(mail)
+                .forUser(user)
+                .apply(rule.getAction()));
+    }
+
+    private Optional<User> retrieveUser(MailAddress recipient) {
+        try {
+            return Optional.ofNullable(User.fromUsername(usersRepository.getUser(recipient)));
+        } catch (UsersRepositoryException e) {
+            logger.error("cannot retrieve user " + recipient.asString(), e);
+            return Optional.empty();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
new file mode 100644
index 0000000..c9ea72b
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -0,0 +1,225 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import static org.apache.james.jmap.api.filtering.Rule.Condition;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.james.javax.AddressHelper;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
+import org.apache.mailet.Mail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.functions.ThrowingFunction;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+public interface MailMatcher {
+
+    interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {}
+
+    class HeaderMatcher implements MailMatcher {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(HeaderMatcher.class);
+
+        private final ContentMatcher contentMatcher;
+        private final String ruleValue;
+        private final HeaderExtractor headerExtractor;
+
+        private HeaderMatcher(ContentMatcher contentMatcher, String ruleValue,
+                      HeaderExtractor headerExtractor) {
+            Preconditions.checkNotNull(contentMatcher);
+            Preconditions.checkNotNull(headerExtractor);
+
+            this.contentMatcher = contentMatcher;
+            this.ruleValue = ruleValue;
+            this.headerExtractor = headerExtractor;
+        }
+
+        @Override
+        public boolean match(Mail mail) {
+            try {
+                final Stream<String> headerLines = headerExtractor.apply(mail);
+                return contentMatcher.match(headerLines, ruleValue);
+            } catch (Exception e) {
+                LOGGER.error("error while extracting mail header", e);
+                return false;
+            }
+        }
+    }
+
+    interface ContentMatcher {
+
+        class AddressHeader {
+            private static final Logger LOGGER = LoggerFactory.getLogger(AddressHeader.class);
+
+            private final Optional<String> personal;
+            private final Optional<String> address;
+            private final String fullAddress;
+
+            private AddressHeader(String fullAddress) {
+                this.fullAddress = fullAddress;
+                Optional<InternetAddress> internetAddress = parseFullAddress();
+                this.personal = internetAddress.map(InternetAddress::getPersonal);
+                this.address = internetAddress.map(InternetAddress::getAddress);
+            }
+
+            private Optional<InternetAddress> parseFullAddress() {
+                try {
+                    return Optional.of(new InternetAddress(fullAddress));
+                } catch (AddressException e) {
+                    LOGGER.error("error while parsing full address {}", fullAddress, e);
+                    return Optional.empty();
+                }
+            }
+
+            public Optional<String> getPersonal() {
+                return personal;
+            }
+
+            public Optional<String> getAddress() {
+                return address;
+            }
+
+            public String getFullAddress() {
+                return fullAddress;
+            }
+        }
+
+        ContentMatcher STRING_CONTAINS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.contains(content, valueToMatch));
+        ContentMatcher STRING_NOT_CONTAINS_MATCHER = negate(STRING_CONTAINS_MATCHER);
+        ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.equals(content, valueToMatch));
+        ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = negate(STRING_EXACTLY_EQUALS_MATCHER);
+
+        Map<Rule.Condition.Comparator, ContentMatcher> CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
+                .put(Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
+                .put(Condition.Comparator.NOT_CONTAINS, STRING_NOT_CONTAINS_MATCHER)
+                .put(Condition.Comparator.EXACTLY_EQUALS, STRING_EXACTLY_EQUALS_MATCHER)
+                .put(Condition.Comparator.NOT_EXACTLY_EQUALS, STRING_NOT_EXACTLY_EQUALS_MATCHER)
+                .build();
+
+        ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> contents
+                .map(ContentMatcher::asAddressHeader)
+                .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.getFullAddress(), valueToMatch));
+
+        ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = negate(ADDRESS_CONTAINS_MATCHER);
+        ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents
+                .map(ContentMatcher::asAddressHeader)
+                .anyMatch(addressHeader ->
+                        StringUtils.equalsIgnoreCase(addressHeader.getFullAddress(), valueToMatch)
+                        || StringUtils.equalsIgnoreCase(addressHeader.getAddress().orElse(null), valueToMatch)
+                        || StringUtils.equalsIgnoreCase(addressHeader.getPersonal().orElse(null), valueToMatch));
+
+        ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
+
+        Map<Rule.Condition.Comparator, ContentMatcher> HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
+                .put(Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
+                .put(Condition.Comparator.NOT_CONTAINS, ADDRESS_NOT_CONTAINS_MATCHER)
+                .put(Condition.Comparator.EXACTLY_EQUALS, ADDRESS_EXACTLY_EQUALS_MATCHER)
+                .put(Condition.Comparator.NOT_EXACTLY_EQUALS, ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
+                .build();
+
+        Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>> CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>>builder()
+                .put(Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
+                .put(Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
+                .put(Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
+                .put(Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
+                .put(Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
+                .build();
+
+        static ContentMatcher negate(ContentMatcher contentMatcher) {
+            return (Stream<String> contents, String valueToMatch) ->
+                    !contentMatcher.match(contents, valueToMatch);
+        }
+
+        static Optional<ContentMatcher> asContentMatcher(Condition.Field field, Condition.Comparator comparator) {
+            return Optional
+                .ofNullable(CONTENT_MATCHER_REGISTRY.get(field))
+                .map(matcherRegistry -> matcherRegistry.get(comparator));
+        }
+
+        static AddressHeader asAddressHeader(String addressAsString) {
+            return new AddressHeader(addressAsString);
+        }
+
+        boolean match(Stream<String> contents, String valueToMatch);
+    }
+
+    HeaderExtractor SUBJECT_EXTRACTOR = mail ->
+            Optional.ofNullable(mail.getMessage().getSubject())
+                    .map(Stream::of)
+                    .orElse(Stream.empty());
+    HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
+            ArrayUtils.addAll(
+                mail.getMessage().getRecipients(Message.RecipientType.TO),
+                mail.getMessage().getRecipients(Message.RecipientType.CC)));
+
+    HeaderExtractor FROM_EXTRACTOR = mail -> addressExtractor(mail.getMessage().getFrom());
+    HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
+    HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
+
+    Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Field, HeaderExtractor>builder()
+            .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
+            .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
+            .put(Field.FROM, FROM_EXTRACTOR)
+            .put(Field.CC, CC_EXTRACTOR)
+            .put(Field.TO, TO_EXTRACTOR)
+            .build();
+
+    static MailMatcher from(Rule rule) {
+        Condition ruleCondition = rule.getCondition();
+        Optional<ContentMatcher> maybeContentMatcher = ContentMatcher.asContentMatcher(ruleCondition.getField(), ruleCondition.getComparator());
+        Optional<HeaderExtractor> maybeHeaderExtractor = getHeaderExtractor(ruleCondition.getField());
+
+        return new HeaderMatcher(
+            maybeContentMatcher.orElseThrow(() -> new RuntimeException("No content matcher associated with field " + ruleCondition.getField())),
+            rule.getCondition().getValue(),
+            maybeHeaderExtractor.orElseThrow(() -> new RuntimeException("No content matcher associated with comparator " + ruleCondition.getComparator())));
+    }
+
+    static HeaderExtractor recipientExtractor(Message.RecipientType type) {
+        return mail -> addressExtractor(mail.getMessage().getRecipients(type));
+    }
+
+    static Stream<String> addressExtractor(Address[] addresses) {
+        return Optional.ofNullable(addresses)
+                .map(AddressHelper::asStringStream)
+                .orElse(Stream.empty());
+    }
+
+    static Optional<HeaderExtractor> getHeaderExtractor(Field field) {
+        return Optional
+            .ofNullable(HEADER_EXTRACTOR_REGISTRY.get(field));
+    }
+
+    boolean match(Mail mail);
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
new file mode 100644
index 0000000..abc1da1
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
@@ -0,0 +1,44 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.mailet.filter;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.mailet.Mail;
+
+import com.google.common.base.Preconditions;
+
+class RuleMatcher {
+    private final List<Rule> filteringRules;
+
+    RuleMatcher(List<Rule> filteringRules) {
+        Preconditions.checkNotNull(filteringRules);
+
+        this.filteringRules = filteringRules;
+    }
+
+    Optional<Rule> findApplicableRule(Mail mail) {
+        return filteringRules.stream()
+            .filter(rule -> MailMatcher.from(rule).match(mail))
+            .findFirst();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
new file mode 100644
index 0000000..05dc317
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
@@ -0,0 +1,151 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_MAILBOX_1;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_USERNAME;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_ADDRESS;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.core.User;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStore;
+import org.apache.james.jmap.api.filtering.FilteringManagement;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
+import org.apache.james.mailbox.inmemory.InMemoryId;
+import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+import com.google.common.collect.ImmutableList;
+
+public class JMAPFilteringExtension implements BeforeEachCallback, ParameterResolver {
+
+    class JMAPFilteringTestSystem {
+
+        private final JMAPFiltering jmapFiltering;
+        private final FilteringManagement filteringManagement;
+        private final InMemoryMailboxManager mailboxManager;
+        private final MailboxId recipient1Mailbox;
+
+        JMAPFilteringTestSystem(JMAPFiltering jmapFiltering, FilteringManagement filteringManagement,
+                                InMemoryMailboxManager mailboxManager) {
+            this.jmapFiltering = jmapFiltering;
+            this.filteringManagement = filteringManagement;
+            this.mailboxManager = mailboxManager;
+            try {
+                this.recipient1Mailbox = createMailbox(mailboxManager, RECIPIENT_1_USERNAME, RECIPIENT_1_MAILBOX_1);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public JMAPFiltering getJmapFiltering() {
+            return jmapFiltering;
+        }
+
+        public FilteringManagement getFilteringManagement() {
+            return filteringManagement;
+        }
+
+        public InMemoryMailboxManager getMailboxManager() {
+            return mailboxManager;
+        }
+
+        public MailboxId getRecipient1MailboxId() {
+            return recipient1Mailbox;
+        }
+
+        public MailboxId createMailbox(InMemoryMailboxManager mailboxManager, String username, String mailboxName) throws Exception {
+            MailboxSession mailboxSession = mailboxManager.createSystemSession(username);
+            return mailboxManager
+                .createMailbox(MailboxPath.forUser(username, mailboxName), mailboxSession)
+                .orElseThrow(() -> new RuntimeException("Missing mailboxId when creating mailbox"));
+        }
+
+        public void defineRulesForRecipient1(Rule.Condition... conditions) {
+            defineRulesForRecipient1(Arrays.asList(conditions));
+        }
+
+        public void defineRulesForRecipient1(List<Rule.Condition> conditions) {
+            AtomicInteger counter = new AtomicInteger();
+            ImmutableList<Rule> rules = conditions
+                .stream()
+                .map(condition -> Rule.builder()
+                    .id(Rule.Id.of(String.valueOf(counter.incrementAndGet())))
+                    .name(String.valueOf(counter.incrementAndGet()))
+                    .condition(condition)
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize())))
+                    .build())
+                .collect(ImmutableList.toImmutableList());
+
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME), rules);
+        }
+
+        public FakeMail asMail(MimeMessageBuilder mimeMessageBuilder) throws MessagingException {
+            return FakeMail.builder()
+                .sender(USER_1_ADDRESS)
+                .recipients(RECIPIENT_1)
+                .mimeMessage(mimeMessageBuilder)
+                .build();
+        }
+    }
+
+    private JMAPFilteringTestSystem testSystem;
+
+    @Override
+    public void beforeEach(ExtensionContext extensionContext) throws Exception {
+        FilteringManagement filteringManagement = new EventSourcingFilteringManagement(new InMemoryEventStore());
+        MemoryUsersRepository usersRepository = MemoryUsersRepository.withoutVirtualHosting();
+        InMemoryMailboxManager mailboxManager = new InMemoryIntegrationResources().createMailboxManager(new SimpleGroupMembershipResolver());
+        ActionApplier.Factory actionApplierFactory = ActionApplier.factory(mailboxManager, new InMemoryId.Factory());
+
+        JMAPFiltering jmapFiltering = new JMAPFiltering(filteringManagement, usersRepository, actionApplierFactory);
+
+        testSystem = new JMAPFilteringTestSystem(jmapFiltering, filteringManagement, mailboxManager);
+    }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == JMAPFilteringTestSystem.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return testSystem;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringFixture.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringFixture.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringFixture.java
new file mode 100644
index 0000000..1b4792b
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringFixture.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+public interface JMAPFilteringFixture {
+    String GA_BOU_ZO_MEU_FULL_ADDRESS = "GA BOU ZO MEU <GA...@james.org>";
+    String BOU = "BOU";
+
+    String USER_1_FULL_ADDRESS = "user1 <us...@james.org>";
+    String USER_1_ADDRESS = "user1@james.org";
+    String USER_1_USERNAME = "user1";
+
+    String USER_2_FULL_ADDRESS = "user2 <us...@james.org>";
+    String USER_2_ADDRESS = "user2@james.org";
+    String USER_2_USERNAME = "user2";
+
+    String USER_1_AND_UNFOLDED_USER_FULL_ADDRESS = "user2 <se...@james.org>, \r\nunfolded\r\n_user\r\n <un...@james.org>";
+
+    String USER_3_FULL_ADDRESS = "user3 <us...@james.org>";
+    String USER_3_ADDRESS = "user3@james.org";
+    String USER_3_USERNAME = "user3";
+
+    String USER_4_FULL_ADDRESS = "user4 <us...@james.org>";
+
+    String SCRAMBLED_SUBJECT = "this is the subject =?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= of the mail";
+    String UNSCRAMBLED_SUBJECT = "this is the subject Frédéric MARTIN of the mail";
+    String SHOULD_NOT_MATCH = "should not match";
+
+    String RECIPIENT_1 = "recipient1@james.org";
+    String RECIPIENT_1_USERNAME = "recipient1";
+    String RECIPIENT_1_MAILBOX_1 = "recipient1_maibox1";
+
+    String FRED_MARTIN_FULLNAME = "Frédéric MARTIN";
+    String FRED_MARTIN_FULL_SCRAMBLED_ADDRESS = "=?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= <fr...@linagora.com>";
+
+    String UNFOLDED_USERNAME = "unfolded_user";
+
+    String EMPTY = "";
+
+    String TO_HEADER = "to";
+    String CC_HEADER = "cc";
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/897cb08c/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
new file mode 100644
index 0000000..0d697f8
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
@@ -0,0 +1,731 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import static org.apache.james.core.builder.MimeMessageBuilder.mimeMessageBuilder;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.CONTAINS;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.EXACTLY_EQUALS;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.NOT_CONTAINS;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.NOT_EXACTLY_EQUALS;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.CC;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.FROM;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.RECIPIENT;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.SUBJECT;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.TO;
+import static org.apache.james.jmap.mailet.filter.ActionApplier.DELIVERY_PATH_PREFIX;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.BOU;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.CC_HEADER;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.EMPTY;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULLNAME;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULL_SCRAMBLED_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.GA_BOU_ZO_MEU_FULL_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_MAILBOX_1;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_USERNAME;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SCRAMBLED_SUBJECT;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SHOULD_NOT_MATCH;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.TO_HEADER;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.UNFOLDED_USERNAME;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.UNSCRAMBLED_SUBJECT;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_AND_UNFOLDED_USER_FULL_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_FULL_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_USERNAME;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_FULL_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_FULL_ADDRESS;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_USERNAME;
+import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_4_FULL_ADDRESS;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Locale;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.james.core.User;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.mailet.filter.JMAPFilteringExtension.JMAPFilteringTestSystem;
+import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.util.StreamUtils;
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import com.github.fge.lambdas.Throwing;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+@ExtendWith(JMAPFilteringExtension.class)
+class JMAPFilteringTest {
+
+    static class FilteringArgumentBuilder {
+        private Optional<String> description;
+        private Optional<Rule.Condition.Field> field;
+        private MimeMessageBuilder mimeMessageBuilder;
+        private Optional<String> valueToMatch;
+        
+        private FilteringArgumentBuilder() {
+            this.description = Optional.empty();
+            this.field = Optional.empty();
+            mimeMessageBuilder = MimeMessageBuilder.mimeMessageBuilder();
+            this.valueToMatch = Optional.empty();
+        }
+
+        public FilteringArgumentBuilder description(String description) {
+            this.description = Optional.ofNullable(description);
+            return this;
+        }
+
+        public FilteringArgumentBuilder field(Rule.Condition.Field field) {
+            this.field = Optional.ofNullable(field);
+            return this;
+        }
+
+        public FilteringArgumentBuilder from(String from) {
+            Optional.ofNullable(from).ifPresent(Throwing.consumer(mimeMessageBuilder::addFrom));
+            return this;
+        }
+
+        public FilteringArgumentBuilder noHeader() {
+            return this;
+        }
+
+        public FilteringArgumentBuilder toRecipient(String toRecipient) {
+            Optional.ofNullable(toRecipient).ifPresent(Throwing.consumer(mimeMessageBuilder::addToRecipient));
+            return this;
+        }
+
+        public FilteringArgumentBuilder ccRecipient(String ccRecipient) {
+            Optional.ofNullable(ccRecipient).ifPresent(Throwing.consumer(mimeMessageBuilder::addCcRecipient));
+            return this;
+        }
+
+        public FilteringArgumentBuilder bccRecipient(String bccRecipient) {
+            Optional.ofNullable(bccRecipient).ifPresent(Throwing.consumer(mimeMessageBuilder::addBccRecipient));
+            return this;
+        }
+
+        public FilteringArgumentBuilder header(String headerName, String headerValue) {
+            mimeMessageBuilder.addHeader(headerName, headerValue);
+            return this;
+        }
+
+        public FilteringArgumentBuilder headerForField(String headerValue) {
+            Preconditions.checkState(field.isPresent(), "field should be set first");
+
+            mimeMessageBuilder.addHeader(field.get().asString(), headerValue);
+            return this;
+        }
+
+        public FilteringArgumentBuilder subject(String subject) {
+            mimeMessageBuilder.setSubject(subject);
+            return this;
+        }
+
+        public FilteringArgumentBuilder valueToMatch(String valueToMatch) {
+            this.valueToMatch = Optional.ofNullable(valueToMatch);
+            return this;
+        }
+
+        public FilteringArgumentBuilder scrambledSubjectToMatch(String valueToMatch) {
+            return description("normal content")
+                .field(SUBJECT)
+                .subject(SCRAMBLED_SUBJECT)
+                .valueToMatch(valueToMatch);
+        }
+
+        public FilteringArgumentBuilder scrambledSubjectShouldNotMatchCaseSensitive() {
+            return description("normal content (case sensitive)")
+                .field(SUBJECT)
+                .subject(SCRAMBLED_SUBJECT)
+                .valueToMatch(SCRAMBLED_SUBJECT.toUpperCase(Locale.FRENCH));
+        }
+
+        public FilteringArgumentBuilder unscrambledSubjectToMatch(String valueToMatch) {
+            return description("unscrambled content")
+                .field(SUBJECT)
+                .subject(UNSCRAMBLED_SUBJECT)
+                .valueToMatch(valueToMatch);
+        }
+
+        public FilteringArgumentBuilder unscrambledSubjectShouldNotMatchCaseSensitive() {
+            return description("unscrambled content (case sensitive)")
+                    .field(SUBJECT)
+                    .subject(UNSCRAMBLED_SUBJECT)
+                    .valueToMatch(UNSCRAMBLED_SUBJECT.toUpperCase(Locale.FRENCH));
+        }
+
+        public Arguments build() {
+            Preconditions.checkState(description.isPresent());
+            Preconditions.checkState(field.isPresent());
+            Preconditions.checkState(valueToMatch.isPresent());
+            
+            return Arguments.of(description.get(), field.get(), mimeMessageBuilder, valueToMatch.get());
+        }
+
+    }
+
+    static FilteringArgumentBuilder argumentBuilder() {
+        return new FilteringArgumentBuilder();
+    }
+
+    static FilteringArgumentBuilder argumentBuilder(Rule.Condition.Field field) {
+        return new FilteringArgumentBuilder()
+            .field(field);
+    }
+
+    static Stream<Arguments> exactlyEqualsTestSuite() {
+        return StreamUtils.flatten(
+            Stream.of(FROM, TO, CC)
+                .flatMap(headerField -> Stream.of(
+                        argumentBuilder(headerField)
+                        .description("full address value")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(USER_1_USERNAME),
+                    argumentBuilder(headerField)
+                        .description("full address value (different case)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(USER_1_USERNAME.toUpperCase(Locale.ENGLISH)),
+                    argumentBuilder(headerField)
+                        .description("address only value")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(USER_1_ADDRESS),
+                    argumentBuilder(headerField)
+                        .description("address only value (different case)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(USER_1_ADDRESS.toUpperCase(Locale.ENGLISH)),
+                    argumentBuilder(headerField)
+                        .description("personal only value")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(USER_1_FULL_ADDRESS),
+                    argumentBuilder(headerField)
+                        .description("personal only value (different case)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(USER_1_FULL_ADDRESS.toUpperCase()),
+                    argumentBuilder(headerField)
+                        .description("personal header should match personal")
+                        .headerForField(USER_1_USERNAME)
+                        .valueToMatch(USER_1_USERNAME),
+                    argumentBuilder(headerField)
+                        .description("address header should match address")
+                        .headerForField(USER_1_ADDRESS)
+                        .valueToMatch(USER_1_ADDRESS),
+                    argumentBuilder(headerField)
+                        .description("multiple headers")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .headerForField(USER_2_FULL_ADDRESS)
+                        .valueToMatch(USER_1_USERNAME),
+                    argumentBuilder(headerField)
+                        .description("scrambled content")
+                        .headerForField(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .valueToMatch(FRED_MARTIN_FULLNAME),
+                    argumentBuilder(headerField)
+                        .description("folded content")
+                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch(UNFOLDED_USERNAME),
+                    argumentBuilder(headerField)
+                        .description("folded content (different case)")
+                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch(UNFOLDED_USERNAME.toUpperCase())
+                    ).map(FilteringArgumentBuilder::build)),
+            Stream.of(TO_HEADER, CC_HEADER)
+                .flatMap(headerName -> Stream.of(
+                    argumentBuilder()
+                        .description("full address " + headerName + " header")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(USER_3_FULL_ADDRESS)
+                        .build(),
+                    argumentBuilder()
+                        .description("full address " + headerName + " header (different case)")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(USER_3_FULL_ADDRESS.toUpperCase(Locale.ENGLISH))
+                        .build(),
+                    argumentBuilder()
+                        .description("address only " + headerName + " header")
+                        .field(RECIPIENT).header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(USER_3_ADDRESS)
+                        .build(),
+                    argumentBuilder()
+                        .description("personal only " + headerName + " header")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(USER_3_USERNAME)
+                        .build(),
+                    argumentBuilder()
+                        .description("scrambled content in " + headerName + " header")
+                        .field(RECIPIENT)
+                        .header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .valueToMatch(FRED_MARTIN_FULLNAME)
+                        .build(),
+                    argumentBuilder()
+                        .description("folded content in " + headerName + " header")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch(UNFOLDED_USERNAME)
+                        .build())),
+            Stream.of(
+                argumentBuilder().description("multiple to and cc headers").field(RECIPIENT)
+                    .ccRecipient(USER_1_FULL_ADDRESS)
+                    .ccRecipient(USER_2_FULL_ADDRESS)
+                    .toRecipient(USER_3_FULL_ADDRESS)
+                    .toRecipient(USER_4_FULL_ADDRESS)
+                    .valueToMatch(USER_4_FULL_ADDRESS)
+                    .build(),
+                argumentBuilder().scrambledSubjectToMatch(UNSCRAMBLED_SUBJECT).build(),
+                argumentBuilder().unscrambledSubjectToMatch(UNSCRAMBLED_SUBJECT).build()));
+    }
+
+    static Stream<Arguments> containsTestSuite() {
+        return Stream.concat(
+            exactlyEqualsTestSuite(),
+            containsArguments());
+    }
+
+    private static Stream<Arguments> containsArguments() {
+        return StreamUtils.flatten(
+            Stream.of(FROM, TO, CC)
+                .flatMap(headerField -> Stream.of(
+                    argumentBuilder(headerField)
+                        .description("full address value (partial matching)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch("ser1 <"),
+                    argumentBuilder(headerField)
+                        .description("full address value (partial matching, different case)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch("SER1 <"),
+                    argumentBuilder(headerField)
+                        .description("address only value (partial matching)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch("ser1@jam"),
+                    argumentBuilder(headerField)
+                        .description("personal only value (partial matching)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch("ser1"),
+                    argumentBuilder(headerField)
+                        .description("address header & match in the address (partial matching)")
+                        .headerForField(USER_1_ADDRESS)
+                        .valueToMatch("ser1@jam"),
+                    argumentBuilder(headerField)
+                        .description("raw value matching (partial matching)")
+                        .headerForField(GA_BOU_ZO_MEU_FULL_ADDRESS)
+                        .valueToMatch(BOU),
+                    argumentBuilder(headerField)
+                        .description("multiple headers (partial matching)")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .headerForField(USER_2_FULL_ADDRESS)
+                        .valueToMatch("ser1@jam"),
+                    argumentBuilder(headerField)
+                        .description("scrambled content (partial matching)")
+                        .headerForField(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .valueToMatch("déric MAR"),
+                    argumentBuilder(headerField)
+                        .description("folded content (partial matching)")
+                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch("ded_us"))
+                    .map(FilteringArgumentBuilder::build)),
+            Stream.of(TO_HEADER, CC_HEADER)
+                .flatMap(headerName -> Stream.of(
+                    argumentBuilder()
+                        .description("full address " + headerName + " header (partial matching)")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch("ser3 <us")
+                        .build(),
+                    argumentBuilder()
+                        .description("full address " + headerName + " header (partial matching, different case)")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch("SER3 <US")
+                        .build(),
+                    argumentBuilder()
+                        .description("address only " + headerName + " header (partial matching)")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch("ser3@jam")
+                        .build(),
+                    argumentBuilder()
+                        .description("personal only " + headerName + " header (partial matching)")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch("ser3")
+                        .build(),
+                    argumentBuilder()
+                        .description("scrambled content in " + headerName + " header (partial matching)")
+                        .field(RECIPIENT)
+                        .header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .valueToMatch("déric MAR")
+                        .build(),
+                    argumentBuilder()
+                        .description("folded content in " + headerName + " header (partial matching)")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch("folded_us")
+                        .build())),
+            Stream.of(
+                argumentBuilder().description("multiple to and cc headers (partial matching)").field(RECIPIENT)
+                    .ccRecipient(USER_1_FULL_ADDRESS)
+                    .ccRecipient(USER_2_FULL_ADDRESS)
+                    .toRecipient(USER_3_FULL_ADDRESS)
+                    .toRecipient(USER_4_FULL_ADDRESS)
+                    .valueToMatch("user4@jam").build(),
+                argumentBuilder().scrambledSubjectToMatch("is the subject").build(),
+                argumentBuilder().unscrambledSubjectToMatch("rédéric MART").build()));
+    }
+
+    static Stream<Arguments> notEqualsTestSuite() {
+        return Stream.concat(
+            notContainsTestSuite(),
+            containsArguments());
+    }
+
+    static Stream<Arguments> notContainsTestSuite() {
+        return StreamUtils.flatten(
+            Stream.of(FROM, TO, CC)
+                .flatMap(headerField -> Stream.of(
+                    argumentBuilder(headerField)
+                        .description("normal content")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(headerField)
+                        .description("multiple headers")
+                        .headerForField(USER_1_FULL_ADDRESS)
+                        .from(USER_2_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(headerField)
+                        .description("scrambled content")
+                        .headerForField(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(headerField)
+                        .description("folded content")
+                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(headerField)
+                        .description("empty content")
+                        .headerForField(EMPTY)
+                        .valueToMatch(SHOULD_NOT_MATCH))
+                    .map(FilteringArgumentBuilder::build)),
+            Stream.of(TO_HEADER, CC_HEADER)
+                .flatMap(headerName -> Stream.of(
+                    argumentBuilder()
+                        .description("normal content " + headerName + " header")
+                        .field(RECIPIENT).header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH)
+                        .build(),
+                    argumentBuilder()
+                        .description("scrambled content in " + headerName + " header")
+                        .field(RECIPIENT).header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH)
+                        .build(),
+                    argumentBuilder()
+                        .description("folded content in " + headerName + " header")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH)
+                        .build(),
+                    argumentBuilder()
+                        .description("bcc header")
+                        .field(RECIPIENT)
+                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH)
+                        .build())),
+            Stream.of(
+                argumentBuilder().description("multiple to and cc headers").field(RECIPIENT)
+                    .ccRecipient(USER_1_FULL_ADDRESS)
+                    .ccRecipient(USER_2_FULL_ADDRESS)
+                    .toRecipient(USER_3_FULL_ADDRESS)
+                    .toRecipient(USER_4_FULL_ADDRESS)
+                    .valueToMatch(SHOULD_NOT_MATCH)
+                    .build(),
+                argumentBuilder().description("matching bcc headers").field(RECIPIENT)
+                    .bccRecipient(USER_1_FULL_ADDRESS)
+                    .valueToMatch(USER_1_FULL_ADDRESS)
+                    .build(),
+                argumentBuilder().scrambledSubjectToMatch(SHOULD_NOT_MATCH).build(),
+                argumentBuilder().scrambledSubjectShouldNotMatchCaseSensitive().build(),
+                argumentBuilder().unscrambledSubjectToMatch(SHOULD_NOT_MATCH).build(),
+                argumentBuilder().unscrambledSubjectShouldNotMatchCaseSensitive().build()),
+            Stream.of(Rule.Condition.Field.values())
+                .map(field -> argumentBuilder()
+                    .description("no header")
+                    .field(field)
+                    .noHeader()
+                    .valueToMatch(USER_1_USERNAME)
+                    .build()));
+    }
+
+    @ParameterizedTest(name = "CONTAINS should match for header field {1}, with {0}")
+    @MethodSource("containsTestSuite")
+    void matchingContainsTest(String testDescription,
+                              Rule.Condition.Field fieldToMatch,
+                              MimeMessageBuilder mimeMessageBuilder,
+                              String valueToMatch,
+                              JMAPFilteringTestSystem testSystem) throws Exception {
+
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, CONTAINS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isEqualTo(RECIPIENT_1_MAILBOX_1);
+    }
+
+    @ParameterizedTest(name = "CONTAINS should not match for header field {1}, with {0}")
+    @MethodSource("notContainsTestSuite")
+    void notMatchingContainsTest(String testDescription,
+                              Rule.Condition.Field fieldToMatch,
+                              MimeMessageBuilder mimeMessageBuilder,
+                              String valueToMatch,
+                              JMAPFilteringTestSystem testSystem) throws Exception {
+
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, CONTAINS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isNull();
+    }
+
+    @ParameterizedTest(name = "NOT-CONTAINS should be matching for field {1}, with {0}")
+    @MethodSource("notContainsTestSuite")
+    void matchingNotContainsTest(String testDescription,
+                                 Rule.Condition.Field fieldToMatch,
+                                 MimeMessageBuilder mimeMessageBuilder,
+                                 String valueToMatch,
+                                 JMAPFilteringTestSystem testSystem) throws Exception {
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_CONTAINS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+            .isEqualTo(RECIPIENT_1_MAILBOX_1);
+    }
+
+
+    @ParameterizedTest(name = "NOT-CONTAINS should not be matching for field {1}, with {0}")
+    @MethodSource("containsTestSuite")
+    void notContainsNotMatchingTest(String testDescription,
+                                    Rule.Condition.Field fieldToMatch,
+                                    MimeMessageBuilder mimeMessageBuilder,
+                                    String valueToMatch,
+                                    JMAPFilteringTestSystem testSystem) throws Exception {
+
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_CONTAINS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+            .isNull();
+    }
+
+    @ParameterizedTest(name = "EXACTLY-EQUALS should match for header field {1}, with {0}")
+    @MethodSource("exactlyEqualsTestSuite")
+    void equalsMatchingTest(String testDescription,
+                            Rule.Condition.Field fieldToMatch,
+                            MimeMessageBuilder mimeMessageBuilder,
+                            String valueToMatch,
+                            JMAPFilteringTestSystem testSystem) throws Exception {
+
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, EXACTLY_EQUALS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+            .isEqualTo(RECIPIENT_1_MAILBOX_1);
+    }
+
+    @ParameterizedTest(name = "EXACTLY-EQUALS should not match for header field {1}, with {0}")
+    @MethodSource("notEqualsTestSuite")
+    void equalsNotMatchingTest(String testDescription,
+                            Rule.Condition.Field fieldToMatch,
+                            MimeMessageBuilder mimeMessageBuilder,
+                            String valueToMatch,
+                            JMAPFilteringTestSystem testSystem) throws Exception {
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, EXACTLY_EQUALS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+            .isNull();
+    }
+
+    @ParameterizedTest(name = "NOT_EXACTLY_EQUALS should match for header field {1}, with {0}")
+    @MethodSource("notEqualsTestSuite")
+    void notEqualsMatchingTest(String testDescription,
+                               Rule.Condition.Field fieldToMatch,
+                               MimeMessageBuilder mimeMessageBuilder,
+                               String valueToMatch,
+                               JMAPFilteringTestSystem testSystem) throws Exception {
+
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_EXACTLY_EQUALS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+            .isEqualTo(RECIPIENT_1_MAILBOX_1);
+    }
+
+    @ParameterizedTest(name = "NOT_EXACTLY_EQUALS should not match for header field {1}, with {0}")
+    @MethodSource("exactlyEqualsTestSuite")
+    void notMatchingNotEqualsTests(String testDescription,
+                                   Rule.Condition.Field fieldToMatch,
+                                   MimeMessageBuilder mimeMessageBuilder,
+                                   String valueToMatch,
+                                   JMAPFilteringTestSystem testSystem) throws Exception {
+        testSystem.defineRulesForRecipient1(Rule.Condition.of(fieldToMatch, NOT_EXACTLY_EQUALS, valueToMatch));
+        FakeMail mail = testSystem.asMail(mimeMessageBuilder);
+        testSystem.getJmapFiltering().service(mail);
+
+        assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+            .isNull();
+    }
+
+    @Nested
+    class MultiRuleBehaviourTest {
+        @Test
+        void mailDirectiveShouldSetFirstMatchedRuleWhenMultipleRules(JMAPFilteringTestSystem testSystem) throws Exception {
+            InMemoryMailboxManager mailboxManager = testSystem.getMailboxManager();
+            MailboxId mailbox1Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
+            MailboxId mailbox2Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
+            MailboxId mailbox3Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
+
+            testSystem.defineRulesForRecipient1();
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                ImmutableList.of(
+                    Rule.builder()
+                        .id(Rule.Id.of("1"))
+                        .name("rule 1")
+                        .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
+                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox1Id.serialize())))
+                        .build(),
+                    Rule.builder()
+                        .id(Rule.Id.of("2"))
+                        .name("rule 2")
+                        .condition(Rule.Condition.of(FROM, NOT_CONTAINS, USER_1_USERNAME))
+                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox2Id.serialize())))
+                        .build(),
+                    Rule.builder()
+                        .id(Rule.Id.of("3"))
+                        .name("rule 3")
+                        .condition(Rule.Condition.of(TO, EXACTLY_EQUALS, USER_3_ADDRESS))
+                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox3Id.serialize())))
+                        .build()));
+
+            FakeMail mail = FakeMail.builder()
+                .sender(USER_1_ADDRESS)
+                .recipients(RECIPIENT_1)
+                .mimeMessage(mimeMessageBuilder()
+                    .addFrom(USER_2_ADDRESS)
+                    .addToRecipient(USER_3_ADDRESS)
+                    .setSubject(UNSCRAMBLED_SUBJECT))
+                .build();
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isEqualTo("RECIPIENT_1_MAILBOX_1");
+        }
+
+        @Test
+        void mailDirectiveShouldSetFirstMatchedMailboxWhenMultipleMailboxes(JMAPFilteringTestSystem testSystem) throws Exception {
+            InMemoryMailboxManager mailboxManager = testSystem.getMailboxManager();
+            MailboxId mailbox1Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
+            MailboxId mailbox2Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
+            MailboxId mailbox3Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
+
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                ImmutableList.of(
+                    Rule.builder()
+                        .id(Rule.Id.of("1"))
+                        .name("rule 1")
+                        .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
+                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(
+                            mailbox3Id.serialize(),
+                            mailbox2Id.serialize(),
+                            mailbox1Id.serialize()))))
+                        .build()));
+
+            FakeMail mail = FakeMail.builder()
+                .sender(USER_1_ADDRESS)
+                .recipients(RECIPIENT_1)
+                .mimeMessage(mimeMessageBuilder()
+                    .setSubject(UNSCRAMBLED_SUBJECT))
+                .build();
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isEqualTo("RECIPIENT_1_MAILBOX_3");
+        }
+
+        @Test
+        void mailDirectiveShouldNotBeSetWhenAllDoNotExactlyEqualsRuleValue(JMAPFilteringTestSystem testSystem) throws Exception {
+            testSystem.defineRulesForRecipient1(
+                Rule.Condition.of(FROM, CONTAINS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(FROM, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(TO, CONTAINS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(TO, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(CC, CONTAINS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(CC, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(RECIPIENT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(RECIPIENT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(SUBJECT, CONTAINS, USER_1_FULL_ADDRESS),
+                Rule.Condition.of(SUBJECT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS)
+            );
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder());
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isNull();
+        }
+
+        @Test
+        void mailDirectiveShouldNotBeSetWhenNoneRulesValueIsContained(JMAPFilteringTestSystem testSystem) throws Exception {
+
+            testSystem.defineRulesForRecipient1(
+                Rule.Condition.of(FROM, CONTAINS, SHOULD_NOT_MATCH),
+                Rule.Condition.of(TO, CONTAINS, SHOULD_NOT_MATCH),
+                Rule.Condition.of(CC, CONTAINS, SHOULD_NOT_MATCH));
+
+            FakeMail mail = FakeMail.builder()
+                .sender(USER_1_ADDRESS)
+                .recipients(RECIPIENT_1)
+                .mimeMessage(mimeMessageBuilder()
+                    .addFrom(USER_1_FULL_ADDRESS)
+                    .addToRecipient(USER_2_FULL_ADDRESS)
+                    .addCcRecipient(USER_3_FULL_ADDRESS))
+                .build();
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isNull();
+        }
+    }
+}
\ No newline at end of file


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


[13/26] james-project git commit: JAMES-2529 Factorize RECIPIENT tests with other fields

Posted by ad...@apache.org.
JAMES-2529 Factorize RECIPIENT tests with other fields


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

Branch: refs/heads/master
Commit: c6a6e9769bb0953fdb3e5781d865296dd700c12c
Parents: 8b1c70a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 13:57:16 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:03 2018 +0200

----------------------------------------------------------------------
 .../jmap/mailet/filter/JMAPFilteringTest.java   | 322 ++++++-------------
 1 file changed, 106 insertions(+), 216 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c6a6e976/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
index 29d6bca..0aed4e4 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
@@ -31,7 +31,6 @@ import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.SUBJECT;
 import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.TO;
 import static org.apache.james.jmap.mailet.filter.ActionApplier.DELIVERY_PATH_PREFIX;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.BOU;
-import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.CC_HEADER;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.EMPTY;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULLNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULL_SCRAMBLED_ADDRESS;
@@ -40,7 +39,6 @@ import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_USERNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SCRAMBLED_SUBJECT;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SHOULD_NOT_MATCH;
-import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.TO_HEADER;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.UNFOLDED_USERNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.UNSCRAMBLED_SUBJECT;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_1_ADDRESS;
@@ -51,7 +49,6 @@ import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_AD
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_2_FULL_ADDRESS;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_ADDRESS;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_FULL_ADDRESS;
-import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_USERNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_4_FULL_ADDRESS;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
@@ -63,9 +60,11 @@ import java.util.stream.Stream;
 import org.apache.james.core.User;
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
 import org.apache.james.jmap.mailet.filter.JMAPFilteringExtension.JMAPFilteringTestSystem;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.util.StreamUtils;
+import org.apache.mailet.base.RFC2822Headers;
 import org.apache.mailet.base.test.FakeMail;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -133,13 +132,6 @@ class JMAPFilteringTest {
             return this;
         }
 
-        public FilteringArgumentBuilder headerForField(String headerValue) {
-            Preconditions.checkState(field.isPresent(), "field should be set first");
-
-            mimeMessageBuilder.addHeader(field.get().asString(), headerValue);
-            return this;
-        }
-
         public FilteringArgumentBuilder subject(String subject) {
             mimeMessageBuilder.setSubject(subject);
             return this;
@@ -197,121 +189,97 @@ class JMAPFilteringTest {
             .field(field);
     }
 
+    static class FieldAndHeader {
+        private final Rule.Condition.Field field;
+        private final String headerName;
+
+        public FieldAndHeader(Rule.Condition.Field field, String headerName) {
+            this.field = field;
+            this.headerName = headerName;
+        }
+    }
+
+    public static final ImmutableList<FieldAndHeader> ADDRESS_TESTING_COMBINATION = ImmutableList.of(
+        new FieldAndHeader(Field.FROM, RFC2822Headers.FROM),
+        new FieldAndHeader(Field.TO, RFC2822Headers.TO),
+        new FieldAndHeader(Field.CC, RFC2822Headers.CC),
+        new FieldAndHeader(Field.RECIPIENT, RFC2822Headers.TO),
+        new FieldAndHeader(Field.RECIPIENT, RFC2822Headers.CC));
+
     static Stream<Arguments> exactlyEqualsTestSuite() {
         return StreamUtils.flatten(
-            Stream.of(FROM, TO, CC)
-                .flatMap(headerField -> Stream.of(
-                    argumentBuilder(headerField)
+            ADDRESS_TESTING_COMBINATION
+                .stream()
+                .flatMap(fieldAndHeader -> Stream.of(
+                    argumentBuilder(fieldAndHeader.field)
                         .description("full address value")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_USERNAME),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("full address value (different case)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_USERNAME.toUpperCase(Locale.ENGLISH)),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("address only value")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_ADDRESS),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("address only value (different case)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_ADDRESS.toUpperCase(Locale.ENGLISH)),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("personal only value")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_FULL_ADDRESS),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("personal only value (different case)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_FULL_ADDRESS.toUpperCase()),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("personal header should match personal")
-                        .headerForField(USER_1_USERNAME)
+                        .header(fieldAndHeader.headerName, USER_1_USERNAME)
                         .valueToMatch(USER_1_USERNAME),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("address header should match address")
-                        .headerForField(USER_1_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_ADDRESS)
                         .valueToMatch(USER_1_ADDRESS),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("multiple headers")
-                        .headerForField(USER_1_FULL_ADDRESS)
-                        .headerForField(USER_2_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_2_FULL_ADDRESS)
                         .valueToMatch(USER_1_USERNAME),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("scrambled content")
-                        .headerForField(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .header(fieldAndHeader.headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
                         .valueToMatch(FRED_MARTIN_FULLNAME),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("folded content")
-                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
                         .valueToMatch(UNFOLDED_USERNAME),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("folded content (different case)")
-                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
                         .valueToMatch(UNFOLDED_USERNAME.toUpperCase()),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, personal match")
-                        .headerForField("Benoit <invalid>")
+                        .header(fieldAndHeader.headerName, "Benoit <invalid>")
                         .valueToMatch("Benoit"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, address match")
-                        .headerForField("Benoit <invalid>")
+                        .header(fieldAndHeader.headerName, "Benoit <invalid>")
                         .valueToMatch("invalid"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, full match")
-                        .headerForField("Benoit <invalid>")
+                        .header(fieldAndHeader.headerName, "Benoit <invalid>")
                         .valueToMatch("Benoit <invalid>"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid header, full match")
-                        .headerForField("Benoit <invalid")
+                        .header(fieldAndHeader.headerName, "Benoit <invalid")
                         .valueToMatch("Benoit <invalid")
                     ).map(FilteringArgumentBuilder::build)),
-            Stream.of(TO_HEADER, CC_HEADER)
-                .flatMap(headerName -> Stream.of(
-                    argumentBuilder(RECIPIENT)
-                        .description("full address " + headerName + " header")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_FULL_ADDRESS),
-                    argumentBuilder(RECIPIENT)
-                        .description("full address " + headerName + " header (different case)")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_FULL_ADDRESS.toUpperCase(Locale.ENGLISH)),
-                    argumentBuilder(RECIPIENT)
-                        .description("address only " + headerName + " header")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_ADDRESS),
-                    argumentBuilder(RECIPIENT)
-                        .description("personal only " + headerName + " header")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_USERNAME),
-                    argumentBuilder(RECIPIENT)
-                        .description("scrambled content in " + headerName + " header")
-                        .header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
-                        .valueToMatch(FRED_MARTIN_FULLNAME),
-                    argumentBuilder(RECIPIENT)
-                        .description("folded content in " + headerName + " header")
-                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch(UNFOLDED_USERNAME),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid " + headerName + " address, personal match")
-                        .header(headerName, "Benoit <invalid>")
-                        .valueToMatch("Benoit"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid " + headerName + " address, address match")
-                        .header(headerName, "Benoit <invalid>")
-                        .valueToMatch("invalid"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid " + headerName + " address, full match")
-                        .header(headerName, "Benoit <invalid>")
-                        .valueToMatch("Benoit <invalid>"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid " + headerName + ", full match")
-                        .header(headerName, "Benoit <invalid")
-                        .valueToMatch("Benoit <invalid"))
-                    .map(FilteringArgumentBuilder::build)),
             Stream.of(
-                argumentBuilder().description("multiple to and cc headers").field(RECIPIENT)
+                argumentBuilder().description("multiple to and cc headers")
+                    .field(RECIPIENT)
                     .ccRecipient(USER_1_FULL_ADDRESS)
                     .ccRecipient(USER_2_FULL_ADDRESS)
                     .toRecipient(USER_3_FULL_ADDRESS)
@@ -330,119 +298,68 @@ class JMAPFilteringTest {
 
     private static Stream<Arguments> containsArguments() {
         return StreamUtils.flatten(
-            Stream.of(FROM, TO, CC)
-                .flatMap(headerField -> Stream.of(
-                    argumentBuilder(headerField)
+            ADDRESS_TESTING_COMBINATION.stream()
+                .flatMap(fieldAndHeader -> Stream.of(
+                    argumentBuilder(fieldAndHeader.field)
                         .description("full address value (partial matching)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch("ser1 <"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("full address value (partial matching, different case)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch("SER1 <"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("address only value (partial matching)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch("ser1@jam"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("personal only value (partial matching)")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName,USER_1_FULL_ADDRESS)
                         .valueToMatch("ser1"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("address header & match in the address (partial matching)")
-                        .headerForField(USER_1_ADDRESS)
+                        .header(fieldAndHeader.headerName,USER_1_ADDRESS)
                         .valueToMatch("ser1@jam"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("raw value matching (partial matching)")
-                        .headerForField(GA_BOU_ZO_MEU_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName,GA_BOU_ZO_MEU_FULL_ADDRESS)
                         .valueToMatch(BOU),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("multiple headers (partial matching)")
-                        .headerForField(USER_1_FULL_ADDRESS)
-                        .headerForField(USER_2_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName,USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName,USER_2_FULL_ADDRESS)
                         .valueToMatch("ser1@jam"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("scrambled content (partial matching)")
-                        .headerForField(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .header(fieldAndHeader.headerName,FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
                         .valueToMatch("déric MAR"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("folded content (partial matching)")
-                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName,USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
                         .valueToMatch("ded_us"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, personal match (partial matching)")
-                        .headerForField("Benoit <invalid>")
+                        .header(fieldAndHeader.headerName,"Benoit <invalid>")
                         .valueToMatch("enoi"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, address match (partial matching)")
-                        .headerForField("Benoit <invalid>")
+                        .header(fieldAndHeader.headerName,"Benoit <invalid>")
                         .valueToMatch("nvali"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, full match (partial matching)")
-                        .headerForField("Benoit <invalid>")
+                        .header(fieldAndHeader.headerName,"Benoit <invalid>")
                         .valueToMatch("enoit <invali"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid header, full match (partial matching)")
-                        .headerForField("Benoit <invalid")
+                        .header(fieldAndHeader.headerName,"Benoit <invalid")
                         .valueToMatch("enoit <invali"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid header, personal match (partial matching)")
-                        .headerForField("Benoit <invalid")
+                        .header(fieldAndHeader.headerName,"Benoit <invalid")
                         .valueToMatch("enoi"),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid header, address match (partial matching)")
-                        .headerForField("Benoit <invalid")
-                        .valueToMatch("nvali"))
-                    .map(FilteringArgumentBuilder::build)),
-            Stream.of(TO_HEADER, CC_HEADER)
-                .flatMap(headerName -> Stream.of(
-                    argumentBuilder(RECIPIENT)
-                        .description("full address " + headerName + " header (partial matching)")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("ser3 <us"),
-                    argumentBuilder(RECIPIENT)
-                        .description("full address " + headerName + " header (partial matching, different case)")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("SER3 <US"),
-                    argumentBuilder(RECIPIENT)
-                        .description("address only " + headerName + " header (partial matching)")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("ser3@jam"),
-                    argumentBuilder(RECIPIENT)
-                        .description("personal only " + headerName + " header (partial matching)")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("ser3"),
-                    argumentBuilder(RECIPIENT)
-                        .description("scrambled content in " + headerName + " header (partial matching)")
-                        .header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
-                        .valueToMatch("déric MAR"),
-                    argumentBuilder(RECIPIENT)
-                        .description("folded content in " + headerName + " header (partial matching)")
-                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch("folded_us"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid address, personal match (partial matching)")
-                        .header(headerName, "Benoit <invalid>")
-                        .valueToMatch("enoi"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid address, address match (partial matching)")
-                        .header(headerName, "Benoit <invalid>")
-                        .valueToMatch("nvali"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid address, full match (partial matching)")
-                        .header(headerName, "Benoit <invalid>")
-                        .valueToMatch("enoit <invali"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid header, full match (partial matching)")
-                        .header(headerName, "Benoit <invalid")
-                        .valueToMatch("enoit <invali"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid header, personal match (partial matching)")
-                        .header(headerName, "Benoit <invalid")
-                        .valueToMatch("enoi"),
-                    argumentBuilder(RECIPIENT)
-                        .description("invalid header, address match (partial matching)")
-                        .header(headerName, "Benoit <invalid")
+                        .header(fieldAndHeader.headerName,"Benoit <invalid")
                         .valueToMatch("nvali"))
                     .map(FilteringArgumentBuilder::build)),
             Stream.of(
@@ -464,63 +381,36 @@ class JMAPFilteringTest {
 
     static Stream<Arguments> notContainsTestSuite() {
         return StreamUtils.flatten(
-            Stream.of(FROM, TO, CC)
-                .flatMap(headerField -> Stream.of(
-                    argumentBuilder(headerField)
+            ADDRESS_TESTING_COMBINATION.stream()
+                .flatMap(fieldAndHeader -> Stream.of(
+                    argumentBuilder(fieldAndHeader.field)
                         .description("normal content")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("multiple headers")
-                        .headerForField(USER_1_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS)
                         .from(USER_2_FULL_ADDRESS)
                         .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("scrambled content")
-                        .headerForField(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
+                        .header(fieldAndHeader.headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
                         .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("folded content")
-                        .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
                         .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(headerField)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("empty content")
-                        .headerForField(EMPTY)
-                        .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(headerField)
-                        .description("invalid address, personal match")
-                        .headerForField("Benoit <invalid>")
-                        .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(headerField)
-                        .description("invalid header, full match")
-                        .headerForField("Benoit <invalid")
-                        .valueToMatch(SHOULD_NOT_MATCH))
-                    .map(FilteringArgumentBuilder::build)),
-            Stream.of(TO_HEADER, CC_HEADER)
-                .flatMap(headerName -> Stream.of(
-                    argumentBuilder(RECIPIENT)
-                        .description("normal content " + headerName + " header")
-                        .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(RECIPIENT)
-                        .description("scrambled content in " + headerName + " header")
-                        .field(RECIPIENT).header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(RECIPIENT)
-                        .description("folded content in " + headerName + " header")
-                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(RECIPIENT)
-                        .description("bcc header")
-                        .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
+                        .header(fieldAndHeader.headerName, EMPTY)
                         .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(RECIPIENT)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid address, personal match")
-                        .header(headerName, "Benoit <invalid>")
+                        .header(fieldAndHeader.headerName, "Benoit <invalid>")
                         .valueToMatch(SHOULD_NOT_MATCH),
-                    argumentBuilder(RECIPIENT)
+                    argumentBuilder(fieldAndHeader.field)
                         .description("invalid header, full match")
-                        .header(headerName, "Benoit <invalid")
+                        .header(fieldAndHeader.headerName, "Benoit <invalid")
                         .valueToMatch(SHOULD_NOT_MATCH))
                     .map(FilteringArgumentBuilder::build)),
             Stream.of(


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


[11/26] james-project git commit: JAMES-2529 Remove .orElse(null)

Posted by ad...@apache.org.
JAMES-2529 Remove .orElse(null)


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

Branch: refs/heads/master
Commit: b62bc554ddb3c31556b0a8ca33124274c878ca18
Parents: e06fb4a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 10:21:37 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:02 2018 +0200

----------------------------------------------------------------------
 .../java/org/apache/james/jmap/mailet/filter/MailMatcher.java  | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/b62bc554/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index aea0b75..410146e 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -133,9 +133,9 @@ public interface MailMatcher {
         ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents
             .map(ContentMatcher::asAddressHeader)
             .anyMatch(addressHeader ->
-                StringUtils.equalsIgnoreCase(addressHeader.getFullAddress(), valueToMatch)
-                    || StringUtils.equalsIgnoreCase(addressHeader.getAddress().orElse(null), valueToMatch)
-                    || StringUtils.equalsIgnoreCase(addressHeader.getPersonal().orElse(null), valueToMatch));
+                valueToMatch.equalsIgnoreCase(addressHeader.getFullAddress())
+                    || addressHeader.getAddress().map(valueToMatch::equalsIgnoreCase).orElse(false)
+                    || addressHeader.getPersonal().map(valueToMatch::equalsIgnoreCase).orElse(false));
         ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
 
         Map<Rule.Condition.Comparator, ContentMatcher> HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()


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


[25/26] james-project git commit: JAMES-2529 JMAP filtering mailet should be compulsory

Posted by ad...@apache.org.
JAMES-2529 JMAP filtering mailet should be compulsory


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

Branch: refs/heads/master
Commit: 9ba6a1dd270f99735c7f9d3d4b2adb5076583c10
Parents: 808c8fa
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 12:36:24 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:55 2018 +0200

----------------------------------------------------------------------
 .../src/test/resources/mailetcontainer.xml      |  1 +
 .../sample-configuration/mailetcontainer.xml    |  1 +
 .../src/test/resources/mailetcontainer.xml      |  1 +
 .../java/org/apache/james/jmap/JMAPModule.java  |  8 +++-
 .../resources/defaultJmapMailetContainer.xml    |  1 +
 .../james/jmap/MailetPreconditionTest.java      | 40 ++++++++++++++++++++
 6 files changed, 51 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9ba6a1dd/server/container/cli-integration/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/container/cli-integration/src/test/resources/mailetcontainer.xml b/server/container/cli-integration/src/test/resources/mailetcontainer.xml
index f14a335..cb2c0dc 100644
--- a/server/container/cli-integration/src/test/resources/mailetcontainer.xml
+++ b/server/container/cli-integration/src/test/resources/mailetcontainer.xml
@@ -58,6 +58,7 @@
             </mailet>
             <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
             <mailet match="RecipientIsLocal" class="Sieve"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ba6a1dd/server/container/guice/memory-guice/sample-configuration/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/memory-guice/sample-configuration/mailetcontainer.xml b/server/container/guice/memory-guice/sample-configuration/mailetcontainer.xml
index a910f1b..6b29c87 100644
--- a/server/container/guice/memory-guice/sample-configuration/mailetcontainer.xml
+++ b/server/container/guice/memory-guice/sample-configuration/mailetcontainer.xml
@@ -59,6 +59,7 @@
             <mailet match="All" class="RecipientRewriteTable" />
             <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
             <mailet match="RecipientIsLocal" class="Sieve"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
 
             <mailet match="relay-allowed" class="RemoteDelivery">

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ba6a1dd/server/container/guice/memory-guice/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/memory-guice/src/test/resources/mailetcontainer.xml b/server/container/guice/memory-guice/src/test/resources/mailetcontainer.xml
index ab279cc..e82f960 100644
--- a/server/container/guice/memory-guice/src/test/resources/mailetcontainer.xml
+++ b/server/container/guice/memory-guice/src/test/resources/mailetcontainer.xml
@@ -65,6 +65,7 @@
             <mailet match="IsMarkedAsSpam" class="WithStorageDirective">
                 <targetFolderName>Spam</targetFolderName>
             </mailet>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ba6a1dd/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
index 5158d2f..365df71 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
@@ -31,6 +31,7 @@ import org.apache.commons.io.FileUtils;
 import org.apache.james.filesystem.api.FileSystem;
 import org.apache.james.jmap.event.PropagateLookupRightListener;
 import org.apache.james.jmap.mailet.VacationMailet;
+import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.jmap.methods.RequestHandler;
 import org.apache.james.jmap.send.PostDequeueDecoratorFactory;
 import org.apache.james.jmap.utils.HtmlTextExtractor;
@@ -76,6 +77,10 @@ public class JMAPModule extends AbstractModule {
         new CamelMailetContainerModule.TransportProcessorCheck.Impl(
             RecipientIsLocal.class,
             VacationMailet.class);
+    public static final CamelMailetContainerModule.TransportProcessorCheck FILTERING_MAILET_CHECK =
+        new CamelMailetContainerModule.TransportProcessorCheck.Impl(
+            RecipientIsLocal.class,
+            JMAPFiltering.class);
 
     @Override
     protected void configure() {
@@ -96,7 +101,8 @@ public class JMAPModule extends AbstractModule {
 
         Multibinder<CamelMailetContainerModule.TransportProcessorCheck> transportProcessorChecks = Multibinder.newSetBinder(binder(), CamelMailetContainerModule.TransportProcessorCheck.class);
         transportProcessorChecks.addBinding().toInstance(VACATION_MAILET_CHECK);
-        
+        transportProcessorChecks.addBinding().toInstance(FILTERING_MAILET_CHECK);
+
         bind(SystemMailboxesProvider.class).to(SystemMailboxesProviderImpl.class);
         bind(MailQueueItemDecoratorFactory.class).to(PostDequeueDecoratorFactory.class).in(Scopes.SINGLETON);
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ba6a1dd/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml b/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
index ccf6017..4f70e72 100644
--- a/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
+++ b/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
@@ -42,6 +42,7 @@
         </mailet>
         <mailet match="All" class="RecipientRewriteTable" />
         <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
+        <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
         <mailet match="RecipientIsLocal" class="LocalDelivery"/>
         <mailet match="HostIsLocal" class="ToProcessor">
             <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/9ba6a1dd/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
index aaacbd9..6971db3 100644
--- a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
+++ b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
@@ -26,6 +26,7 @@ import java.util.List;
 
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.james.jmap.mailet.VacationMailet;
+import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.mailetcontainer.impl.MatcherMailetPair;
 import org.apache.james.modules.server.CamelMailetContainerModule;
 import org.apache.james.transport.mailets.Null;
@@ -84,6 +85,45 @@ class MailetPreconditionTest {
     }
 
     @Nested
+    class FilteringMailetCheck {
+        @Test
+        void filteringMailetCheckShouldThrowOnEmptyList() {
+            assertThatThrownBy(() -> JMAPModule.FILTERING_MAILET_CHECK.check(Lists.newArrayList()))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void filteringMailetCheckShouldThrowOnNullList() {
+            assertThatThrownBy(() -> JMAPModule.FILTERING_MAILET_CHECK.check(null))
+                .isInstanceOf(NullPointerException.class);
+        }
+
+        @Test
+        void filteringMailetCheckShouldThrowOnWrongMatcher() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new JMAPFiltering(null, null, null)));
+
+            assertThatThrownBy(() -> JMAPModule.FILTERING_MAILET_CHECK.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void filteringMailetCheckShouldThrowOnWrongMailet() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new Null()));
+
+            assertThatThrownBy(() -> JMAPModule.FILTERING_MAILET_CHECK.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void filteringMailetCheckShouldNotThrowIfValidPairPresent() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new JMAPFiltering(null, null, null)));
+
+            assertThatCode(() -> JMAPModule.FILTERING_MAILET_CHECK.check(pairs))
+                .doesNotThrowAnyException();
+        }
+    }
+
+    @Nested
     class BccCheck {
         @Test
         void bccMailetCheckShouldThrowOnEmptyList() {


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


[24/26] james-project git commit: JAMES-2529 Configure JMAP filtering mailet by default

Posted by ad...@apache.org.
JAMES-2529 Configure JMAP filtering mailet by default


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

Branch: refs/heads/master
Commit: 808c8fa3cf44810481ba7be2f656ece9c50082a3
Parents: 7164031
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 12:35:34 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:55 2018 +0200

----------------------------------------------------------------------
 .../cassandra/package/etc/james/templates/mailetcontainer.xml    | 1 +
 .../run/guice/cassandra/destination/conf/mailetcontainer.xml     | 1 +
 .../guice/cassandra-guice/src/test/resources/mailetcontainer.xml | 1 +
 .../cassandra-ldap-guice/src/test/resources/mailetcontainer.xml  | 1 +
 .../org/apache/james/mailets/configuration/CommonProcessors.java | 4 ++++
 .../apache/james/transport/mailets/GroupMappingRelayTest.java    | 4 ++++
 .../org/apache/james/transport/mailets/GroupMappingTest.java     | 4 ++++
 .../src/test/resources/mailetcontainer.xml                       | 1 +
 8 files changed, 17 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/dockerfiles/packaging/guice/cassandra/package/etc/james/templates/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/dockerfiles/packaging/guice/cassandra/package/etc/james/templates/mailetcontainer.xml b/dockerfiles/packaging/guice/cassandra/package/etc/james/templates/mailetcontainer.xml
index 681b966..e8200f4 100644
--- a/dockerfiles/packaging/guice/cassandra/package/etc/james/templates/mailetcontainer.xml
+++ b/dockerfiles/packaging/guice/cassandra/package/etc/james/templates/mailetcontainer.xml
@@ -64,6 +64,7 @@
             <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
             <mailet match="RecipientIsLocal" class="Sieve"/>
             <mailet match="RecipientIsLocal" class="AddDeliveredToHeader"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/dockerfiles/run/guice/cassandra/destination/conf/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/mailetcontainer.xml b/dockerfiles/run/guice/cassandra/destination/conf/mailetcontainer.xml
index 3839b39..04d0459 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/mailetcontainer.xml
+++ b/dockerfiles/run/guice/cassandra/destination/conf/mailetcontainer.xml
@@ -66,6 +66,7 @@
             <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
             <mailet match="RecipientIsLocal" class="Sieve"/>
             <mailet match="RecipientIsLocal" class="AddDeliveredToHeader"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml b/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
index 2365c7c..412e6e9 100644
--- a/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
+++ b/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
@@ -74,6 +74,7 @@
             <mailet match="IsMarkedAsSpam" class="WithStorageDirective">
                 <targetFolderName>Spam</targetFolderName>
             </mailet>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/server/container/guice/cassandra-ldap-guice/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-ldap-guice/src/test/resources/mailetcontainer.xml b/server/container/guice/cassandra-ldap-guice/src/test/resources/mailetcontainer.xml
index 4a7d1e8..69d03e2 100644
--- a/server/container/guice/cassandra-ldap-guice/src/test/resources/mailetcontainer.xml
+++ b/server/container/guice/cassandra-ldap-guice/src/test/resources/mailetcontainer.xml
@@ -59,6 +59,7 @@
             </mailet>
             <mailet match="All" class="RecipientRewriteTable" />
             <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
index 936124f..503b940 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
@@ -21,6 +21,7 @@
 package org.apache.james.mailets.configuration;
 
 import org.apache.james.jmap.mailet.VacationMailet;
+import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.transport.mailets.AddDeliveredToHeader;
 import org.apache.james.transport.mailets.Bounce;
@@ -95,6 +96,9 @@ public class CommonProcessors {
                         .mailet(VacationMailet.class))
                 .addMailet(MailetConfiguration.builder()
                         .matcher(RecipientIsLocal.class)
+                        .mailet(JMAPFiltering.class))
+                .addMailet(MailetConfiguration.builder()
+                        .matcher(RecipientIsLocal.class)
                         .mailet(Sieve.class))
                 .addMailet(MailetConfiguration.builder()
                         .matcher(RecipientIsLocal.class)

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingRelayTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingRelayTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingRelayTest.java
index 4ba8505..a4de0bb 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingRelayTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingRelayTest.java
@@ -30,6 +30,7 @@ import javax.mail.internet.MimeMessage;
 
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.jmap.mailet.VacationMailet;
+import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.mailets.TemporaryJamesServer;
 import org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
@@ -100,6 +101,9 @@ public class GroupMappingRelayTest {
                 .addMailet(MailetConfiguration.builder()
                     .matcher(RecipientIsLocal.class)
                     .mailet(VacationMailet.class))
+                .addMailet(MailetConfiguration.builder()
+                    .matcher(RecipientIsLocal.class)
+                    .mailet(JMAPFiltering.class))
                 .addMailetsFrom(CommonProcessors.deliverOnlyTransport())
                 .addMailet(MailetConfiguration.remoteDeliveryBuilder()
                     .matcher(All.class)

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
index 7b9dc10..a2ff045 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
@@ -30,6 +30,7 @@ import javax.mail.internet.MimeMessage;
 
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.jmap.mailet.VacationMailet;
+import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailets.TemporaryJamesServer;
 import org.apache.james.mailets.configuration.CommonProcessors;
@@ -98,6 +99,9 @@ public class GroupMappingTest {
                 .addMailet(MailetConfiguration.builder()
                     .matcher(RecipientIsLocal.class)
                     .mailet(VacationMailet.class))
+                .addMailet(MailetConfiguration.builder()
+                    .matcher(RecipientIsLocal.class)
+                    .mailet(JMAPFiltering.class))
                 .addMailetsFrom(CommonProcessors.deliverOnlyTransport()))
             .putProcessor(ProcessorConfiguration.builder()
                 .state(RRT_ERROR)

http://git-wip-us.apache.org/repos/asf/james-project/blob/808c8fa3/server/protocols/webadmin-integration-test/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin-integration-test/src/test/resources/mailetcontainer.xml b/server/protocols/webadmin-integration-test/src/test/resources/mailetcontainer.xml
index f24fd9c..f9d4127 100644
--- a/server/protocols/webadmin-integration-test/src/test/resources/mailetcontainer.xml
+++ b/server/protocols/webadmin-integration-test/src/test/resources/mailetcontainer.xml
@@ -60,6 +60,7 @@
             <mailet match="All" class="RecipientRewriteTable" />
             <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.VacationMailet"/>
             <mailet match="RecipientIsLocal" class="Sieve"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>


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


[19/26] james-project git commit: JAMES-2529 Factorize code for Transport checks

Posted by ad...@apache.org.
JAMES-2529 Factorize code for Transport checks


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

Branch: refs/heads/master
Commit: 8382d61981e89c6d2387734aacd18aa57a456670
Parents: 472bf31
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 12:20:32 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:54 2018 +0200

----------------------------------------------------------------------
 .../server/CamelMailetContainerModule.java      | 56 +++++++++++++++-----
 .../java/org/apache/james/jmap/JMAPModule.java  | 19 ++-----
 .../james/jmap/MailetPreconditionTest.java      | 22 ++++----
 3 files changed, 59 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/8382d619/server/container/guice/mailet/src/main/java/org/apache/james/modules/server/CamelMailetContainerModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/mailet/src/main/java/org/apache/james/modules/server/CamelMailetContainerModule.java b/server/container/guice/mailet/src/main/java/org/apache/james/modules/server/CamelMailetContainerModule.java
index 0d41d59..92fd1a5 100644
--- a/server/container/guice/mailet/src/main/java/org/apache/james/modules/server/CamelMailetContainerModule.java
+++ b/server/container/guice/mailet/src/main/java/org/apache/james/modules/server/CamelMailetContainerModule.java
@@ -22,6 +22,7 @@ package org.apache.james.modules.server;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.Predicate;
 
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.impl.SimpleRegistry;
@@ -50,7 +51,9 @@ import org.apache.james.utils.GuiceMatcherLoader;
 import org.apache.james.utils.GuiceProbe;
 import org.apache.james.utils.MailetConfigurationOverride;
 import org.apache.james.utils.SpoolerProbe;
+import org.apache.mailet.Mailet;
 import org.apache.mailet.MailetContext;
+import org.apache.mailet.Matcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,6 +70,12 @@ public class CamelMailetContainerModule extends AbstractModule {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(CamelMailetContainerModule.class);
 
+    public static final TransportProcessorCheck.Impl BCC_Check = new TransportProcessorCheck.Impl(
+        All.class,
+        RemoveMimeHeader.class,
+        pair -> pair.getMailet().getMailetConfig().getInitParameter("name").equals("bcc"),
+        "Should be configured to remove Bcc header");
+
     @Override
     protected void configure() {
         bind(CamelCompositeProcessor.class).in(Scopes.SINGLETON);
@@ -85,7 +94,7 @@ public class CamelMailetContainerModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(MailetModuleConfigurationPerformer.class);
 
         Multibinder<CamelMailetContainerModule.TransportProcessorCheck> transportProcessorChecks = Multibinder.newSetBinder(binder(), CamelMailetContainerModule.TransportProcessorCheck.class);
-        transportProcessorChecks.addBinding().to(BccMailetCheck.class);
+        transportProcessorChecks.addBinding().toInstance(BCC_Check);
     }
 
     @Provides
@@ -215,18 +224,39 @@ public class CamelMailetContainerModule extends AbstractModule {
     @FunctionalInterface
     public interface TransportProcessorCheck {
         void check(List<MatcherMailetPair> pairs) throws ConfigurationException;
-    }
-    
-    public static class BccMailetCheck implements CamelMailetContainerModule.TransportProcessorCheck {
-        @Override
-        public void check(List<MatcherMailetPair> pairs) throws ConfigurationException {
-            Preconditions.checkNotNull(pairs);
-            pairs.stream()
-                .filter(pair -> pair.getMailet().getClass().equals(RemoveMimeHeader.class))
-                .filter(pair -> pair.getMatcher().getClass().equals(All.class))
-                .filter(pair -> pair.getMailet().getMailetConfig().getInitParameter("name").equals("bcc"))
-                .findAny()
-                .orElseThrow(() -> new ConfigurationException("Missing RemoveMimeHeader in mailets configuration (mailetcontainer -> processors -> transport). Should be configured to remove Bcc header"));
+
+        class Impl implements TransportProcessorCheck {
+            private final Class<? extends Matcher> matcherClass;
+            private final Class<? extends Mailet> mailetClass;
+            private final Optional<Predicate<? super MatcherMailetPair>> additionalFilter;
+            private final Optional<String> additionalErrorMessage;
+
+            public Impl(Class<? extends Matcher> matcherClass, Class<? extends Mailet> mailetClass) {
+                this(matcherClass, mailetClass, Optional.empty(), Optional.empty());
+            }
+
+            public Impl(Class<? extends Matcher> matcherClass, Class<? extends Mailet> mailetClass, Predicate<? super MatcherMailetPair> additionalFilter, String additionalErrorMessage) {
+                this(matcherClass, mailetClass, Optional.of(additionalFilter), Optional.of(additionalErrorMessage));
+            }
+
+            private Impl(Class<? extends Matcher> matcherClass, Class<? extends Mailet> mailetClass, Optional<Predicate<? super MatcherMailetPair>> additionalFilter, Optional<String> additionalErrorMessage) {
+                this.matcherClass = matcherClass;
+                this.mailetClass = mailetClass;
+                this.additionalFilter = additionalFilter;
+                this.additionalErrorMessage = additionalErrorMessage;
+            }
+
+            @Override
+            public void check(List<MatcherMailetPair> pairs) throws ConfigurationException {
+                Preconditions.checkNotNull(pairs);
+                pairs.stream()
+                    .filter(pair -> pair.getMailet().getClass().equals(mailetClass))
+                    .filter(pair -> pair.getMatcher().getClass().equals(matcherClass))
+                    .filter(additionalFilter.orElse(any -> true))
+                    .findAny()
+                    .orElseThrow(() -> new ConfigurationException("Missing " + mailetClass.getName() + " in mailets configuration (mailetcontainer -> processors -> transport). " +
+                        additionalErrorMessage.orElse("")));
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/8382d619/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
index b70c953..5158d2f 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
@@ -42,7 +42,6 @@ import org.apache.james.lifecycle.api.Configurable;
 import org.apache.james.mailbox.MailboxListener;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
-import org.apache.james.mailetcontainer.impl.MatcherMailetPair;
 import org.apache.james.modules.server.CamelMailetContainerModule;
 import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
 import org.apache.james.server.core.configuration.FileConfigurationProvider;
@@ -73,6 +72,10 @@ public class JMAPModule extends AbstractModule {
                 throw new RuntimeException(e);
             }
         };
+    public static final CamelMailetContainerModule.TransportProcessorCheck VACATION_MAILET_CHECK =
+        new CamelMailetContainerModule.TransportProcessorCheck.Impl(
+            RecipientIsLocal.class,
+            VacationMailet.class);
 
     @Override
     protected void configure() {
@@ -92,7 +95,7 @@ public class JMAPModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(RequiredCapabilitiesPrecondition.class);
 
         Multibinder<CamelMailetContainerModule.TransportProcessorCheck> transportProcessorChecks = Multibinder.newSetBinder(binder(), CamelMailetContainerModule.TransportProcessorCheck.class);
-        transportProcessorChecks.addBinding().to(VacationMailetCheck.class);
+        transportProcessorChecks.addBinding().toInstance(VACATION_MAILET_CHECK);
         
         bind(SystemMailboxesProvider.class).to(SystemMailboxesProviderImpl.class);
         bind(MailQueueItemDecoratorFactory.class).to(PostDequeueDecoratorFactory.class).in(Scopes.SINGLETON);
@@ -166,16 +169,4 @@ public class JMAPModule extends AbstractModule {
         }
     }
 
-    public static class VacationMailetCheck implements CamelMailetContainerModule.TransportProcessorCheck {
-        @Override
-        public void check(List<MatcherMailetPair> pairs) throws ConfigurationException {
-            Preconditions.checkNotNull(pairs);
-            pairs.stream()
-                .filter(pair -> pair.getMailet().getClass().equals(VacationMailet.class))
-                .filter(pair -> pair.getMatcher().getClass().equals(RecipientIsLocal.class))
-                .findAny()
-                .orElseThrow(() -> new ConfigurationException("Missing " + VacationMailet.class.getName() + " in mailets configuration (mailetcontainer -> processors -> transport)"));
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/8382d619/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
index 0e1af99..72d74f2 100644
--- a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
+++ b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
@@ -44,52 +44,52 @@ public class MailetPreconditionTest {
 
     @Test(expected = ConfigurationException.class)
     public void vacationMailetCheckShouldThrowOnEmptyList() throws Exception {
-        new JMAPModule.VacationMailetCheck().check(Lists.newArrayList());
+        JMAPModule.VACATION_MAILET_CHECK.check(Lists.newArrayList());
     }
 
     @Test(expected = NullPointerException.class)
     public void vacationMailetCheckShouldThrowOnNullList() throws Exception {
-        new JMAPModule.VacationMailetCheck().check(null);
+        JMAPModule.VACATION_MAILET_CHECK.check(null);
     }
 
     @Test(expected = ConfigurationException.class)
     public void vacationMailetCheckShouldThrowOnWrongMatcher() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new VacationMailet(null, null, null, null, null)));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
     public void vacationMailetCheckShouldThrowOnWrongMailet() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new Null()));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 
     @Test
     public void vacationMailetCheckShouldNotThrowIfValidPairPresent() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new VacationMailet(null, null, null, null, null)));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
     public void bccMailetCheckShouldThrowOnEmptyList() throws Exception {
-        new CamelMailetContainerModule.BccMailetCheck().check(Lists.newArrayList());
+        CamelMailetContainerModule.BCC_Check.check(Lists.newArrayList());
     }
 
     @Test(expected = NullPointerException.class)
     public void bccMailetCheckShouldThrowOnNullList() throws Exception {
-        new CamelMailetContainerModule.BccMailetCheck().check(null);
+        CamelMailetContainerModule.BCC_Check.check(null);
     }
 
     @Test(expected = ConfigurationException.class)
     public void bccMailetCheckShouldThrowOnWrongMatcher() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(),  new RemoveMimeHeader()));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
     public void bccMailetCheckShouldThrowOnWrongMailet() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new Null()));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
@@ -104,7 +104,7 @@ public class MailetPreconditionTest {
                 .build());
 
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
@@ -117,6 +117,6 @@ public class MailetPreconditionTest {
                 .build());
 
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
-        new JMAPModule.VacationMailetCheck().check(pairs);
+        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
     }
 }


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


[15/26] james-project git commit: JAMES-2529 Add tests for OptionalUtils::ofNullableToStream

Posted by ad...@apache.org.
JAMES-2529 Add tests for OptionalUtils::ofNullableToStream


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

Branch: refs/heads/master
Commit: d0ee96861c32bfd6ee5ea9ba50919d16f1d97090
Parents: 1e2124e
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 14:53:09 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:03 2018 +0200

----------------------------------------------------------------------
 .../apache/james/util/OptionalUtilsTest.java    | 23 +++++++++++++-------
 1 file changed, 15 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/d0ee9686/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
index 17fe06e..f7d7bf2 100644
--- a/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
+++ b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
@@ -27,8 +27,6 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import com.github.steveash.guavate.Guavate;
-
 public class OptionalUtilsTest {
 
     @Rule
@@ -69,18 +67,14 @@ public class OptionalUtilsTest {
 
     @Test
     public void toStreamShouldConvertEmptyOptionalToEmptyStream() {
-        assertThat(
-            OptionalUtils.toStream(Optional.empty())
-                .collect(Guavate.toImmutableList()))
+        assertThat(OptionalUtils.toStream(Optional.empty()))
             .isEmpty();
     }
 
     @Test
     public void toStreamShouldConvertFullOptionalToStream() {
         long value = 18L;
-        assertThat(
-            OptionalUtils.toStream(Optional.of(value))
-                .collect(Guavate.toImmutableList()))
+        assertThat(OptionalUtils.toStream(Optional.of(value)))
             .containsExactly(value);
     }
 
@@ -91,6 +85,19 @@ public class OptionalUtilsTest {
     }
 
     @Test
+    public void ofNullableToStreamShouldReturnAStreamContainingTheValueWhenNotNull() {
+        long value = 18L;
+        assertThat(OptionalUtils.ofNullableToStream(value))
+            .containsExactly(value);
+    }
+
+    @Test
+    public void ofNullableToStreamShouldReturnAnEmptyStreamWhenNull() {
+        assertThat(OptionalUtils.ofNullableToStream(null))
+            .isEmpty();
+    }
+
+    @Test
     public void orShouldReturnEmptyWhenEmpty() {
         assertThat(
             OptionalUtils.or(


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


[18/26] james-project git commit: JAMES-2529 Document filtering mailet

Posted by ad...@apache.org.
JAMES-2529 Document filtering mailet


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

Branch: refs/heads/master
Commit: 472bf31a47f8a5ffaa82a0ba15577eecaab67e96
Parents: b53509c
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 12:00:36 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:54 2018 +0200

----------------------------------------------------------------------
 .../apache/james/jmap/mailet/filter/JMAPFiltering.java   | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/472bf31a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
index a68193a..0a71376 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
@@ -36,6 +36,17 @@ import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Mailet for applying JMAP filtering to incoming email.
+ *
+ * Users are able to set their personal filtering rules using JMAP setFilter/getFilter methods.
+ *
+ * Configuring this mailet in the 'transport' processor is mandatory when running a JMAP server.
+ *
+ * Example:
+ *
+ *  &lt;mailet matcher="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/&gt;
+ */
 public class JMAPFiltering extends GenericMailet {
 
     private final Logger logger = LoggerFactory.getLogger(JMAPFiltering.class);


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


[04/26] james-project git commit: JAMES-2529 Improve addressExtractor factory method

Posted by ad...@apache.org.
JAMES-2529 Improve addressExtractor factory method


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

Branch: refs/heads/master
Commit: 016a2826021340c825187ed408d7cce48602a410
Parents: 897cb08
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 09:24:11 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:01 2018 +0200

----------------------------------------------------------------------
 .../apache/james/jmap/mailet/filter/MailMatcher.java  | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/016a2826/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index c9ea72b..4626a7f 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -21,7 +21,9 @@ package org.apache.james.jmap.mailet.filter;
 
 import static org.apache.james.jmap.api.filtering.Rule.Condition;
 
+import java.util.Arrays;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Stream;
 
@@ -30,7 +32,6 @@ import javax.mail.Message;
 import javax.mail.internet.AddressException;
 import javax.mail.internet.InternetAddress;
 
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.james.javax.AddressHelper;
 import org.apache.james.jmap.api.filtering.Rule;
@@ -179,9 +180,8 @@ public interface MailMatcher {
                     .map(Stream::of)
                     .orElse(Stream.empty());
     HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
-            ArrayUtils.addAll(
                 mail.getMessage().getRecipients(Message.RecipientType.TO),
-                mail.getMessage().getRecipients(Message.RecipientType.CC)));
+                mail.getMessage().getRecipients(Message.RecipientType.CC));
 
     HeaderExtractor FROM_EXTRACTOR = mail -> addressExtractor(mail.getMessage().getFrom());
     HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
@@ -210,10 +210,12 @@ public interface MailMatcher {
         return mail -> addressExtractor(mail.getMessage().getRecipients(type));
     }
 
-    static Stream<String> addressExtractor(Address[] addresses) {
+    static Stream<String> addressExtractor(Address[]... addresses) {
         return Optional.ofNullable(addresses)
-                .map(AddressHelper::asStringStream)
-                .orElse(Stream.empty());
+            .map(Arrays::stream)
+            .orElse(Stream.empty())
+            .filter(Objects::nonNull)
+            .flatMap(AddressHelper::asStringStream);
     }
 
     static Optional<HeaderExtractor> getHeaderExtractor(Field field) {


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


[22/26] james-project git commit: JAMES-2529 Modernize default JMAP configuration

Posted by ad...@apache.org.
JAMES-2529 Modernize default JMAP configuration


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

Branch: refs/heads/master
Commit: 2dcde783134618cf92690aa697a281606a24d699
Parents: 9ba6a1d
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 14:11:00 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:55 2018 +0200

----------------------------------------------------------------------
 .../resources/defaultJmapMailetContainer.xml    | 21 ++++++++------------
 1 file changed, 8 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/2dcde783/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml b/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
index 4f70e72..713316e 100644
--- a/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
+++ b/server/container/guice/protocols/jmap/src/main/resources/defaultJmapMailetContainer.xml
@@ -31,8 +31,13 @@
         <mailet match="All" class="Null"/>
     </processor>
 
-
     <processor state="transport" enableJmx="false">
+        <matcher name="relay-allowed" match="org.apache.james.mailetcontainer.impl.matchers.Or">
+            <matcher match="SMTPAuthSuccessful"/>
+            <matcher match="SMTPIsAuthNetwork"/>
+            <matcher match="SentByMailet"/>
+            <matcher match="org.apache.james.jmap.mailet.SentByJmap"/>
+        </matcher>
         <mailet match="SMTPAuthSuccessful" class="SetMimeHeader">
             <name>X-UserIsAuth</name>
             <value>true</value>
@@ -48,13 +53,7 @@
             <processor>local-address-error</processor>
             <notice>550 - Requested action not taken: no such user here</notice>
         </mailet>
-        <mailet match="SMTPAuthSuccessful" class="ToProcessor">
-            <processor>relay</processor>
-        </mailet>
-        <mailet match="org.apache.james.jmap.mailet.SentByJmap" class="ToProcessor">
-            <processor>relay</processor>
-        </mailet>
-        <mailet match="SentByMailet" class="ToProcessor">
+        <mailet match="relay-allowed" class="ToProcessor">
             <processor>relay</processor>
         </mailet>
         <mailet match="All" class="ToProcessor">
@@ -65,7 +64,7 @@
     <processor state="relay" enableJmx="true">
         <mailet match="All" class="RemoteDelivery">
             <outgoingQueue>outgoing</outgoingQueue>
-            <delayTime>5000, 100000, 500000</delayTime>
+            <delayTime>5000, 100000, 23*500000</delayTime>
             <maxRetries>25</maxRetries>
             <maxDnsProblemRetries>0</maxDnsProblemRetries>
             <deliveryThreads>10</deliveryThreads>
@@ -74,10 +73,6 @@
         </mailet>
     </processor>
 
-    <processor state="spam" enableJmx="false">
-        <mailet match="All" class="Null"/>
-    </processor>
-
     <processor state="local-address-error" enableJmx="false">
         <mailet match="All" class="Bounce">
             <attachment>none</attachment>


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


[16/26] james-project git commit: JAMES-2529 Extract ContentMatcher and HeaderExtractor from MailMatcher

Posted by ad...@apache.org.
JAMES-2529 Extract ContentMatcher and HeaderExtractor from MailMatcher


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

Branch: refs/heads/master
Commit: 7527daec794b8e0022161e079e9a2bf710c58e77
Parents: aee5424
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 15:02:55 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:03 2018 +0200

----------------------------------------------------------------------
 .../jmap/mailet/filter/ContentMatcher.java      | 118 +++++++++++++++
 .../jmap/mailet/filter/HeaderExtractor.java     |  93 ++++++++++++
 .../james/jmap/mailet/filter/MailMatcher.java   | 150 -------------------
 3 files changed, 211 insertions(+), 150 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7527daec/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
new file mode 100644
index 0000000..a388332
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
@@ -0,0 +1,118 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+public interface ContentMatcher {
+
+    class AddressHeader {
+        private static final Logger LOGGER = LoggerFactory.getLogger(AddressHeader.class);
+
+        private final Optional<String> personal;
+        private final Optional<String> address;
+        private final String fullAddress;
+
+        private AddressHeader(String fullAddress) {
+            this.fullAddress = fullAddress;
+            Optional<InternetAddress> internetAddress = parseFullAddress();
+            this.personal = internetAddress.map(InternetAddress::getPersonal);
+            this.address = internetAddress.map(InternetAddress::getAddress);
+        }
+
+        private Optional<InternetAddress> parseFullAddress() {
+            try {
+                return Optional.of(new InternetAddress(fullAddress));
+            } catch (AddressException e) {
+                LOGGER.error("error while parsing full address {}", fullAddress, e);
+                return Optional.empty();
+            }
+        }
+    }
+
+    ContentMatcher STRING_CONTAINS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.contains(content, valueToMatch));
+    ContentMatcher STRING_NOT_CONTAINS_MATCHER = negate(STRING_CONTAINS_MATCHER);
+    ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.equals(content, valueToMatch));
+    ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = negate(STRING_EXACTLY_EQUALS_MATCHER);
+
+    ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> contents
+        .map(ContentMatcher::asAddressHeader)
+        .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.fullAddress, valueToMatch));
+    ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = negate(ADDRESS_CONTAINS_MATCHER);
+    ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents
+        .map(ContentMatcher::asAddressHeader)
+        .anyMatch(addressHeader ->
+            valueToMatch.equalsIgnoreCase(addressHeader.fullAddress)
+                || addressHeader.address.map(valueToMatch::equalsIgnoreCase).orElse(false)
+                || addressHeader.personal.map(valueToMatch::equalsIgnoreCase).orElse(false));
+    ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
+
+    Map<Rule.Condition.Comparator, ContentMatcher> HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
+        .put(Rule.Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_CONTAINS, ADDRESS_NOT_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.EXACTLY_EQUALS, ADDRESS_EXACTLY_EQUALS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_EXACTLY_EQUALS, ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
+        .build();
+
+    Map<Rule.Condition.Comparator, ContentMatcher> CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
+        .put(Rule.Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_CONTAINS, STRING_NOT_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.EXACTLY_EQUALS, STRING_EXACTLY_EQUALS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_EXACTLY_EQUALS, STRING_NOT_EXACTLY_EQUALS_MATCHER)
+        .build();
+
+    Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>> CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>>builder()
+        .put(Rule.Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .build();
+
+    static ContentMatcher negate(ContentMatcher contentMatcher) {
+        return (Stream<String> contents, String valueToMatch) ->
+            !contentMatcher.match(contents, valueToMatch);
+    }
+
+    static Optional<ContentMatcher> asContentMatcher(Rule.Condition.Field field, Rule.Condition.Comparator comparator) {
+        return Optional
+            .ofNullable(CONTENT_MATCHER_REGISTRY.get(field))
+            .map(matcherRegistry -> matcherRegistry.get(comparator));
+    }
+
+    static AddressHeader asAddressHeader(String addressAsString) {
+        return new AddressHeader(addressAsString);
+    }
+
+    boolean match(Stream<String> contents, String valueToMatch);
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7527daec/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
new file mode 100644
index 0000000..65d1629
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
@@ -0,0 +1,93 @@
+/****************************************************************
+ * 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.mailet.filter;
+
+import static org.apache.mailet.base.RFC2822Headers.FROM;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.Address;
+import javax.mail.Message;
+
+import org.apache.james.javax.AddressHelper;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.mime4j.util.MimeUtil;
+import org.apache.james.util.StreamUtils;
+import org.apache.mailet.Mail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.functions.ThrowingFunction;
+import com.google.common.collect.ImmutableMap;
+
+public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {
+    Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class);
+
+    HeaderExtractor SUBJECT_EXTRACTOR = mail ->
+        StreamUtils.ofNullables(mail.getMessage().getSubject());
+    HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
+    HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
+    HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR);
+    HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> mail.getMessage().getFrom(), FROM);
+
+    Map<Rule.Condition.Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Rule.Condition.Field, HeaderExtractor>builder()
+        .put(Rule.Condition.Field.SUBJECT, SUBJECT_EXTRACTOR)
+        .put(Rule.Condition.Field.RECIPIENT, RECIPIENT_EXTRACTOR)
+        .put(Rule.Condition.Field.FROM, FROM_EXTRACTOR)
+        .put(Rule.Condition.Field.CC, CC_EXTRACTOR)
+        .put(Rule.Condition.Field.TO, TO_EXTRACTOR)
+        .build();
+
+    static HeaderExtractor and(HeaderExtractor headerExtractor1, HeaderExtractor headerExtractor2) {
+        return (Mail mail) -> StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail));
+    }
+
+    static HeaderExtractor recipientExtractor(Message.RecipientType type) {
+        ThrowingFunction<Mail, Address[]> addressGetter = mail -> mail.getMessage().getRecipients(type);
+        String fallbackHeaderName = type.toString();
+
+        return addressExtractor(addressGetter, fallbackHeaderName);
+    }
+
+    static HeaderExtractor addressExtractor(ThrowingFunction<Mail, Address[]> addressGetter, String fallbackHeaderName) {
+        return mail -> {
+            try {
+                return toContent(addressGetter.apply(mail));
+            } catch (Exception e) {
+                LOGGER.info("Failed parsing header. Falling back to unparsed header value matching", e);
+                return Stream.of(mail.getMessage().getHeader(fallbackHeaderName))
+                    .map(MimeUtil::unscrambleHeaderValue);
+            }
+        };
+    }
+
+    static Stream<String> toContent(Address[] addresses) {
+        return Optional.ofNullable(addresses)
+            .map(AddressHelper::asStringStream)
+            .orElse(Stream.empty());
+    }
+
+    static Optional<HeaderExtractor> asHeaderExtractor(Rule.Condition.Field field) {
+        return Optional.ofNullable(
+            HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7527daec/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index 031e696..ed5db25 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -20,169 +20,19 @@
 package org.apache.james.jmap.mailet.filter;
 
 import static org.apache.james.jmap.api.filtering.Rule.Condition;
-import static org.apache.mailet.base.RFC2822Headers.FROM;
 
-import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Stream;
 
-import javax.mail.Address;
-import javax.mail.Message;
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.james.javax.AddressHelper;
 import org.apache.james.jmap.api.filtering.Rule;
-import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
-import org.apache.james.mime4j.util.MimeUtil;
-import org.apache.james.util.StreamUtils;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.github.fge.lambdas.functions.ThrowingFunction;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
 
 public interface MailMatcher {
 
-    interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {
-        Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class);
-
-        HeaderExtractor SUBJECT_EXTRACTOR = mail ->
-            StreamUtils.ofNullables(mail.getMessage().getSubject());
-        HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
-        HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
-        HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR);
-        HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> mail.getMessage().getFrom(), FROM);
-
-        Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Field, HeaderExtractor>builder()
-            .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
-            .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
-            .put(Field.FROM, FROM_EXTRACTOR)
-            .put(Field.CC, CC_EXTRACTOR)
-            .put(Field.TO, TO_EXTRACTOR)
-            .build();
-
-        static HeaderExtractor and(HeaderExtractor headerExtractor1, HeaderExtractor headerExtractor2) {
-            return (Mail mail) -> StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail));
-        }
-
-        static HeaderExtractor recipientExtractor(Message.RecipientType type) {
-            ThrowingFunction<Mail, Address[]> addressGetter = mail -> mail.getMessage().getRecipients(type);
-            String fallbackHeaderName = type.toString();
-
-            return addressExtractor(addressGetter, fallbackHeaderName);
-        }
-
-        static HeaderExtractor addressExtractor(ThrowingFunction<Mail, Address[]> addressGetter, String fallbackHeaderName) {
-            return mail -> {
-                try {
-                    return toContent(addressGetter.apply(mail));
-                } catch (Exception e) {
-                    LOGGER.info("Failed parsing header. Falling back to unparsed header value matching", e);
-                    return Stream.of(mail.getMessage().getHeader(fallbackHeaderName))
-                        .map(MimeUtil::unscrambleHeaderValue);
-                }
-            };
-        }
-
-        static Stream<String> toContent(Address[] addresses) {
-            return Optional.ofNullable(addresses)
-                .map(AddressHelper::asStringStream)
-                .orElse(Stream.empty());
-        }
-
-        static Optional<HeaderExtractor> asHeaderExtractor(Field field) {
-            return Optional.ofNullable(
-                HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
-        }
-    }
-
-    interface ContentMatcher {
-
-        class AddressHeader {
-            private static final Logger LOGGER = LoggerFactory.getLogger(AddressHeader.class);
-
-            private final Optional<String> personal;
-            private final Optional<String> address;
-            private final String fullAddress;
-
-            private AddressHeader(String fullAddress) {
-                this.fullAddress = fullAddress;
-                Optional<InternetAddress> internetAddress = parseFullAddress();
-                this.personal = internetAddress.map(InternetAddress::getPersonal);
-                this.address = internetAddress.map(InternetAddress::getAddress);
-            }
-
-            private Optional<InternetAddress> parseFullAddress() {
-                try {
-                    return Optional.of(new InternetAddress(fullAddress));
-                } catch (AddressException e) {
-                    LOGGER.error("error while parsing full address {}", fullAddress, e);
-                    return Optional.empty();
-                }
-            }
-        }
-
-        ContentMatcher STRING_CONTAINS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.contains(content, valueToMatch));
-        ContentMatcher STRING_NOT_CONTAINS_MATCHER = negate(STRING_CONTAINS_MATCHER);
-        ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.equals(content, valueToMatch));
-        ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = negate(STRING_EXACTLY_EQUALS_MATCHER);
-
-        ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> contents
-            .map(ContentMatcher::asAddressHeader)
-            .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.fullAddress, valueToMatch));
-        ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = negate(ADDRESS_CONTAINS_MATCHER);
-        ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents
-            .map(ContentMatcher::asAddressHeader)
-            .anyMatch(addressHeader ->
-                valueToMatch.equalsIgnoreCase(addressHeader.fullAddress)
-                    || addressHeader.address.map(valueToMatch::equalsIgnoreCase).orElse(false)
-                    || addressHeader.personal.map(valueToMatch::equalsIgnoreCase).orElse(false));
-        ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
-
-        Map<Rule.Condition.Comparator, ContentMatcher> HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
-            .put(Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
-            .put(Condition.Comparator.NOT_CONTAINS, ADDRESS_NOT_CONTAINS_MATCHER)
-            .put(Condition.Comparator.EXACTLY_EQUALS, ADDRESS_EXACTLY_EQUALS_MATCHER)
-            .put(Condition.Comparator.NOT_EXACTLY_EQUALS, ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
-            .build();
-
-        Map<Rule.Condition.Comparator, ContentMatcher> CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
-            .put(Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
-            .put(Condition.Comparator.NOT_CONTAINS, STRING_NOT_CONTAINS_MATCHER)
-            .put(Condition.Comparator.EXACTLY_EQUALS, STRING_EXACTLY_EQUALS_MATCHER)
-            .put(Condition.Comparator.NOT_EXACTLY_EQUALS, STRING_NOT_EXACTLY_EQUALS_MATCHER)
-            .build();
-
-        Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>> CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>>builder()
-            .put(Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
-            .put(Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .put(Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .put(Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .put(Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .build();
-
-        static ContentMatcher negate(ContentMatcher contentMatcher) {
-            return (Stream<String> contents, String valueToMatch) ->
-                !contentMatcher.match(contents, valueToMatch);
-        }
-
-        static Optional<ContentMatcher> asContentMatcher(Condition.Field field, Condition.Comparator comparator) {
-            return Optional
-                .ofNullable(CONTENT_MATCHER_REGISTRY.get(field))
-                .map(matcherRegistry -> matcherRegistry.get(comparator));
-        }
-
-        static AddressHeader asAddressHeader(String addressAsString) {
-            return new AddressHeader(addressAsString);
-        }
-
-        boolean match(Stream<String> contents, String valueToMatch);
-    }
-
     class HeaderMatcher implements MailMatcher {
 
         private static final Logger LOGGER = LoggerFactory.getLogger(HeaderMatcher.class);


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


[02/26] james-project git commit: JAMES-2529 Remove final varaiables

Posted by ad...@apache.org.
JAMES-2529 Remove final varaiables


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

Branch: refs/heads/master
Commit: e06fb4ac82423a0eed9aa31fb3805a5a85d1fb5b
Parents: 59b0e8b
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 10:09:37 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:01 2018 +0200

----------------------------------------------------------------------
 .../main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/e06fb4ac/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index d762120..aea0b75 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -199,7 +199,7 @@ public interface MailMatcher {
         @Override
         public boolean match(Mail mail) {
             try {
-                final Stream<String> headerLines = headerExtractor.apply(mail);
+                Stream<String> headerLines = headerExtractor.apply(mail);
                 return contentMatcher.match(headerLines, ruleValue);
             } catch (Exception e) {
                 LOGGER.error("error while extracting mail header", e);


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


[26/26] james-project git commit: JAMES-2074 removing webadmin.properties in webadmin intergation test to avoid confusing

Posted by ad...@apache.org.
JAMES-2074 removing webadmin.properties in webadmin intergation test to avoid confusing


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

Branch: refs/heads/master
Commit: 072feaca684f640b86ddd59cc5c9cdd8ff1af2a8
Parents: 443f3c6
Author: duc <dt...@linagora.com>
Authored: Thu Aug 30 16:29:51 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:15:34 2018 +0200

----------------------------------------------------------------------
 .../src/test/resources/webadmin.properties                        | 3 ---
 1 file changed, 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/072feaca/server/protocols/webadmin-integration-test/src/test/resources/webadmin.properties
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin-integration-test/src/test/resources/webadmin.properties b/server/protocols/webadmin-integration-test/src/test/resources/webadmin.properties
deleted file mode 100644
index 475d655..0000000
--- a/server/protocols/webadmin-integration-test/src/test/resources/webadmin.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-enabled=true
-port=8000
-host=localhost
\ No newline at end of file


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


[06/26] james-project git commit: JAMES-2529 Encapsulate MailMatcher code in relevant sub-classes

Posted by ad...@apache.org.
JAMES-2529 Encapsulate MailMatcher code in relevant sub-classes

This makes reading way easier as in an IDE one can fold complete sub-classes


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

Branch: refs/heads/master
Commit: 9a2e06d1ace6a54a084ab880ff50416a4dbe2eeb
Parents: 247a851
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 09:36:25 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:01 2018 +0200

----------------------------------------------------------------------
 .../james/jmap/mailet/filter/MailMatcher.java   | 118 +++++++++----------
 1 file changed, 58 insertions(+), 60 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9a2e06d1/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index d9efaf7..d762120 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -48,36 +48,38 @@ import com.google.common.collect.ImmutableMap;
 public interface MailMatcher {
 
     interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {
+        HeaderExtractor SUBJECT_EXTRACTOR = mail ->
+            OptionalUtils.ofNullableToStream(mail.getMessage().getSubject());
+        HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
+            mail.getMessage().getRecipients(Message.RecipientType.TO),
+            mail.getMessage().getRecipients(Message.RecipientType.CC));
+        HeaderExtractor FROM_EXTRACTOR = mail -> addressExtractor(mail.getMessage().getFrom());
+        HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
+        HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
+
+        Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Field, HeaderExtractor>builder()
+            .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
+            .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
+            .put(Field.FROM, FROM_EXTRACTOR)
+            .put(Field.CC, CC_EXTRACTOR)
+            .put(Field.TO, TO_EXTRACTOR)
+            .build();
 
-    }
-
-    class HeaderMatcher implements MailMatcher {
-
-        private static final Logger LOGGER = LoggerFactory.getLogger(HeaderMatcher.class);
-
-        private final ContentMatcher contentMatcher;
-        private final String ruleValue;
-        private final HeaderExtractor headerExtractor;
-
-        private HeaderMatcher(ContentMatcher contentMatcher, String ruleValue,
-                              HeaderExtractor headerExtractor) {
-            Preconditions.checkNotNull(contentMatcher);
-            Preconditions.checkNotNull(headerExtractor);
+        static HeaderExtractor recipientExtractor(Message.RecipientType type) {
+            return mail -> addressExtractor(mail.getMessage().getRecipients(type));
+        }
 
-            this.contentMatcher = contentMatcher;
-            this.ruleValue = ruleValue;
-            this.headerExtractor = headerExtractor;
+        static Stream<String> addressExtractor(Address[]... addresses) {
+            return Optional.ofNullable(addresses)
+                .map(Arrays::stream)
+                .orElse(Stream.empty())
+                .filter(Objects::nonNull)
+                .flatMap(AddressHelper::asStringStream);
         }
 
-        @Override
-        public boolean match(Mail mail) {
-            try {
-                final Stream<String> headerLines = headerExtractor.apply(mail);
-                return contentMatcher.match(headerLines, ruleValue);
-            } catch (Exception e) {
-                LOGGER.error("error while extracting mail header", e);
-                return false;
-            }
+        static Optional<HeaderExtractor> asHeaderExtractor(Field field) {
+            return Optional
+                .ofNullable(HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
         }
     }
 
@@ -176,27 +178,40 @@ public interface MailMatcher {
         boolean match(Stream<String> contents, String valueToMatch);
     }
 
-    HeaderExtractor SUBJECT_EXTRACTOR = mail ->
-        OptionalUtils.ofNullableToStream(mail.getMessage().getSubject());
-    HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
-        mail.getMessage().getRecipients(Message.RecipientType.TO),
-        mail.getMessage().getRecipients(Message.RecipientType.CC));
-    HeaderExtractor FROM_EXTRACTOR = mail -> addressExtractor(mail.getMessage().getFrom());
-    HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
-    HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
-
-    Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Field, HeaderExtractor>builder()
-        .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
-        .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
-        .put(Field.FROM, FROM_EXTRACTOR)
-        .put(Field.CC, CC_EXTRACTOR)
-        .put(Field.TO, TO_EXTRACTOR)
-        .build();
+    class HeaderMatcher implements MailMatcher {
+
+        private static final Logger LOGGER = LoggerFactory.getLogger(HeaderMatcher.class);
+
+        private final ContentMatcher contentMatcher;
+        private final String ruleValue;
+        private final HeaderExtractor headerExtractor;
+
+        private HeaderMatcher(ContentMatcher contentMatcher, String ruleValue,
+                              HeaderExtractor headerExtractor) {
+            Preconditions.checkNotNull(contentMatcher);
+            Preconditions.checkNotNull(headerExtractor);
+
+            this.contentMatcher = contentMatcher;
+            this.ruleValue = ruleValue;
+            this.headerExtractor = headerExtractor;
+        }
+
+        @Override
+        public boolean match(Mail mail) {
+            try {
+                final Stream<String> headerLines = headerExtractor.apply(mail);
+                return contentMatcher.match(headerLines, ruleValue);
+            } catch (Exception e) {
+                LOGGER.error("error while extracting mail header", e);
+                return false;
+            }
+        }
+    }
 
     static MailMatcher from(Rule rule) {
         Condition ruleCondition = rule.getCondition();
         Optional<ContentMatcher> maybeContentMatcher = ContentMatcher.asContentMatcher(ruleCondition.getField(), ruleCondition.getComparator());
-        Optional<HeaderExtractor> maybeHeaderExtractor = getHeaderExtractor(ruleCondition.getField());
+        Optional<HeaderExtractor> maybeHeaderExtractor = HeaderExtractor.asHeaderExtractor(ruleCondition.getField());
 
         return new HeaderMatcher(
             maybeContentMatcher.orElseThrow(() -> new RuntimeException("No content matcher associated with field " + ruleCondition.getField())),
@@ -204,22 +219,5 @@ public interface MailMatcher {
             maybeHeaderExtractor.orElseThrow(() -> new RuntimeException("No content matcher associated with comparator " + ruleCondition.getComparator())));
     }
 
-    static HeaderExtractor recipientExtractor(Message.RecipientType type) {
-        return mail -> addressExtractor(mail.getMessage().getRecipients(type));
-    }
-
-    static Stream<String> addressExtractor(Address[]... addresses) {
-        return Optional.ofNullable(addresses)
-            .map(Arrays::stream)
-            .orElse(Stream.empty())
-            .filter(Objects::nonNull)
-            .flatMap(AddressHelper::asStringStream);
-    }
-
-    static Optional<HeaderExtractor> getHeaderExtractor(Field field) {
-        return Optional
-            .ofNullable(HEADER_EXTRACTOR_REGISTRY.get(field));
-    }
-
     boolean match(Mail mail);
 }


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


[09/26] james-project git commit: JAMES-2529 Falback on address parsing failure

Posted by ad...@apache.org.
JAMES-2529 Falback on address parsing failure

 - (not) exactly-equals default to full header matching
 - (not) contains stays fully functional


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

Branch: refs/heads/master
Commit: 97ba6503c9551d449e6ae3732a61d8b6706d5589
Parents: ee14044
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 11:20:33 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:02 2018 +0200

----------------------------------------------------------------------
 .../james/jmap/mailet/filter/MailMatcher.java   |  46 +++--
 .../jmap/mailet/filter/JMAPFilteringTest.java   | 201 +++++++++++++------
 2 files changed, 167 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/97ba6503/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index ab7e05d..a7ac517 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -20,10 +20,9 @@
 package org.apache.james.jmap.mailet.filter;
 
 import static org.apache.james.jmap.api.filtering.Rule.Condition;
+import static org.apache.mailet.base.RFC2822Headers.FROM;
 
-import java.util.Arrays;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Stream;
 
@@ -36,7 +35,9 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.james.javax.AddressHelper;
 import org.apache.james.jmap.api.filtering.Rule;
 import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
+import org.apache.james.mime4j.util.MimeUtil;
 import org.apache.james.util.OptionalUtils;
+import org.apache.james.util.StreamUtils;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,14 +49,14 @@ import com.google.common.collect.ImmutableMap;
 public interface MailMatcher {
 
     interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {
+        Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class);
+
         HeaderExtractor SUBJECT_EXTRACTOR = mail ->
             OptionalUtils.ofNullableToStream(mail.getMessage().getSubject());
-        HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
-            mail.getMessage().getRecipients(Message.RecipientType.TO),
-            mail.getMessage().getRecipients(Message.RecipientType.CC));
-        HeaderExtractor FROM_EXTRACTOR = mail -> addressExtractor(mail.getMessage().getFrom());
         HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
         HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
+        HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR);
+        HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> mail.getMessage().getFrom(), FROM);
 
         Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Field, HeaderExtractor>builder()
             .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
@@ -65,21 +66,38 @@ public interface MailMatcher {
             .put(Field.TO, TO_EXTRACTOR)
             .build();
 
+        static HeaderExtractor and(HeaderExtractor headerExtractor1, HeaderExtractor headerExtractor2) {
+            return (Mail mail) -> StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail));
+        }
+
         static HeaderExtractor recipientExtractor(Message.RecipientType type) {
-            return mail -> addressExtractor(mail.getMessage().getRecipients(type));
+            ThrowingFunction<Mail, Address[]> addressGetter = mail -> mail.getMessage().getRecipients(type);
+            String fallbackHeaderName = type.toString();
+
+            return addressExtractor(addressGetter, fallbackHeaderName);
         }
 
-        static Stream<String> addressExtractor(Address[]... addresses) {
+        static HeaderExtractor addressExtractor(ThrowingFunction<Mail, Address[]> addressGetter, String fallbackHeaderName) {
+            return mail -> {
+                try {
+                    return toContent(addressGetter.apply(mail));
+                } catch (Exception e) {
+                    LOGGER.info("Failed parsing header. Falling back to unparsed header value matching", e);
+                    return Stream.of(mail.getMessage().getHeader(fallbackHeaderName))
+                        .map(MimeUtil::unscrambleHeaderValue);
+                }
+            };
+        }
+
+        static Stream<String> toContent(Address[] addresses) {
             return Optional.ofNullable(addresses)
-                .map(Arrays::stream)
-                .orElse(Stream.empty())
-                .filter(Objects::nonNull)
-                .flatMap(AddressHelper::asStringStream);
+                .map(AddressHelper::asStringStream)
+                .orElse(Stream.empty());
         }
 
         static Optional<HeaderExtractor> asHeaderExtractor(Field field) {
-            return Optional
-                .ofNullable(HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
+            return Optional.ofNullable(
+                HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/97ba6503/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
index 57bec4a..15ba523 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
@@ -36,7 +36,6 @@ import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.EMPTY;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULLNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.FRED_MARTIN_FULL_SCRAMBLED_ADDRESS;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.GA_BOU_ZO_MEU_FULL_ADDRESS;
-import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_MAILBOX_1;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.RECIPIENT_1_USERNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.SCRAMBLED_SUBJECT;
@@ -250,45 +249,67 @@ class JMAPFilteringTest {
                     argumentBuilder(headerField)
                         .description("folded content (different case)")
                         .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch(UNFOLDED_USERNAME.toUpperCase())
+                        .valueToMatch(UNFOLDED_USERNAME.toUpperCase()),
+                    argumentBuilder(headerField)
+                        .description("invalid address, personal match")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch("Benoit"),
+                    argumentBuilder(headerField)
+                        .description("invalid address, address match")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch("invalid"),
+                    argumentBuilder(headerField)
+                        .description("invalid address, full match")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch("Benoit <invalid>"),
+                    argumentBuilder(headerField)
+                        .description("invalid header, full match")
+                        .headerForField("Benoit <invalid")
+                        .valueToMatch("Benoit <invalid")
                     ).map(FilteringArgumentBuilder::build)),
             Stream.of(TO_HEADER, CC_HEADER)
                 .flatMap(headerName -> Stream.of(
-                    argumentBuilder()
+                    argumentBuilder(RECIPIENT)
                         .description("full address " + headerName + " header")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_FULL_ADDRESS)
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch(USER_3_FULL_ADDRESS),
+                    argumentBuilder(RECIPIENT)
                         .description("full address " + headerName + " header (different case)")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_FULL_ADDRESS.toUpperCase(Locale.ENGLISH))
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch(USER_3_FULL_ADDRESS.toUpperCase(Locale.ENGLISH)),
+                    argumentBuilder(RECIPIENT)
                         .description("address only " + headerName + " header")
-                        .field(RECIPIENT).header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_ADDRESS)
-                        .build(),
-                    argumentBuilder()
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(USER_3_ADDRESS),
+                    argumentBuilder(RECIPIENT)
                         .description("personal only " + headerName + " header")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(USER_3_USERNAME)
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch(USER_3_USERNAME),
+                    argumentBuilder(RECIPIENT)
                         .description("scrambled content in " + headerName + " header")
-                        .field(RECIPIENT)
                         .header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
-                        .valueToMatch(FRED_MARTIN_FULLNAME)
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch(FRED_MARTIN_FULLNAME),
+                    argumentBuilder(RECIPIENT)
                         .description("folded content in " + headerName + " header")
-                        .field(RECIPIENT)
                         .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch(UNFOLDED_USERNAME)
-                        .build())),
+                        .valueToMatch(UNFOLDED_USERNAME),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid " + headerName + " address, personal match")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch("Benoit"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid " + headerName + " address, address match")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch("invalid"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid " + headerName + " address, full match")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch("Benoit <invalid>"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid " + headerName + ", full match")
+                        .header(headerName, "Benoit <invalid")
+                        .valueToMatch("Benoit <invalid"))
+                    .map(FilteringArgumentBuilder::build)),
             Stream.of(
                 argumentBuilder().description("multiple to and cc headers").field(RECIPIENT)
                     .ccRecipient(USER_1_FULL_ADDRESS)
@@ -347,46 +368,83 @@ class JMAPFilteringTest {
                     argumentBuilder(headerField)
                         .description("folded content (partial matching)")
                         .headerForField(USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch("ded_us"))
+                        .valueToMatch("ded_us"),
+                    argumentBuilder(headerField)
+                        .description("invalid address, personal match (partial matching)")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch("enoi"),
+                    argumentBuilder(headerField)
+                        .description("invalid address, address match (partial matching)")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch("nvali"),
+                    argumentBuilder(headerField)
+                        .description("invalid address, full match (partial matching)")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch("enoit <invali"),
+                    argumentBuilder(headerField)
+                        .description("invalid header, full match (partial matching)")
+                        .headerForField("Benoit <invalid")
+                        .valueToMatch("enoit <invali"),
+                    argumentBuilder(headerField)
+                        .description("invalid header, personal match (partial matching)")
+                        .headerForField("Benoit <invalid")
+                        .valueToMatch("enoi"),
+                    argumentBuilder(headerField)
+                        .description("invalid header, address match (partial matching)")
+                        .headerForField("Benoit <invalid")
+                        .valueToMatch("nvali"))
                     .map(FilteringArgumentBuilder::build)),
             Stream.of(TO_HEADER, CC_HEADER)
                 .flatMap(headerName -> Stream.of(
-                    argumentBuilder()
+                    argumentBuilder(RECIPIENT)
                         .description("full address " + headerName + " header (partial matching)")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("ser3 <us")
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch("ser3 <us"),
+                    argumentBuilder(RECIPIENT)
                         .description("full address " + headerName + " header (partial matching, different case)")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("SER3 <US")
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch("SER3 <US"),
+                    argumentBuilder(RECIPIENT)
                         .description("address only " + headerName + " header (partial matching)")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("ser3@jam")
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch("ser3@jam"),
+                    argumentBuilder(RECIPIENT)
                         .description("personal only " + headerName + " header (partial matching)")
-                        .field(RECIPIENT)
                         .header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch("ser3")
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch("ser3"),
+                    argumentBuilder(RECIPIENT)
                         .description("scrambled content in " + headerName + " header (partial matching)")
-                        .field(RECIPIENT)
                         .header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
-                        .valueToMatch("déric MAR")
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch("déric MAR"),
+                    argumentBuilder(RECIPIENT)
                         .description("folded content in " + headerName + " header (partial matching)")
-                        .field(RECIPIENT)
                         .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch("folded_us")
-                        .build())),
+                        .valueToMatch("folded_us"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid address, personal match (partial matching)")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch("enoi"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid address, address match (partial matching)")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch("nvali"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid address, full match (partial matching)")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch("enoit <invali"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid header, full match (partial matching)")
+                        .header(headerName, "Benoit <invalid")
+                        .valueToMatch("enoit <invali"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid header, personal match (partial matching)")
+                        .header(headerName, "Benoit <invalid")
+                        .valueToMatch("enoi"),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid header, address match (partial matching)")
+                        .header(headerName, "Benoit <invalid")
+                        .valueToMatch("nvali"))
+                    .map(FilteringArgumentBuilder::build)),
             Stream.of(
                 argumentBuilder().description("multiple to and cc headers (partial matching)").field(RECIPIENT)
                     .ccRecipient(USER_1_FULL_ADDRESS)
@@ -428,32 +486,43 @@ class JMAPFilteringTest {
                     argumentBuilder(headerField)
                         .description("empty content")
                         .headerForField(EMPTY)
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(headerField)
+                        .description("invalid address, personal match")
+                        .headerForField("Benoit <invalid>")
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(headerField)
+                        .description("invalid header, full match")
+                        .headerForField("Benoit <invalid")
                         .valueToMatch(SHOULD_NOT_MATCH))
                     .map(FilteringArgumentBuilder::build)),
             Stream.of(TO_HEADER, CC_HEADER)
                 .flatMap(headerName -> Stream.of(
-                    argumentBuilder()
+                    argumentBuilder(RECIPIENT)
                         .description("normal content " + headerName + " header")
-                        .field(RECIPIENT).header(headerName, USER_3_FULL_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH)
-                        .build(),
-                    argumentBuilder()
+                        .header(headerName, USER_3_FULL_ADDRESS)
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(RECIPIENT)
                         .description("scrambled content in " + headerName + " header")
                         .field(RECIPIENT).header(headerName, FRED_MARTIN_FULL_SCRAMBLED_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH)
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(RECIPIENT)
                         .description("folded content in " + headerName + " header")
-                        .field(RECIPIENT)
                         .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH)
-                        .build(),
-                    argumentBuilder()
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(RECIPIENT)
                         .description("bcc header")
-                        .field(RECIPIENT)
                         .header(headerName, USER_1_AND_UNFOLDED_USER_FULL_ADDRESS)
-                        .valueToMatch(SHOULD_NOT_MATCH)
-                        .build())),
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid address, personal match")
+                        .header(headerName, "Benoit <invalid>")
+                        .valueToMatch(SHOULD_NOT_MATCH),
+                    argumentBuilder(RECIPIENT)
+                        .description("invalid header, full match")
+                        .header(headerName, "Benoit <invalid")
+                        .valueToMatch(SHOULD_NOT_MATCH))
+                    .map(FilteringArgumentBuilder::build)),
             Stream.of(
                 argumentBuilder().description("multiple to and cc headers").field(RECIPIENT)
                     .ccRecipient(USER_1_FULL_ADDRESS)


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


[07/26] james-project git commit: JAMES-2529 Re-indent MailMatcher with 4 indent levels

Posted by ad...@apache.org.
JAMES-2529 Re-indent MailMatcher with 4 indent levels


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

Branch: refs/heads/master
Commit: 247a8516fbec4b129df41a075c97ee9217fbef8e
Parents: 924d23d
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 09:30:15 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:01 2018 +0200

----------------------------------------------------------------------
 .../james/jmap/mailet/filter/MailMatcher.java   | 77 ++++++++++----------
 1 file changed, 38 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/247a8516/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index 185c08b..d9efaf7 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -47,7 +47,9 @@ import com.google.common.collect.ImmutableMap;
 
 public interface MailMatcher {
 
-    interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {}
+    interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {
+
+    }
 
     class HeaderMatcher implements MailMatcher {
 
@@ -58,7 +60,7 @@ public interface MailMatcher {
         private final HeaderExtractor headerExtractor;
 
         private HeaderMatcher(ContentMatcher contentMatcher, String ruleValue,
-                      HeaderExtractor headerExtractor) {
+                              HeaderExtractor headerExtractor) {
             Preconditions.checkNotNull(contentMatcher);
             Preconditions.checkNotNull(headerExtractor);
 
@@ -122,45 +124,43 @@ public interface MailMatcher {
         ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.equals(content, valueToMatch));
         ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = negate(STRING_EXACTLY_EQUALS_MATCHER);
 
-        Map<Rule.Condition.Comparator, ContentMatcher> CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
-                .put(Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
-                .put(Condition.Comparator.NOT_CONTAINS, STRING_NOT_CONTAINS_MATCHER)
-                .put(Condition.Comparator.EXACTLY_EQUALS, STRING_EXACTLY_EQUALS_MATCHER)
-                .put(Condition.Comparator.NOT_EXACTLY_EQUALS, STRING_NOT_EXACTLY_EQUALS_MATCHER)
-                .build();
-
         ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> contents
-                .map(ContentMatcher::asAddressHeader)
-                .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.getFullAddress(), valueToMatch));
-
+            .map(ContentMatcher::asAddressHeader)
+            .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.getFullAddress(), valueToMatch));
         ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = negate(ADDRESS_CONTAINS_MATCHER);
         ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents
-                .map(ContentMatcher::asAddressHeader)
-                .anyMatch(addressHeader ->
-                        StringUtils.equalsIgnoreCase(addressHeader.getFullAddress(), valueToMatch)
-                        || StringUtils.equalsIgnoreCase(addressHeader.getAddress().orElse(null), valueToMatch)
-                        || StringUtils.equalsIgnoreCase(addressHeader.getPersonal().orElse(null), valueToMatch));
-
+            .map(ContentMatcher::asAddressHeader)
+            .anyMatch(addressHeader ->
+                StringUtils.equalsIgnoreCase(addressHeader.getFullAddress(), valueToMatch)
+                    || StringUtils.equalsIgnoreCase(addressHeader.getAddress().orElse(null), valueToMatch)
+                    || StringUtils.equalsIgnoreCase(addressHeader.getPersonal().orElse(null), valueToMatch));
         ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
 
         Map<Rule.Condition.Comparator, ContentMatcher> HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
-                .put(Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
-                .put(Condition.Comparator.NOT_CONTAINS, ADDRESS_NOT_CONTAINS_MATCHER)
-                .put(Condition.Comparator.EXACTLY_EQUALS, ADDRESS_EXACTLY_EQUALS_MATCHER)
-                .put(Condition.Comparator.NOT_EXACTLY_EQUALS, ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
-                .build();
+            .put(Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
+            .put(Condition.Comparator.NOT_CONTAINS, ADDRESS_NOT_CONTAINS_MATCHER)
+            .put(Condition.Comparator.EXACTLY_EQUALS, ADDRESS_EXACTLY_EQUALS_MATCHER)
+            .put(Condition.Comparator.NOT_EXACTLY_EQUALS, ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
+            .build();
+
+        Map<Rule.Condition.Comparator, ContentMatcher> CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()
+            .put(Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
+            .put(Condition.Comparator.NOT_CONTAINS, STRING_NOT_CONTAINS_MATCHER)
+            .put(Condition.Comparator.EXACTLY_EQUALS, STRING_EXACTLY_EQUALS_MATCHER)
+            .put(Condition.Comparator.NOT_EXACTLY_EQUALS, STRING_NOT_EXACTLY_EQUALS_MATCHER)
+            .build();
 
         Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>> CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>>builder()
-                .put(Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
-                .put(Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
-                .put(Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
-                .put(Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
-                .put(Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
-                .build();
+            .put(Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
+            .put(Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
+            .put(Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
+            .put(Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
+            .put(Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
+            .build();
 
         static ContentMatcher negate(ContentMatcher contentMatcher) {
             return (Stream<String> contents, String valueToMatch) ->
-                    !contentMatcher.match(contents, valueToMatch);
+                !contentMatcher.match(contents, valueToMatch);
         }
 
         static Optional<ContentMatcher> asContentMatcher(Condition.Field field, Condition.Comparator comparator) {
@@ -179,20 +179,19 @@ public interface MailMatcher {
     HeaderExtractor SUBJECT_EXTRACTOR = mail ->
         OptionalUtils.ofNullableToStream(mail.getMessage().getSubject());
     HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
-                mail.getMessage().getRecipients(Message.RecipientType.TO),
-                mail.getMessage().getRecipients(Message.RecipientType.CC));
-
+        mail.getMessage().getRecipients(Message.RecipientType.TO),
+        mail.getMessage().getRecipients(Message.RecipientType.CC));
     HeaderExtractor FROM_EXTRACTOR = mail -> addressExtractor(mail.getMessage().getFrom());
     HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
     HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
 
     Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Field, HeaderExtractor>builder()
-            .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
-            .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
-            .put(Field.FROM, FROM_EXTRACTOR)
-            .put(Field.CC, CC_EXTRACTOR)
-            .put(Field.TO, TO_EXTRACTOR)
-            .build();
+        .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
+        .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
+        .put(Field.FROM, FROM_EXTRACTOR)
+        .put(Field.CC, CC_EXTRACTOR)
+        .put(Field.TO, TO_EXTRACTOR)
+        .build();
 
     static MailMatcher from(Rule rule) {
         Condition ruleCondition = rule.getCondition();


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


[05/26] james-project git commit: JAMES-2529 Apply basic refactoring to multi-rule testing

Posted by ad...@apache.org.
JAMES-2529 Apply basic refactoring to multi-rule testing


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

Branch: refs/heads/master
Commit: 59b0e8b0e086dce4ce4999dd9593c22599c4ebd8
Parents: 9a2e06d
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 09:38:05 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:01 2018 +0200

----------------------------------------------------------------------
 .../jmap/mailet/filter/JMAPFilteringTest.java   | 91 ++++++++------------
 1 file changed, 37 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/59b0e8b0/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
index 0d697f8..57bec4a 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
@@ -202,7 +202,7 @@ class JMAPFilteringTest {
         return StreamUtils.flatten(
             Stream.of(FROM, TO, CC)
                 .flatMap(headerField -> Stream.of(
-                        argumentBuilder(headerField)
+                    argumentBuilder(headerField)
                         .description("full address value")
                         .headerForField(USER_1_FULL_ADDRESS)
                         .valueToMatch(USER_1_USERNAME),
@@ -614,36 +614,30 @@ class JMAPFilteringTest {
             MailboxId mailbox2Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
             MailboxId mailbox3Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
 
-            testSystem.defineRulesForRecipient1();
             testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
-                ImmutableList.of(
-                    Rule.builder()
-                        .id(Rule.Id.of("1"))
-                        .name("rule 1")
-                        .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
-                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox1Id.serialize())))
-                        .build(),
-                    Rule.builder()
-                        .id(Rule.Id.of("2"))
-                        .name("rule 2")
-                        .condition(Rule.Condition.of(FROM, NOT_CONTAINS, USER_1_USERNAME))
-                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox2Id.serialize())))
-                        .build(),
-                    Rule.builder()
-                        .id(Rule.Id.of("3"))
-                        .name("rule 3")
-                        .condition(Rule.Condition.of(TO, EXACTLY_EQUALS, USER_3_ADDRESS))
-                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox3Id.serialize())))
-                        .build()));
-
-            FakeMail mail = FakeMail.builder()
-                .sender(USER_1_ADDRESS)
-                .recipients(RECIPIENT_1)
-                .mimeMessage(mimeMessageBuilder()
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox1Id.serialize())))
+                    .build(),
+                Rule.builder()
+                    .id(Rule.Id.of("2"))
+                    .name("rule 2")
+                    .condition(Rule.Condition.of(FROM, NOT_CONTAINS, USER_1_USERNAME))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox2Id.serialize())))
+                    .build(),
+                Rule.builder()
+                    .id(Rule.Id.of("3"))
+                    .name("rule 3")
+                    .condition(Rule.Condition.of(TO, EXACTLY_EQUALS, USER_3_ADDRESS))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(mailbox3Id.serialize())))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
                     .addFrom(USER_2_ADDRESS)
                     .addToRecipient(USER_3_ADDRESS)
-                    .setSubject(UNSCRAMBLED_SUBJECT))
-                .build();
+                    .setSubject(UNSCRAMBLED_SUBJECT));
 
             testSystem.getJmapFiltering().service(mail);
 
@@ -659,23 +653,18 @@ class JMAPFilteringTest {
             MailboxId mailbox3Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
 
             testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
-                ImmutableList.of(
-                    Rule.builder()
-                        .id(Rule.Id.of("1"))
-                        .name("rule 1")
-                        .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
-                        .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(
-                            mailbox3Id.serialize(),
-                            mailbox2Id.serialize(),
-                            mailbox1Id.serialize()))))
-                        .build()));
-
-            FakeMail mail = FakeMail.builder()
-                .sender(USER_1_ADDRESS)
-                .recipients(RECIPIENT_1)
-                .mimeMessage(mimeMessageBuilder()
-                    .setSubject(UNSCRAMBLED_SUBJECT))
-                .build();
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(
+                        mailbox3Id.serialize(),
+                        mailbox2Id.serialize(),
+                        mailbox1Id.serialize()))))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
+                    .setSubject(UNSCRAMBLED_SUBJECT));
 
             testSystem.getJmapFiltering().service(mail);
 
@@ -695,8 +684,7 @@ class JMAPFilteringTest {
                 Rule.Condition.of(RECIPIENT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
                 Rule.Condition.of(RECIPIENT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS),
                 Rule.Condition.of(SUBJECT, CONTAINS, USER_1_FULL_ADDRESS),
-                Rule.Condition.of(SUBJECT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS)
-            );
+                Rule.Condition.of(SUBJECT, EXACTLY_EQUALS, USER_1_FULL_ADDRESS));
 
             FakeMail mail = testSystem.asMail(mimeMessageBuilder());
             testSystem.getJmapFiltering().service(mail);
@@ -707,20 +695,15 @@ class JMAPFilteringTest {
 
         @Test
         void mailDirectiveShouldNotBeSetWhenNoneRulesValueIsContained(JMAPFilteringTestSystem testSystem) throws Exception {
-
             testSystem.defineRulesForRecipient1(
                 Rule.Condition.of(FROM, CONTAINS, SHOULD_NOT_MATCH),
                 Rule.Condition.of(TO, CONTAINS, SHOULD_NOT_MATCH),
                 Rule.Condition.of(CC, CONTAINS, SHOULD_NOT_MATCH));
 
-            FakeMail mail = FakeMail.builder()
-                .sender(USER_1_ADDRESS)
-                .recipients(RECIPIENT_1)
-                .mimeMessage(mimeMessageBuilder()
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
                     .addFrom(USER_1_FULL_ADDRESS)
                     .addToRecipient(USER_2_FULL_ADDRESS)
-                    .addCcRecipient(USER_3_FULL_ADDRESS))
-                .build();
+                    .addCcRecipient(USER_3_FULL_ADDRESS));
 
             testSystem.getJmapFiltering().service(mail);
 


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


[10/26] james-project git commit: JAMES-2529 Remove getter for AddressHeader

Posted by ad...@apache.org.
JAMES-2529 Remove getter for AddressHeader

The code is only used by the MailMatcher class thus this boiler plate code is not required.


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

Branch: refs/heads/master
Commit: ee14044c842916b0f36e1d09b2199cb971a936ce
Parents: b62bc55
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 10:23:48 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:02 2018 +0200

----------------------------------------------------------------------
 .../james/jmap/mailet/filter/MailMatcher.java   | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ee14044c/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index 410146e..ab7e05d 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -107,18 +107,6 @@ public interface MailMatcher {
                     return Optional.empty();
                 }
             }
-
-            public Optional<String> getPersonal() {
-                return personal;
-            }
-
-            public Optional<String> getAddress() {
-                return address;
-            }
-
-            public String getFullAddress() {
-                return fullAddress;
-            }
         }
 
         ContentMatcher STRING_CONTAINS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.contains(content, valueToMatch));
@@ -128,14 +116,14 @@ public interface MailMatcher {
 
         ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> contents
             .map(ContentMatcher::asAddressHeader)
-            .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.getFullAddress(), valueToMatch));
+            .anyMatch(addressHeader -> StringUtils.containsIgnoreCase(addressHeader.fullAddress, valueToMatch));
         ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = negate(ADDRESS_CONTAINS_MATCHER);
         ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents
             .map(ContentMatcher::asAddressHeader)
             .anyMatch(addressHeader ->
-                valueToMatch.equalsIgnoreCase(addressHeader.getFullAddress())
-                    || addressHeader.getAddress().map(valueToMatch::equalsIgnoreCase).orElse(false)
-                    || addressHeader.getPersonal().map(valueToMatch::equalsIgnoreCase).orElse(false));
+                valueToMatch.equalsIgnoreCase(addressHeader.fullAddress)
+                    || addressHeader.address.map(valueToMatch::equalsIgnoreCase).orElse(false)
+                    || addressHeader.personal.map(valueToMatch::equalsIgnoreCase).orElse(false));
         ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
 
         Map<Rule.Condition.Comparator, ContentMatcher> HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, ContentMatcher>builder()


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


[08/26] james-project git commit: JAMES-2529 ActionApplier should handle MailboxNotFound

Posted by ad...@apache.org.
JAMES-2529 ActionApplier should handle MailboxNotFound

This case can happen frequently. Example:

 - Bob create the 'toto' mailbox
 - Bob set up a rule to redirect mails from Fred to the 'toto' mailbox
 - Bob deletes the 'toto' mailbox
 - Fred sends a mail to Bob

Note that this is implemented by applying all actions (and thus relaxing the 'first applied' tests)


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

Branch: refs/heads/master
Commit: 8b1c70aba0032173c21ed377820eba4932c811fb
Parents: 97ba650
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 11:33:40 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:02 2018 +0200

----------------------------------------------------------------------
 .../james/jmap/mailet/filter/ActionApplier.java |  33 +++--
 .../james/jmap/mailet/filter/JMAPFiltering.java |   9 +-
 .../james/jmap/mailet/filter/RuleMatcher.java   |   7 +-
 .../mailet/filter/JMAPFilteringExtension.java   |   4 +-
 .../jmap/mailet/filter/JMAPFilteringTest.java   | 143 +++++++++++++++++--
 5 files changed, 160 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/8b1c70ab/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
index fdb55ac..7006801 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.jmap.mailet.filter;
 
+import java.util.stream.Stream;
+
 import javax.inject.Inject;
 
 import org.apache.james.core.User;
@@ -27,14 +29,18 @@ import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.mailet.Mail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
 import com.google.common.annotations.VisibleForTesting;
 
 public class ActionApplier {
     static final String DELIVERY_PATH_PREFIX = "DeliveryPath_";
+    public static final Logger LOGGER = LoggerFactory.getLogger(ActionApplier.class);
 
     @VisibleForTesting
     static class Factory {
@@ -81,21 +87,22 @@ public class ActionApplier {
         this.user = user;
     }
 
-    public void apply(Rule.Action action) {
-        action.getAppendInMailboxes()
-                .getMailboxIds()
-                .stream()
-                .findFirst()
-                .map(mailboxIdFactory::fromString)
-                .ifPresent(Throwing.consumer(this::addStorageDirective));
+    public void apply(Stream<Rule.Action> actions) {
+        actions.flatMap(action -> action.getAppendInMailboxes().getMailboxIds().stream())
+            .map(mailboxIdFactory::fromString)
+            .forEach(Throwing.consumer(this::addStorageDirective));
     }
 
     private void addStorageDirective(MailboxId mailboxId) throws MailboxException {
-        MailboxSession mailboxSession = mailboxManager.createSystemSession(user.asString());
-        MessageManager messageManager = mailboxManager.getMailbox(mailboxId, mailboxSession);
-
-        String mailboxName = messageManager.getMailboxPath().getName();
-        String attributeNameForUser = DELIVERY_PATH_PREFIX + user.asString();
-        mail.setAttribute(attributeNameForUser, mailboxName);
+        try {
+            MailboxSession mailboxSession = mailboxManager.createSystemSession(user.asString());
+            MessageManager messageManager = mailboxManager.getMailbox(mailboxId, mailboxSession);
+
+            String mailboxName = messageManager.getMailboxPath().getName();
+            String attributeNameForUser = DELIVERY_PATH_PREFIX + user.asString();
+            mail.setAttribute(attributeNameForUser, mailboxName);
+        } catch (MailboxNotFoundException e) {
+            LOGGER.info("Mailbox {} does not exist, but it was mentioned in a JMAP filtering rule", mailboxId, e);
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/8b1c70ab/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
index 388fabe..a68193a 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/JMAPFiltering.java
@@ -21,6 +21,7 @@ package org.apache.james.jmap.mailet.filter;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
@@ -67,11 +68,11 @@ public class JMAPFiltering extends GenericMailet {
     private void findFirstApplicableRule(User user, Mail mail) {
         List<Rule> filteringRules = filteringManagement.listRulesForUser(user);
         RuleMatcher ruleMatcher = new RuleMatcher(filteringRules);
-        Optional<Rule> maybeMatchingRule = ruleMatcher.findApplicableRule(mail);
+        Stream<Rule> matchingRules = ruleMatcher.findApplicableRules(mail);
 
-        maybeMatchingRule.ifPresent(rule -> actionApplierFactory.forMail(mail)
-                .forUser(user)
-                .apply(rule.getAction()));
+        actionApplierFactory.forMail(mail)
+            .forUser(user)
+            .apply(matchingRules.map(Rule::getAction));
     }
 
     private Optional<User> retrieveUser(MailAddress recipient) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/8b1c70ab/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
index abc1da1..07f0371 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.mailet.filter;
 
 import java.util.List;
-import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.apache.james.jmap.api.filtering.Rule;
 import org.apache.mailet.Mail;
@@ -36,9 +36,8 @@ class RuleMatcher {
         this.filteringRules = filteringRules;
     }
 
-    Optional<Rule> findApplicableRule(Mail mail) {
+    Stream<Rule> findApplicableRules(Mail mail) {
         return filteringRules.stream()
-            .filter(rule -> MailMatcher.from(rule).match(mail))
-            .findFirst();
+            .filter(rule -> MailMatcher.from(rule).match(mail));
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/8b1c70ab/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
index 05dc317..526c94b 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringExtension.java
@@ -68,7 +68,7 @@ public class JMAPFilteringExtension implements BeforeEachCallback, ParameterReso
             this.filteringManagement = filteringManagement;
             this.mailboxManager = mailboxManager;
             try {
-                this.recipient1Mailbox = createMailbox(mailboxManager, RECIPIENT_1_USERNAME, RECIPIENT_1_MAILBOX_1);
+                this.recipient1Mailbox = createMailbox(RECIPIENT_1_USERNAME, RECIPIENT_1_MAILBOX_1);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
@@ -90,7 +90,7 @@ public class JMAPFilteringExtension implements BeforeEachCallback, ParameterReso
             return recipient1Mailbox;
         }
 
-        public MailboxId createMailbox(InMemoryMailboxManager mailboxManager, String username, String mailboxName) throws Exception {
+        public MailboxId createMailbox(String username, String mailboxName) throws Exception {
             MailboxSession mailboxSession = mailboxManager.createSystemSession(username);
             return mailboxManager
                 .createMailbox(MailboxPath.forUser(username, mailboxName), mailboxSession)

http://git-wip-us.apache.org/repos/asf/james-project/blob/8b1c70ab/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
index 15ba523..29d6bca 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java
@@ -54,6 +54,7 @@ import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_FU
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_3_USERNAME;
 import static org.apache.james.jmap.mailet.filter.JMAPFilteringFixture.USER_4_FULL_ADDRESS;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
 
 import java.util.Locale;
 import java.util.Optional;
@@ -63,7 +64,6 @@ import org.apache.james.core.User;
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.jmap.api.filtering.Rule;
 import org.apache.james.jmap.mailet.filter.JMAPFilteringExtension.JMAPFilteringTestSystem;
-import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.util.StreamUtils;
 import org.apache.mailet.base.test.FakeMail;
@@ -677,11 +677,10 @@ class JMAPFilteringTest {
     @Nested
     class MultiRuleBehaviourTest {
         @Test
-        void mailDirectiveShouldSetFirstMatchedRuleWhenMultipleRules(JMAPFilteringTestSystem testSystem) throws Exception {
-            InMemoryMailboxManager mailboxManager = testSystem.getMailboxManager();
-            MailboxId mailbox1Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
-            MailboxId mailbox2Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
-            MailboxId mailbox3Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
+        void mailDirectiveShouldSetLastMatchedRuleWhenMultipleRules(JMAPFilteringTestSystem testSystem) throws Exception {
+            MailboxId mailbox1Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
+            MailboxId mailbox2Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
+            MailboxId mailbox3Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
 
             testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
                 Rule.builder()
@@ -711,15 +710,14 @@ class JMAPFilteringTest {
             testSystem.getJmapFiltering().service(mail);
 
             assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
-                .isEqualTo("RECIPIENT_1_MAILBOX_1");
+                .isEqualTo("RECIPIENT_1_MAILBOX_3");
         }
 
         @Test
-        void mailDirectiveShouldSetFirstMatchedMailboxWhenMultipleMailboxes(JMAPFilteringTestSystem testSystem) throws Exception {
-            InMemoryMailboxManager mailboxManager = testSystem.getMailboxManager();
-            MailboxId mailbox1Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
-            MailboxId mailbox2Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
-            MailboxId mailbox3Id = testSystem.createMailbox(mailboxManager, RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
+        void mailDirectiveShouldSetLastMatchedMailboxWhenMultipleMailboxes(JMAPFilteringTestSystem testSystem) throws Exception {
+            MailboxId mailbox1Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
+            MailboxId mailbox2Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_2");
+            MailboxId mailbox3Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_3");
 
             testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
                 Rule.builder()
@@ -738,7 +736,35 @@ class JMAPFilteringTest {
             testSystem.getJmapFiltering().service(mail);
 
             assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
-                .isEqualTo("RECIPIENT_1_MAILBOX_3");
+                .isEqualTo("RECIPIENT_1_MAILBOX_1");
+        }
+
+        @Test
+        void rulesWithEmptyMailboxIdsShouldBeSkept(JMAPFilteringTestSystem testSystem) throws Exception {
+            MailboxId mailbox1Id = testSystem.createMailbox(RECIPIENT_1_USERNAME, "RECIPIENT_1_MAILBOX_1");
+
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of())))
+                    .build(),
+                Rule.builder()
+                    .id(Rule.Id.of("2"))
+                    .name("rule 2")
+                    .condition(Rule.Condition.of(SUBJECT, CONTAINS, UNSCRAMBLED_SUBJECT))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(
+                        mailbox1Id.serialize()))))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
+                    .setSubject(UNSCRAMBLED_SUBJECT));
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isEqualTo("RECIPIENT_1_MAILBOX_1");
         }
 
         @Test
@@ -780,4 +806,95 @@ class JMAPFilteringTest {
                 .isNull();
         }
     }
+
+    @Nested
+    class UnknownMailboxIds {
+        @Test
+        void serviceShouldNotThrowWhenUnknownMailboxId(JMAPFilteringTestSystem testSystem) throws Exception {
+            String unknownMailboxId = "4242";
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(unknownMailboxId)))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
+                .addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
+
+            assertThatCode(() -> testSystem.getJmapFiltering().service(mail))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        void mailDirectiveShouldNotBeSetWhenUnknownMailboxId(JMAPFilteringTestSystem testSystem) throws Exception {
+            String unknownMailboxId = "4242";
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(unknownMailboxId)))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
+                .addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isNull();
+        }
+
+        @Test
+        void rulesWithInvalidMailboxIdsShouldBeSkept(JMAPFilteringTestSystem testSystem) throws Exception {
+            String unknownMailboxId = "4242";
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(unknownMailboxId)))
+                    .build(),
+                Rule.builder()
+                    .id(Rule.Id.of("2"))
+                    .name("rule 2")
+                    .condition(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(
+                        testSystem.getRecipient1MailboxId().serialize())))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
+                .addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isEqualTo(RECIPIENT_1_MAILBOX_1);
+        }
+
+        @Test
+        void rulesWithMultipleMailboxIdsShouldFallbackWhenInvalidFirstMailboxId(JMAPFilteringTestSystem testSystem) throws Exception {
+            String unknownMailboxId = "4242";
+
+            testSystem.getFilteringManagement().defineRulesForUser(User.fromUsername(RECIPIENT_1_USERNAME),
+                Rule.builder()
+                    .id(Rule.Id.of("1"))
+                    .name("rule 1")
+                    .condition(Rule.Condition.of(FROM, CONTAINS, FRED_MARTIN_FULLNAME))
+                    .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(
+                        unknownMailboxId,
+                        testSystem.getRecipient1MailboxId().serialize())))
+                    .build());
+
+            FakeMail mail = testSystem.asMail(mimeMessageBuilder()
+                .addFrom(FRED_MARTIN_FULL_SCRAMBLED_ADDRESS));
+
+            testSystem.getJmapFiltering().service(mail);
+
+            assertThat(mail.getAttribute(DELIVERY_PATH_PREFIX + RECIPIENT_1_USERNAME))
+                .isEqualTo(RECIPIENT_1_MAILBOX_1);
+        }
+    }
 }
\ No newline at end of file


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


[21/26] james-project git commit: JAMES-2529 Correct MailetPreconditionTest for BCC_CHECK

Posted by ad...@apache.org.
JAMES-2529 Correct MailetPreconditionTest for BCC_CHECK


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

Branch: refs/heads/master
Commit: a8a8a905a63423ae48f48c1a15e5501ca874c4ba
Parents: 8382d61
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 12:23:48 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:55 2018 +0200

----------------------------------------------------------------------
 .../james/jmap/MailetPreconditionTest.java      | 32 +++++++++-----------
 1 file changed, 14 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/a8a8a905/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
index 72d74f2..04832d6 100644
--- a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
+++ b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
@@ -20,7 +20,6 @@
 package org.apache.james.jmap;
 
 import java.util.List;
-import java.util.Properties;
 
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.james.jmap.mailet.VacationMailet;
@@ -39,7 +38,6 @@ import com.google.common.collect.Lists;
 public class MailetPreconditionTest {
 
     private static final MailetContext MAILET_CONTEXT = null;
-    private static final String WRONG_NAME = "wrong";
     private static final String BCC = "bcc";
 
     @Test(expected = ConfigurationException.class)
@@ -83,40 +81,38 @@ public class MailetPreconditionTest {
     @Test(expected = ConfigurationException.class)
     public void bccMailetCheckShouldThrowOnWrongMatcher() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(),  new RemoveMimeHeader()));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
+        CamelMailetContainerModule.BCC_Check.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
     public void bccMailetCheckShouldThrowOnWrongMailet() throws Exception {
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new Null()));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
+        CamelMailetContainerModule.BCC_Check.check(pairs);
     }
 
     @Test(expected = ConfigurationException.class)
-    public void bccMailetCheckShouldThrowOnWrongMailetName() throws Exception {
-        Properties properties = new Properties();
-        properties.setProperty("name", WRONG_NAME);
+    public void bccMailetCheckShouldThrowOnWrongFieldName() throws Exception {
         RemoveMimeHeader removeMimeHeader = new RemoveMimeHeader();
         removeMimeHeader.init(FakeMailetConfig.builder()
-                .mailetName(WRONG_NAME)
-                .mailetContext(MAILET_CONTEXT)
-                .setProperty("name", WRONG_NAME)
-                .build());
+            .mailetName(BCC)
+            .mailetContext(MAILET_CONTEXT)
+            .setProperty("name", "bad")
+            .build());
 
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
+        CamelMailetContainerModule.BCC_Check.check(pairs);
     }
 
-    @Test(expected = ConfigurationException.class)
+    @Test
     public void bccMailetCheckShouldNotThrowOnValidPair() throws Exception {
         RemoveMimeHeader removeMimeHeader = new RemoveMimeHeader();
         removeMimeHeader.init(FakeMailetConfig.builder()
-                .mailetName(BCC)
-                .mailetContext(MAILET_CONTEXT)
-                .setProperty("name", BCC)
-                .build());
+            .mailetName(BCC)
+            .mailetContext(MAILET_CONTEXT)
+            .setProperty("name", BCC)
+            .build());
 
         List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
+        CamelMailetContainerModule.BCC_Check.check(pairs);
     }
 }


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


[14/26] james-project git commit: JAMES-2529 Replace OptionalUtils::ofNullableToStream by StreamUtils::ofNullables

Posted by ad...@apache.org.
JAMES-2529 Replace OptionalUtils::ofNullableToStream by StreamUtils::ofNullables


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

Branch: refs/heads/master
Commit: aee5424b30de411ab0e6cf467d63cf49bc4959ae
Parents: d0ee968
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 14:57:19 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:03 2018 +0200

----------------------------------------------------------------------
 .../main/java/org/apache/james/util/OptionalUtils.java |  4 ----
 .../main/java/org/apache/james/util/StreamUtils.java   |  4 ++++
 .../java/org/apache/james/util/OptionalUtilsTest.java  | 13 -------------
 .../james/transport/matchers/dlp/DlpDomainRules.java   |  3 ++-
 .../apache/james/jmap/mailet/filter/MailMatcher.java   |  3 +--
 5 files changed, 7 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/aee5424b/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java b/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
index 8f64e38..4a3449c 100644
--- a/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
+++ b/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
@@ -42,10 +42,6 @@ public class OptionalUtils {
             .orElse(Stream.of());
     }
 
-    public static <T> Stream<T> ofNullableToStream(T maybeNull) {
-        return toStream(Optional.ofNullable(maybeNull));
-    }
-
     @SafeVarargs
     public static <T> Optional<T> or(Optional<T>... optionals) {
         return orStream(Arrays.stream(optionals));

http://git-wip-us.apache.org/repos/asf/james-project/blob/aee5424b/server/container/util/src/main/java/org/apache/james/util/StreamUtils.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/main/java/org/apache/james/util/StreamUtils.java b/server/container/util/src/main/java/org/apache/james/util/StreamUtils.java
index e8a71bd..4f3df3f 100644
--- a/server/container/util/src/main/java/org/apache/james/util/StreamUtils.java
+++ b/server/container/util/src/main/java/org/apache/james/util/StreamUtils.java
@@ -27,6 +27,10 @@ import java.util.stream.Stream;
 
 public class StreamUtils {
 
+    public static <T> Stream<T> ofNullables(T... array) {
+        return ofNullable(array);
+    }
+
     public static <T> Stream<T> ofNullable(T[] array) {
         return ofOptional(Optional.ofNullable(array));
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/aee5424b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
index f7d7bf2..c0cf551 100644
--- a/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
+++ b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
@@ -85,19 +85,6 @@ public class OptionalUtilsTest {
     }
 
     @Test
-    public void ofNullableToStreamShouldReturnAStreamContainingTheValueWhenNotNull() {
-        long value = 18L;
-        assertThat(OptionalUtils.ofNullableToStream(value))
-            .containsExactly(value);
-    }
-
-    @Test
-    public void ofNullableToStreamShouldReturnAnEmptyStreamWhenNull() {
-        assertThat(OptionalUtils.ofNullableToStream(null))
-            .isEmpty();
-    }
-
-    @Test
     public void orShouldReturnEmptyWhenEmpty() {
         assertThat(
             OptionalUtils.or(

http://git-wip-us.apache.org/repos/asf/james-project/blob/aee5424b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
index 3dcfec7..b954801 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
@@ -39,6 +39,7 @@ import org.apache.james.dlp.api.DLPConfigurationItem.Targets;
 import org.apache.james.javax.AddressHelper;
 import org.apache.james.javax.MultipartUtil;
 import org.apache.james.util.OptionalUtils;
+import org.apache.james.util.StreamUtils;
 import org.apache.mailet.Mail;
 
 import com.github.fge.lambdas.Throwing;
@@ -168,7 +169,7 @@ public class DlpDomainRules {
             }
 
             private Stream<String> listEnvelopSender(Mail mail) {
-                return OptionalUtils.ofNullableToStream(mail.getSender()).map(MailAddress::asString);
+                return StreamUtils.ofNullables(mail.getSender()).map(MailAddress::asString);
             }
 
             private Stream<String> listFromHeaders(Mail mail) throws MessagingException {

http://git-wip-us.apache.org/repos/asf/james-project/blob/aee5424b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index a7ac517..031e696 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -36,7 +36,6 @@ import org.apache.james.javax.AddressHelper;
 import org.apache.james.jmap.api.filtering.Rule;
 import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
 import org.apache.james.mime4j.util.MimeUtil;
-import org.apache.james.util.OptionalUtils;
 import org.apache.james.util.StreamUtils;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
@@ -52,7 +51,7 @@ public interface MailMatcher {
         Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class);
 
         HeaderExtractor SUBJECT_EXTRACTOR = mail ->
-            OptionalUtils.ofNullableToStream(mail.getMessage().getSubject());
+            StreamUtils.ofNullables(mail.getMessage().getSubject());
         HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC);
         HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO);
         HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR);


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


[20/26] james-project git commit: JAMES-2529 Rewrite MailetPreconditionTest with JUNIT 5

Posted by ad...@apache.org.
JAMES-2529 Rewrite MailetPreconditionTest with JUNIT 5

Use nested classes to enhance readability


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

Branch: refs/heads/master
Commit: 7164031c3226c4bf8313335ffd7800683f7a0b30
Parents: a8a8a90
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 12:32:28 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:55 2018 +0200

----------------------------------------------------------------------
 server/container/guice/protocols/jmap/pom.xml   |  18 +-
 .../james/jmap/MailetPreconditionTest.java      | 175 +++++++++++--------
 2 files changed, 115 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7164031c/server/container/guice/protocols/jmap/pom.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/pom.xml b/server/container/guice/protocols/jmap/pom.xml
index 9cce57a..df48b58 100644
--- a/server/container/guice/protocols/jmap/pom.xml
+++ b/server/container/guice/protocols/jmap/pom.xml
@@ -71,13 +71,23 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.assertj</groupId>
-            <artifactId>assertj-core</artifactId>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>

http://git-wip-us.apache.org/repos/asf/james-project/blob/7164031c/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
index 04832d6..aaacbd9 100644
--- a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
+++ b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/MailetPreconditionTest.java
@@ -19,6 +19,9 @@
 
 package org.apache.james.jmap;
 
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
 import java.util.List;
 
 import org.apache.commons.configuration.ConfigurationException;
@@ -31,88 +34,112 @@ import org.apache.james.transport.matchers.All;
 import org.apache.james.transport.matchers.RecipientIsLocal;
 import org.apache.mailet.MailetContext;
 import org.apache.mailet.base.test.FakeMailetConfig;
-import org.junit.Test;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
 
 import com.google.common.collect.Lists;
 
-public class MailetPreconditionTest {
+class MailetPreconditionTest {
 
     private static final MailetContext MAILET_CONTEXT = null;
     private static final String BCC = "bcc";
 
-    @Test(expected = ConfigurationException.class)
-    public void vacationMailetCheckShouldThrowOnEmptyList() throws Exception {
-        JMAPModule.VACATION_MAILET_CHECK.check(Lists.newArrayList());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void vacationMailetCheckShouldThrowOnNullList() throws Exception {
-        JMAPModule.VACATION_MAILET_CHECK.check(null);
-    }
-
-    @Test(expected = ConfigurationException.class)
-    public void vacationMailetCheckShouldThrowOnWrongMatcher() throws Exception {
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new VacationMailet(null, null, null, null, null)));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
-    }
-
-    @Test(expected = ConfigurationException.class)
-    public void vacationMailetCheckShouldThrowOnWrongMailet() throws Exception {
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new Null()));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
-    }
-
-    @Test
-    public void vacationMailetCheckShouldNotThrowIfValidPairPresent() throws Exception {
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new VacationMailet(null, null, null, null, null)));
-        JMAPModule.VACATION_MAILET_CHECK.check(pairs);
-    }
-
-    @Test(expected = ConfigurationException.class)
-    public void bccMailetCheckShouldThrowOnEmptyList() throws Exception {
-        CamelMailetContainerModule.BCC_Check.check(Lists.newArrayList());
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void bccMailetCheckShouldThrowOnNullList() throws Exception {
-        CamelMailetContainerModule.BCC_Check.check(null);
-    }
-
-    @Test(expected = ConfigurationException.class)
-    public void bccMailetCheckShouldThrowOnWrongMatcher() throws Exception {
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(),  new RemoveMimeHeader()));
-        CamelMailetContainerModule.BCC_Check.check(pairs);
-    }
-
-    @Test(expected = ConfigurationException.class)
-    public void bccMailetCheckShouldThrowOnWrongMailet() throws Exception {
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new Null()));
-        CamelMailetContainerModule.BCC_Check.check(pairs);
-    }
-
-    @Test(expected = ConfigurationException.class)
-    public void bccMailetCheckShouldThrowOnWrongFieldName() throws Exception {
-        RemoveMimeHeader removeMimeHeader = new RemoveMimeHeader();
-        removeMimeHeader.init(FakeMailetConfig.builder()
-            .mailetName(BCC)
-            .mailetContext(MAILET_CONTEXT)
-            .setProperty("name", "bad")
-            .build());
-
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
-        CamelMailetContainerModule.BCC_Check.check(pairs);
+    @Nested
+    class VacationMailetCheck {
+        @Test
+        void vacationMailetCheckShouldThrowOnEmptyList() {
+            assertThatThrownBy(() -> JMAPModule.VACATION_MAILET_CHECK.check(Lists.newArrayList()))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void vacationMailetCheckShouldThrowOnNullList() {
+            assertThatThrownBy(() -> JMAPModule.VACATION_MAILET_CHECK.check(null))
+                .isInstanceOf(NullPointerException.class);
+        }
+
+        @Test
+        void vacationMailetCheckShouldThrowOnWrongMatcher() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new VacationMailet(null, null, null, null, null)));
+
+            assertThatThrownBy(() -> JMAPModule.VACATION_MAILET_CHECK.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void vacationMailetCheckShouldThrowOnWrongMailet() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new Null()));
+
+            assertThatThrownBy(() -> JMAPModule.VACATION_MAILET_CHECK.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void vacationMailetCheckShouldNotThrowIfValidPairPresent() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new VacationMailet(null, null, null, null, null)));
+
+            assertThatCode(() -> JMAPModule.VACATION_MAILET_CHECK.check(pairs))
+                .doesNotThrowAnyException();
+        }
     }
 
-    @Test
-    public void bccMailetCheckShouldNotThrowOnValidPair() throws Exception {
-        RemoveMimeHeader removeMimeHeader = new RemoveMimeHeader();
-        removeMimeHeader.init(FakeMailetConfig.builder()
-            .mailetName(BCC)
-            .mailetContext(MAILET_CONTEXT)
-            .setProperty("name", BCC)
-            .build());
-
-        List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
-        CamelMailetContainerModule.BCC_Check.check(pairs);
+    @Nested
+    class BccCheck {
+        @Test
+        void bccMailetCheckShouldThrowOnEmptyList() {
+            assertThatThrownBy(() -> CamelMailetContainerModule.BCC_Check.check(Lists.newArrayList()))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void bccMailetCheckShouldThrowOnNullList() {
+            assertThatThrownBy(() -> CamelMailetContainerModule.BCC_Check.check(null))
+                .isInstanceOf(NullPointerException.class);
+        }
+
+        @Test
+        void bccMailetCheckShouldThrowOnWrongMatcher() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new RecipientIsLocal(), new RemoveMimeHeader()));
+
+            assertThatThrownBy(() -> CamelMailetContainerModule.BCC_Check.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void bccMailetCheckShouldThrowOnWrongMailet() {
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), new Null()));
+
+            assertThatThrownBy(() -> CamelMailetContainerModule.BCC_Check.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void bccMailetCheckShouldThrowOnWrongFieldName() throws Exception {
+            RemoveMimeHeader removeMimeHeader = new RemoveMimeHeader();
+            removeMimeHeader.init(FakeMailetConfig.builder()
+                .mailetName(BCC)
+                .mailetContext(MAILET_CONTEXT)
+                .setProperty("name", "bad")
+                .build());
+
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
+
+            assertThatThrownBy(() -> CamelMailetContainerModule.BCC_Check.check(pairs))
+                .isInstanceOf(ConfigurationException.class);
+        }
+
+        @Test
+        void bccMailetCheckShouldNotThrowOnValidPair() throws Exception {
+            RemoveMimeHeader removeMimeHeader = new RemoveMimeHeader();
+            removeMimeHeader.init(FakeMailetConfig.builder()
+                .mailetName(BCC)
+                .mailetContext(MAILET_CONTEXT)
+                .setProperty("name", BCC)
+                .build());
+
+            List<MatcherMailetPair> pairs = Lists.newArrayList(new MatcherMailetPair(new All(), removeMimeHeader));
+            assertThatCode(() -> CamelMailetContainerModule.BCC_Check.check(pairs))
+                .doesNotThrowAnyException();
+        }
     }
 }


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


[23/26] james-project git commit: JAMES-2529 Filter case sensitivity integration test

Posted by ad...@apache.org.
JAMES-2529 Filter case sensitivity integration test


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

Branch: refs/heads/master
Commit: 443f3c612ae2f63513f0c63b3e914bff05df0f07
Parents: 2dcde78
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Wed Aug 29 17:31:35 2018 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:55 2018 +0200

----------------------------------------------------------------------
 .../jmap/methods/integration/FilterTest.java    | 113 +++++++++++++++++++
 1 file changed, 113 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/443f3c61/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
index f56615c..e87c417 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
@@ -41,6 +41,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.hasSize;
 
 import java.io.IOException;
+import java.util.Locale;
 
 import org.apache.james.GuiceJamesServer;
 import org.apache.james.jmap.JmapCommonRequests;
@@ -1219,6 +1220,62 @@ public abstract class FilterTest {
             () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
     }
 
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenSubjectRuleDoesNotMatchRuleBecaseOfCase() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"subject\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"different case value\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"},{ \"name\": \"Cedric\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"subject\": \"DIFFERENT CASE VALUE\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
     @Test
     public void messageShouldBeAppendedInSpecificMailboxWhenContainsComparatorMatches() {
         given()
@@ -1494,6 +1551,62 @@ public abstract class FilterTest {
             () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
     }
 
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenExactlyEqualsMatchesCaseInsensitivelyFullHeader() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"exactly-equals\"," +
+                "        \"value\": \"bob <" + BOB.toUpperCase(Locale.ENGLISH) + ">\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
     @Test
     public void messageShouldBeAppendedInInboxWhenExactlyEqualsComparatorDoesNotMatch() {
         given()


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


[12/26] james-project git commit: JAMES-2529 Handling all mailbox exceptions while resolving mailbox name

Posted by ad...@apache.org.
JAMES-2529 Handling all mailbox exceptions while resolving mailbox name


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

Branch: refs/heads/master
Commit: 1e2124e0ee253bbfb6942740546e6422996dfdfe
Parents: c6a6e97
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 14:01:10 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:03 2018 +0200

----------------------------------------------------------------------
 .../org/apache/james/jmap/mailet/filter/ActionApplier.java   | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1e2124e0/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
index 7006801..72376ab 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
@@ -28,14 +28,12 @@ import org.apache.james.jmap.api.filtering.Rule;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.github.fge.lambdas.Throwing;
 import com.google.common.annotations.VisibleForTesting;
 
 public class ActionApplier {
@@ -90,10 +88,10 @@ public class ActionApplier {
     public void apply(Stream<Rule.Action> actions) {
         actions.flatMap(action -> action.getAppendInMailboxes().getMailboxIds().stream())
             .map(mailboxIdFactory::fromString)
-            .forEach(Throwing.consumer(this::addStorageDirective));
+            .forEach(this::addStorageDirective);
     }
 
-    private void addStorageDirective(MailboxId mailboxId) throws MailboxException {
+    private void addStorageDirective(MailboxId mailboxId) {
         try {
             MailboxSession mailboxSession = mailboxManager.createSystemSession(user.asString());
             MessageManager messageManager = mailboxManager.getMailbox(mailboxId, mailboxSession);
@@ -103,6 +101,8 @@ public class ActionApplier {
             mail.setAttribute(attributeNameForUser, mailboxName);
         } catch (MailboxNotFoundException e) {
             LOGGER.info("Mailbox {} does not exist, but it was mentioned in a JMAP filtering rule", mailboxId, e);
+        } catch (Exception e) {
+            LOGGER.error("Unexpected failure while resolving mailbox name for {}", mailboxId, e);
         }
     }
 }


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


[03/26] james-project git commit: JAMES-2529 Add a OptionalUtils::ofNullableToStream convenience method

Posted by ad...@apache.org.
JAMES-2529 Add a OptionalUtils::ofNullableToStream convenience method


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

Branch: refs/heads/master
Commit: 924d23df354ebcb76b835a5916d93ac33d64111e
Parents: 016a282
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Aug 30 09:28:07 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:07:01 2018 +0200

----------------------------------------------------------------------
 .../util/src/main/java/org/apache/james/util/OptionalUtils.java | 4 ++++
 .../org/apache/james/transport/matchers/dlp/DlpDomainRules.java | 2 +-
 .../java/org/apache/james/jmap/mailet/filter/MailMatcher.java   | 5 ++---
 3 files changed, 7 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/924d23df/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java b/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
index 4a3449c..8f64e38 100644
--- a/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
+++ b/server/container/util/src/main/java/org/apache/james/util/OptionalUtils.java
@@ -42,6 +42,10 @@ public class OptionalUtils {
             .orElse(Stream.of());
     }
 
+    public static <T> Stream<T> ofNullableToStream(T maybeNull) {
+        return toStream(Optional.ofNullable(maybeNull));
+    }
+
     @SafeVarargs
     public static <T> Optional<T> or(Optional<T>... optionals) {
         return orStream(Arrays.stream(optionals));

http://git-wip-us.apache.org/repos/asf/james-project/blob/924d23df/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
index d3f0eef..3dcfec7 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/DlpDomainRules.java
@@ -168,7 +168,7 @@ public class DlpDomainRules {
             }
 
             private Stream<String> listEnvelopSender(Mail mail) {
-                return OptionalUtils.toStream(Optional.ofNullable(mail.getSender()).map(MailAddress::asString));
+                return OptionalUtils.ofNullableToStream(mail.getSender()).map(MailAddress::asString);
             }
 
             private Stream<String> listFromHeaders(Mail mail) throws MessagingException {

http://git-wip-us.apache.org/repos/asf/james-project/blob/924d23df/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index 4626a7f..185c08b 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -36,6 +36,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.james.javax.AddressHelper;
 import org.apache.james.jmap.api.filtering.Rule;
 import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
+import org.apache.james.util.OptionalUtils;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -176,9 +177,7 @@ public interface MailMatcher {
     }
 
     HeaderExtractor SUBJECT_EXTRACTOR = mail ->
-            Optional.ofNullable(mail.getMessage().getSubject())
-                    .map(Stream::of)
-                    .orElse(Stream.empty());
+        OptionalUtils.ofNullableToStream(mail.getMessage().getSubject());
     HeaderExtractor RECIPIENT_EXTRACTOR =  mail -> addressExtractor(
                 mail.getMessage().getRecipients(Message.RecipientType.TO),
                 mail.getMessage().getRecipients(Message.RecipientType.CC));


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


[17/26] james-project git commit: JAMES-2529 Integration tests for JMAP filtering on incoming mails

Posted by ad...@apache.org.
JAMES-2529 Integration tests for JMAP filtering on incoming mails

Exercise all fields and a basic subset of comparators.
More fine grained testing is done in unit tests.


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

Branch: refs/heads/master
Commit: b53509c2ced5710305b5302270ced65ad5f5d1ee
Parents: 7527dae
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Aug 29 11:52:09 2018 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Aug 30 15:11:54 2018 +0200

----------------------------------------------------------------------
 .../src/test/resources/mailetcontainer.xml      |    1 +
 .../apache/james/jmap/JmapCommonRequests.java   |   19 +
 .../jmap/methods/integration/FilterTest.java    | 1572 +++++++++++++++++-
 .../src/test/resources/mailetcontainer.xml      |    3 +-
 4 files changed, 1582 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/b53509c2/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml
index 887c947..2ecff3f 100644
--- a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml
+++ b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml
@@ -72,6 +72,7 @@
             <mailet match="IsMarkedAsSpam" class="WithStorageDirective">
                 <targetFolderName>Spam</targetFolderName>
             </mailet>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>

http://git-wip-us.apache.org/repos/asf/james-project/blob/b53509c2/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapCommonRequests.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapCommonRequests.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapCommonRequests.java
index ef61472..fe3f84d 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapCommonRequests.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapCommonRequests.java
@@ -32,6 +32,7 @@ import java.util.Map;
 
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.mailbox.Role;
+import org.apache.james.mailbox.model.MailboxId;
 
 import io.restassured.builder.ResponseSpecBuilder;
 import io.restassured.specification.ResponseSpecification;
@@ -85,6 +86,24 @@ public class JmapCommonRequests {
         }
     }
 
+    public static boolean isAnyMessageFoundInRecipientsMailbox(AccessToken recipientToken, MailboxId mailboxId) {
+        try {
+            with()
+                .header("Authorization", recipientToken.serialize())
+                .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + mailboxId.serialize() + "\"]}}, \"#0\"]]")
+            .when()
+                .post("/jmap")
+            .then()
+                .statusCode(200)
+                .body(NAME, equalTo("messageList"))
+                .body(ARGUMENTS + ".messageIds", hasSize(1));
+            return true;
+
+        } catch (AssertionError e) {
+            return false;
+        }
+    }
+
     public static String getInboxId(AccessToken accessToken) {
         return getMailboxId(accessToken, Role.INBOX);
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/b53509c2/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
index 643c844..f56615c 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/FilterTest.java
@@ -22,13 +22,20 @@ package org.apache.james.jmap.methods.integration;
 import static io.restassured.RestAssured.given;
 import static io.restassured.RestAssured.with;
 import static org.apache.james.jmap.HttpJmapAuthentication.authenticateJamesUser;
+import static org.apache.james.jmap.JmapCommonRequests.getOutboxId;
 import static org.apache.james.jmap.JmapURIBuilder.baseUri;
 import static org.apache.james.jmap.TestingConstants.ALICE;
 import static org.apache.james.jmap.TestingConstants.ALICE_PASSWORD;
 import static org.apache.james.jmap.TestingConstants.ARGUMENTS;
+import static org.apache.james.jmap.TestingConstants.BOB;
+import static org.apache.james.jmap.TestingConstants.BOB_PASSWORD;
+import static org.apache.james.jmap.TestingConstants.CEDRIC;
 import static org.apache.james.jmap.TestingConstants.DOMAIN;
 import static org.apache.james.jmap.TestingConstants.NAME;
+import static org.apache.james.jmap.TestingConstants.calmlyAwait;
 import static org.apache.james.jmap.TestingConstants.jmapRequestSpecBuilder;
+import static org.apache.james.mailbox.model.MailboxConstants.INBOX;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.hasSize;
@@ -36,8 +43,11 @@ import static org.hamcrest.Matchers.hasSize;
 import java.io.IOException;
 
 import org.apache.james.GuiceJamesServer;
+import org.apache.james.jmap.JmapCommonRequests;
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.modules.MailboxProbeImpl;
 import org.apache.james.probe.DataProbe;
 import org.apache.james.utils.DataProbeImpl;
 import org.apache.james.utils.JmapGuiceProbe;
@@ -54,21 +64,31 @@ public abstract class FilterTest {
     protected abstract MailboxId randomMailboxId();
 
     private AccessToken accessToken;
+    private AccessToken bobAccessToken;
     private GuiceJamesServer jmapServer;
 
+    private MailboxId matchedMailbox;
+    private MailboxId inbox;
+
     @Before
     public void setup() throws Throwable {
         jmapServer = createJmapServer();
         jmapServer.start();
 
         RestAssured.requestSpecification = jmapRequestSpecBuilder
-                .setPort(jmapServer.getProbe(JmapGuiceProbe.class).getJmapPort())
-                .build();
+            .setPort(jmapServer.getProbe(JmapGuiceProbe.class).getJmapPort())
+            .build();
 
         DataProbe dataProbe = jmapServer.getProbe(DataProbeImpl.class);
         dataProbe.addDomain(DOMAIN);
         dataProbe.addUser(ALICE, ALICE_PASSWORD);
+        dataProbe.addUser(BOB, BOB_PASSWORD);
         accessToken = authenticateJamesUser(baseUri(jmapServer), ALICE, ALICE_PASSWORD);
+        bobAccessToken = authenticateJamesUser(baseUri(jmapServer), BOB, BOB_PASSWORD);
+
+        MailboxProbeImpl mailboxProbe = jmapServer.getProbe(MailboxProbeImpl.class);
+        matchedMailbox = mailboxProbe.createMailbox(MailboxPath.forUser(ALICE, "matched"));
+        inbox = mailboxProbe.createMailbox(MailboxPath.forUser(ALICE, INBOX));
     }
 
     @After
@@ -100,7 +120,7 @@ public abstract class FilterTest {
         MailboxId mailbox1 = randomMailboxId();
         MailboxId mailbox2 = randomMailboxId();
 
-        with()
+        given()
             .header("Authorization", accessToken.serialize())
             .body("[[" +
                   "  \"setFilter\", " +
@@ -123,7 +143,11 @@ public abstract class FilterTest {
                   "  ]}, " +
                   "\"#0\"" +
                   "]]")
-            .post("/jmap");
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
         with()
             .header("Authorization", accessToken.serialize())
             .body("[[" +
@@ -147,7 +171,10 @@ public abstract class FilterTest {
                   "  ]}, " +
                   "\"#0\"" +
                   "]]")
-            .post("/jmap");
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
 
         given()
             .header("Authorization", accessToken.serialize())
@@ -336,7 +363,7 @@ public abstract class FilterTest {
         MailboxId mailbox1 = randomMailboxId();
         MailboxId mailbox2 = randomMailboxId();
 
-        with()
+        given()
             .header("Authorization", accessToken.serialize())
             .body("[[" +
                   "  \"setFilter\", " +
@@ -373,7 +400,10 @@ public abstract class FilterTest {
                   "  ]}, " +
                   "\"#0\"" +
                   "]]")
-            .post("/jmap");
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
 
         given()
             .header("Authorization", accessToken.serialize())
@@ -406,7 +436,7 @@ public abstract class FilterTest {
     public void setFilterShouldClearPreviouslyStoredRulesWhenEmptyBody() {
         MailboxId mailbox = randomMailboxId();
 
-        with()
+        given()
             .header("Authorization", accessToken.serialize())
             .body("[[" +
                   "  \"setFilter\", " +
@@ -429,7 +459,10 @@ public abstract class FilterTest {
                   "  ]}, " +
                   "\"#0\"" +
                   "]]")
-            .post("/jmap");
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
 
         with()
             .header("Authorization", accessToken.serialize())
@@ -440,7 +473,10 @@ public abstract class FilterTest {
                   "  }, " +
                   "\"#0\"" +
                   "]]")
-            .post("/jmap");
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
 
         given()
             .header("Authorization", accessToken.serialize())
@@ -461,7 +497,7 @@ public abstract class FilterTest {
     public void allFieldsAndComparatorsShouldBeSupported() {
         MailboxId mailbox = randomMailboxId();
 
-        with()
+        given()
             .header("Authorization", accessToken.serialize())
             .body("[[" +
                   "  \"setFilter\", " +
@@ -540,7 +576,10 @@ public abstract class FilterTest {
                   "  ]}, " +
                   "\"#0\"" +
                   "]]")
-            .post("/jmap");
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
 
         given()
             .header("Authorization", accessToken.serialize())
@@ -572,4 +611,1513 @@ public abstract class FilterTest {
             .body(ARGUMENTS + ".singleton[4].condition.comparator", equalTo("contains"));
     }
 
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenFromRuleMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + BOB + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenToRuleMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"to\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"},{ \"name\": \"Cedric\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenCcRuleMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails cc-ed to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"cc\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"cc\": [{ \"name\": \"Cedric\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenRecipientRuleMatchesCc() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails cc-ed to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"recipient\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"cc\": [{ \"name\": \"Cedric\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenRecipientRuleMatchesTo() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails cc-ed to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"recipient\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"cc\": [{ \"name\": \"Cedric\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenSubjectRuleMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"subject\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"matchme\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"},{ \"name\": \"Cedric\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"subject\": \"matchme\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenFromDoesNotMatchRule() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenToDoesNotMatchRule() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"to\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenCcDoesNotMatchRule() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails cc-ed to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"cc\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"cc\": [{ \"name\": \"Cedric\", \"email\": \"" + BOB + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenRecipientDoesNotMatchRule() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails cc-ed to cedric\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"recipient\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + CEDRIC + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"cc\": [{ \"name\": \"Cedric\", \"email\": \"" + BOB + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenSubjectRuleDoesNotMatchRule() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"subject\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"matchme\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"},{ \"name\": \"Cedric\", \"email\": \"" + CEDRIC + "\"}]," +
+            "      \"subject\": \"nomatch\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenContainsComparatorMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"bo\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenContainsComparatorDoesNotMatch() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"ced\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenExactlyEqualsMatchesName() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"exactly-equals\"," +
+                "        \"value\": \"Bob\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenExactlyEqualsMatchesAddress() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"exactly-equals\"," +
+                "        \"value\": \"" + BOB + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenExactlyEqualsMatchesFullHeader() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"exactly-equals\"," +
+                "        \"value\": \"Bob <" + BOB + ">\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenExactlyEqualsComparatorDoesNotMatch() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"exactly-equals\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"nomatch\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenNotContainsComparatorMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"not-contains\"," +
+                "        \"value\": \"other\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenNotContainsComparatorDoesNotMatch() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"not-contains\"," +
+                "        \"value\": \"bob\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenContainsNotExactlyEqualsMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"not-exactly-equals\"," +
+                "        \"value\": \"nomatch\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenNotExactlyEqualsMatchesAddress() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"not-exactly-equals\"," +
+                "        \"value\": \"" + BOB + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenNotExactlyEqualsMatchesFullHeader() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"not-exactly-equals\"," +
+                "        \"value\": \"Bob <" + BOB + ">\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Bob\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInInboxWhenNotExactlyEqualsComparatorMatchesName() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"not-exactly-equals\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"Bob\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenFirstRuleMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + BOB + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }," +
+                "    {" +
+                "      \"id\": \"3000-346\"," +
+                "      \"name\": \"Emails to alice\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"to\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + ALICE + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void messageShouldBeAppendedInSpecificMailboxWhenSecondRuleMatches() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"unknown@james.org\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }," +
+                "    {" +
+                "      \"id\": \"3000-346\"," +
+                "      \"name\": \"Emails to alice\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"to\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + ALICE + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+    }
+
+    @Test
+    public void inboxShouldBeEmptyWhenFromRuleMatchesInSpecificMailbox() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"" + BOB + "\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox));
+        assertThat(JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox)).isFalse();
+    }
+
+    @Test
+    public void matchedMailboxShouldBeEmptyWhenFromRuleDoesntMatch() {
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body("[[" +
+                "  \"setFilter\", " +
+                "  {" +
+                "    \"singleton\": [" +
+                "    {" +
+                "      \"id\": \"3000-345\"," +
+                "      \"name\": \"Emails from bob\"," +
+                "      \"condition\": {" +
+                "        \"field\": \"from\"," +
+                "        \"comparator\": \"contains\"," +
+                "        \"value\": \"unknown@james.org\"" +
+                "      }," +
+                "      \"action\": {" +
+                "        \"appendIn\": {" +
+                "          \"mailboxIds\": [\"" + matchedMailbox.serialize() + "\"]" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ]}, " +
+                "\"#0\"" +
+                "]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        String messageCreationId = "creationId1337";
+        String requestBody = "[[" +
+            "  \"setMessages\"," +
+            "  {" +
+            "    \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"from\": { \"name\": \"Me\", \"email\": \"" + BOB + "\"}," +
+            "      \"to\": [{ \"name\": \"Alice\", \"email\": \"" + ALICE + "\"}]," +
+            "      \"subject\": \"subject\"," +
+            "      \"mailboxIds\": [\"" + getOutboxId(bobAccessToken) + "\"]" +
+            "    }}" +
+            "  }," +
+            "  \"#0\"" +
+            "]]";
+
+        with()
+            .header("Authorization", bobAccessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, inbox));
+        assertThat(JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, matchedMailbox)).isFalse();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/b53509c2/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/resources/mailetcontainer.xml
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/resources/mailetcontainer.xml b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/resources/mailetcontainer.xml
index c783fd3..c890709 100644
--- a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/resources/mailetcontainer.xml
+++ b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/resources/mailetcontainer.xml
@@ -73,7 +73,8 @@
             <mailet match="IsMarkedAsSpam" class="WithStorageDirective">
                 <targetFolderName>Spam</targetFolderName>
             </mailet>
-             <mailet match="RecipientIsLocal" class="LocalDelivery"/>
+            <mailet match="RecipientIsLocal" class="org.apache.james.jmap.mailet.filter.JMAPFiltering"/>
+            <mailet match="RecipientIsLocal" class="LocalDelivery"/>
             <mailet match="HostIsLocal" class="ToProcessor">
                 <processor>local-address-error</processor>
                 <notice>550 - Requested action not taken: no such user here</notice>


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