JAMES-1854 Split responsibility of class SieveMailet

     - Customizing Mime Message before delivery
     - Delivering Mime message to someone INBOX
     - Extract pretty printing of MailAddresses

JAMES-1854 DeliveryUtils 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/ca85c529
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ca85c529
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ca85c529

Branch: refs/heads/master
Commit: ca85c529a6a13a0501445f9cfb3eb325d5b35288
Parents: 4362338
Author: Benoit Tellier <[email protected]>
Authored: Thu Oct 27 18:10:08 2016 +0200
Committer: Benoit Tellier <[email protected]>
Committed: Fri Nov 18 18:46:46 2016 +0700

----------------------------------------------------------------------
 .../mailets/delivery/DeliveryUtils.java         |  52 +
 .../mailets/delivery/LocalDelivery.java         |  53 +-
 .../mailets/delivery/MailDispatcher.java        | 149 +++
 .../transport/mailets/delivery/MailStorer.java  |  29 +
 .../mailets/delivery/SieveMailStorer.java       | 196 ++++
 .../transport/mailets/delivery/SieveMailet.java | 315 ------
 .../mailets/delivery/ToRecipientFolder.java     |  45 +-
 .../mailets/jsieve/CommonsLoggingAdapter.java   |  45 +-
 .../mailets/delivery/DeliveryUtilsTest.java     |  40 +
 .../mailets/delivery/SieveIntegrationTest.java  | 992 +++++++++++++++++++
 .../mailets/delivery/SieveMailetTest.java       | 953 ------------------
 11 files changed, 1547 insertions(+), 1322 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/DeliveryUtils.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/DeliveryUtils.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/DeliveryUtils.java
new file mode 100644
index 0000000..9c1abef
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/DeliveryUtils.java
@@ -0,0 +1,52 @@
+/****************************************************************
+ * 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.transport.mailets.delivery;
+
+import org.apache.commons.logging.Log;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.mailet.MailAddress;
+
+public class DeliveryUtils {
+
+    public static String getUsername(MailAddress m, UsersRepository 
usersRepository, Log log) {
+        try {
+            if (usersRepository.supportVirtualHosting()) {
+                return m.toString();
+            } else {
+                return m.getLocalPart() + "@localhost";
+            }
+        } catch (UsersRepositoryException e) {
+            log.error("Unable to access UsersRepository", e);
+            return m.getLocalPart() + "@localhost";
+
+        }
+    }
+
+    public static String prettyPrint(MailAddress mailAddress) {
+        if (mailAddress != null) {
+            return  "<" + mailAddress.toString() + ">";
+        } else {
+            return  "<>";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/LocalDelivery.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/LocalDelivery.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/LocalDelivery.java
index 8db7444..47a96ae 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/LocalDelivery.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/LocalDelivery.java
@@ -19,23 +19,21 @@
 
 package org.apache.james.transport.mailets.delivery;
 
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
+import org.apache.commons.logging.Log;
 import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.sieverepository.api.SieveRepository;
 import org.apache.james.transport.mailets.RecipientRewriteTable;
 import org.apache.james.transport.mailets.ResourceLocatorImpl;
+import org.apache.james.transport.mailets.jsieve.CommonsLoggingAdapter;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailetConfig;
-import org.apache.mailet.MailetContext;
 import org.apache.mailet.base.GenericMailet;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.mail.MessagingException;
-import java.util.Iterator;
 
 /**
  * Receives a Mail from the Queue and takes care of delivery of the
@@ -78,52 +76,45 @@ public class LocalDelivery extends GenericMailet {
         this.domainList = domainList;
     }
 
-    private SieveMailet sieveMailet;  // Mailet that actually stores the 
message
+    private MailDispatcher mailDispatcher;  // Mailet that actually stores the 
message
     private RecipientRewriteTable recipientRewriteTable;  // Mailet that 
applies RecipientRewriteTable
 
-    /**
-     * Delivers a mail to a local mailbox.
-     * 
-     * @param mail the mail being processed
-     * 
-     * @throws MessagingException if an error occurs while storing the mail
-     */
     public void service(Mail mail) throws MessagingException {
         recipientRewriteTable.service(mail);
         if (!mail.getState().equals(Mail.GHOST)) {
-            sieveMailet.service(mail);
+            mailDispatcher.dispatch(mail);
         }
     }
 
-    /**
-     * Return a string describing this mailet.
-     * 
-     * @return a string describing this mailet
-     */
     public String getMailetInfo() {
         return "Local Delivery Mailet";
     }
 
-    /**
-     * @see org.apache.mailet.base.GenericMailet#init()
-     */
-    public void init(MailetConfig mailetConfig) throws MessagingException {
-        super.init(mailetConfig);
+    public void init() throws MessagingException {
         recipientRewriteTable = new RecipientRewriteTable();
         recipientRewriteTable.setDomainList(domainList);
         recipientRewriteTable.setRecipientRewriteTable(rrt);
         recipientRewriteTable.init(getMailetConfig());
-        sieveMailet = SieveMailet.builder()
-            .mailboxManager(mailboxManager)
-            .userRepository(usersRepository)
-            .resourceLocator(ResourceLocatorImpl.instanciate(usersRepository, 
sieveRepository))
-            .mailetContext(getMailetContext())
-            .consume(getInitParameter("consume", true))
+        Log log = CommonsLoggingAdapter.builder()
+            .mailet(this)
             .quiet(getInitParameter("quiet", false))
             .verbose(getInitParameter("verbose", false))
-            .folder("INBOX")
             .build();
-        sieveMailet.init(mailetConfig);
+        String folder = "INBOX";
+        mailDispatcher = MailDispatcher.builder()
+            .mailStorer(SieveMailStorer.builder()
+                .sievePoster(new SievePoster(mailboxManager, folder, 
usersRepository, getMailetContext()))
+                .usersRepository(usersRepository)
+                .mailboxManager(mailboxManager)
+                
.resourceLocator(ResourceLocatorImpl.instanciate(usersRepository, 
sieveRepository))
+                .mailetContext(getMailetContext())
+                .folder(folder)
+                .log(log)
+                .build())
+            .consume(getInitParameter("consume", true))
+            .mailetContext(getMailetContext())
+            .log(log)
+            .build();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java
new file mode 100644
index 0000000..fe62364
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailDispatcher.java
@@ -0,0 +1,149 @@
+/****************************************************************
+ * 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.transport.mailets.delivery;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.logging.Log;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+import org.apache.mailet.base.RFC2822Headers;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Contains resource bindings.
+ */
+public class MailDispatcher {
+
+    public static final String DELIVERED_TO = "Delivered-To";
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+        private MailStorer mailStorer;
+        private boolean consume;
+        private MailetContext mailetContext;
+        private Log log;
+
+        public Builder consume(boolean consume) {
+            this.consume = consume;
+            return this;
+        }
+
+        public Builder mailStorer(MailStorer mailStorer) {
+            this.mailStorer = mailStorer;
+            return this;
+        }
+
+        public Builder log(Log log) {
+            this.log = log;
+            return this;
+        }
+
+        public Builder mailetContext(MailetContext mailetContext) {
+            this.mailetContext = mailetContext;
+            return this;
+        }
+
+        public MailDispatcher build() throws MessagingException {
+            Preconditions.checkNotNull(mailStorer);
+            Preconditions.checkNotNull(log);
+            Preconditions.checkNotNull(mailetContext);
+            return new MailDispatcher(mailStorer, consume, log, mailetContext);
+        }
+
+    }
+
+    private final MailStorer mailStorer;
+    private final boolean consume;
+    private final MailetContext mailetContext;
+    private final Log log;
+
+    private MailDispatcher(MailStorer mailStorer, boolean consume, Log log, 
MailetContext mailetContext) {
+        this.mailStorer = mailStorer;
+        this.consume = consume;
+        this.log = log;
+        this.mailetContext = mailetContext;
+    }
+
+    /**
+     * Delivers a mail to a local mailbox.
+     *
+     * @param mail
+     *            the mail being processed
+     *
+     * @throws MessagingException
+     *             if an error occurs while storing the mail
+     */
+    @SuppressWarnings("unchecked")
+    public void dispatch(Mail mail) throws MessagingException {
+        Collection<MailAddress> recipients = mail.getRecipients();
+        Collection<MailAddress> errors = new Vector<MailAddress>();
+
+        MimeMessage message = mail.getMessage();
+
+        // Set Return-Path and remove all other Return-Path headers from the
+        // message
+        // This only works because there is a placeholder inserted by
+        // MimeMessageWrapper
+        message.setHeader(RFC2822Headers.RETURN_PATH, 
DeliveryUtils.prettyPrint(mail.getSender()));
+
+        List<String> deliveredToHeader = 
Collections.list(message.getMatchingHeaders(new String[] { DELIVERED_TO }));
+        message.removeHeader(DELIVERED_TO);
+
+        for (MailAddress recipient : recipients) {
+            try {
+                // Add qmail's de facto standard Delivered-To header
+                message.addHeader(DELIVERED_TO, recipient.toString());
+                mailStorer.storeMail(mail.getSender(), recipient, mail);
+                message.removeHeader(DELIVERED_TO);
+            } catch (Exception ex) {
+                log.error("Error while storing mail.", ex);
+                errors.add(recipient);
+            }
+        }
+        for (String deliveredTo : deliveredToHeader) {
+            message.addHeader(DELIVERED_TO, deliveredTo);
+        }
+        if (!errors.isEmpty()) {
+            // If there were errors, we redirect the email to the ERROR
+            // processor.
+            // In order for this server to meet the requirements of the SMTP
+            // specification, mails on the ERROR processor must be returned to
+            // the sender. Note that this email doesn't include any details
+            // regarding the details of the failure(s).
+            // In the future we may wish to address this.
+            mailetContext.sendMail(mail.getSender(), errors, 
mail.getMessage(), Mail.ERROR);
+        }
+        if (consume) {
+            // Consume this message
+            mail.setState(Mail.GHOST);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStorer.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStorer.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStorer.java
new file mode 100644
index 0000000..d95ee62
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStorer.java
@@ -0,0 +1,29 @@
+/****************************************************************
+ * 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.transport.mailets.delivery;
+
+import javax.mail.MessagingException;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+public interface MailStorer {
+    void storeMail(MailAddress sender, MailAddress recipient, Mail mail) 
throws MessagingException;
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailStorer.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailStorer.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailStorer.java
new file mode 100644
index 0000000..56736ba
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailStorer.java
@@ -0,0 +1,196 @@
+/****************************************************************
+ * 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.transport.mailets.delivery;
+
+import java.io.IOException;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.logging.Log;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.transport.mailets.jsieve.ActionDispatcher;
+import org.apache.james.transport.mailets.jsieve.ResourceLocator;
+import org.apache.james.transport.mailets.jsieve.SieveMailAdapter;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.jsieve.ConfigurationManager;
+import org.apache.jsieve.SieveConfigurationException;
+import org.apache.jsieve.SieveFactory;
+import org.apache.jsieve.exception.SieveException;
+import org.apache.jsieve.parser.generated.ParseException;
+import org.apache.jsieve.parser.generated.TokenMgrError;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+
+import com.google.common.base.Preconditions;
+
+public class SieveMailStorer implements MailStorer {
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+        private MailetContext mailetContext;
+        private UsersRepository usersRepos;
+        private SievePoster sievePoster;
+        private String folder;
+        private ResourceLocator resourceLocator;
+        private Log log;
+        private MailboxManager mailboxManager;
+
+        public Builder folder(String folder) {
+            this.folder = folder;
+            return this;
+        }
+
+        public Builder usersRepository(UsersRepository usersRepository) {
+            this.usersRepos = usersRepository;
+            return this;
+        }
+
+        public Builder sievePoster(SievePoster sievePoster) {
+            this.sievePoster = sievePoster;
+            return this;
+        }
+
+        public Builder mailetContext(MailetContext mailetContext) {
+            this.mailetContext = mailetContext;
+            return this;
+        }
+
+        public Builder resourceLocator(ResourceLocator resourceLocator) {
+            this.resourceLocator = resourceLocator;
+            return this;
+        }
+
+        public Builder log(Log log) {
+            this.log = log;
+            return this;
+        }
+
+        public Builder mailboxManager(MailboxManager mailboxManager) {
+            this.mailboxManager = mailboxManager;
+            return this;
+        }
+
+        public SieveMailStorer build() throws MessagingException {
+            Preconditions.checkNotNull(mailetContext);
+            Preconditions.checkNotNull(usersRepos);
+            Preconditions.checkNotNull(folder);
+            Preconditions.checkNotNull(resourceLocator);
+            Preconditions.checkNotNull(log);
+            Preconditions.checkNotNull(sievePoster);
+            return new SieveMailStorer(mailetContext, usersRepos, 
mailboxManager, folder, resourceLocator, log);
+        }
+
+
+    }
+
+    private final MailetContext mailetContext;
+    private final UsersRepository usersRepos;
+    private final SievePoster sievePoster;
+    private final String folder;
+    private final ResourceLocator resourceLocator;
+    private final SieveFactory factory;
+    private final ActionDispatcher actionDispatcher;
+    private final Log log;
+
+    public SieveMailStorer(MailetContext mailetContext, UsersRepository 
usersRepos, MailboxManager mailboxManager, String folder,
+                           ResourceLocator resourceLocator, Log log) throws 
MessagingException {
+        this.mailetContext = mailetContext;
+        this.usersRepos = usersRepos;
+        this.sievePoster = new SievePoster(mailboxManager, folder, usersRepos, 
mailetContext);
+        this.folder = folder;
+        this.resourceLocator = resourceLocator;
+        try {
+            final ConfigurationManager configurationManager = new 
ConfigurationManager();
+            configurationManager.setLog(log);
+            factory = configurationManager.build();
+        } catch (SieveConfigurationException e) {
+            throw new MessagingException("Failed to load standard Sieve 
configuration.", e);
+        }
+        this.actionDispatcher = new ActionDispatcher();
+        this.log = log;
+    }
+
+    public void storeMail(MailAddress sender, MailAddress recipient, Mail 
mail) throws MessagingException {
+        Preconditions.checkNotNull(recipient, "Recipient for mail to be 
spooled cannot be null.");
+        Preconditions.checkNotNull(mail.getMessage(), "Mail message to be 
spooled cannot be null.");
+
+        sieveMessage(recipient, mail, log);
+        // If no exception was thrown the message was successfully stored in 
the mailbox
+        log.info("Local delivered mail " + mail.getName() + " sucessfully from 
" + DeliveryUtils.prettyPrint(sender) + " to " + 
DeliveryUtils.prettyPrint(recipient)
+            + " in folder " + this.folder);
+    }
+
+    protected void sieveMessage(MailAddress recipient, Mail aMail, Log log) 
throws MessagingException {
+        String username = DeliveryUtils.getUsername(recipient, usersRepos, 
log);
+        try {
+            final ResourceLocator.UserSieveInformation userSieveInformation = 
resourceLocator.get(getScriptUri(recipient, log));
+            sieveMessageEvaluate(recipient, aMail, userSieveInformation, log);
+        } catch (Exception ex) {
+            // SIEVE is a mail filtering protocol.
+            // Rejecting the mail because it cannot be filtered
+            // seems very unfriendly.
+            // So just log and store in INBOX
+            log.error("Cannot evaluate Sieve script. Storing mail in user 
INBOX.", ex);
+            storeMessageInbox(username, aMail.getMessage());
+        }
+    }
+
+    private void sieveMessageEvaluate(MailAddress recipient, Mail aMail, 
ResourceLocator.UserSieveInformation userSieveInformation, Log log) throws 
MessagingException, IOException {
+        try {
+            SieveMailAdapter aMailAdapter = new SieveMailAdapter(aMail,
+                mailetContext, actionDispatcher, sievePoster, 
userSieveInformation.getScriptActivationDate(),
+                userSieveInformation.getScriptInterpretationDate(), recipient);
+            aMailAdapter.setLog(log);
+            // This logging operation is potentially costly
+            log.debug("Evaluating " + aMailAdapter.toString() + "against \"" + 
getScriptUri(recipient, log) + "\"");
+            factory.evaluate(aMailAdapter, 
factory.parse(userSieveInformation.getScriptContent()));
+        } catch (SieveException ex) {
+            handleFailure(recipient, aMail, ex, log);
+        }
+        catch (ParseException ex) {
+            handleFailure(recipient, aMail, ex, log);
+        }
+        catch (TokenMgrError ex) {
+            handleFailure(recipient, aMail, new SieveException(ex), log);
+        }
+    }
+
+    protected String getScriptUri(MailAddress m, Log log) {
+        return "//" + DeliveryUtils.getUsername(m, usersRepos, log) + "/sieve";
+    }
+
+    protected void handleFailure(MailAddress recipient, Mail aMail, Exception 
ex, Log log) throws MessagingException, IOException {
+        String user = DeliveryUtils.getUsername(recipient, usersRepos, log);
+        storeMessageInbox(user, 
SieveFailureMessageComposer.composeMessage(aMail, ex, user));
+    }
+
+    protected void storeMessageInbox(String username, MimeMessage message) 
throws MessagingException {
+        sievePoster.post("mailbox://" + username + "/", message);
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailet.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailet.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailet.java
deleted file mode 100644
index 5a6ea59..0000000
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SieveMailet.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/****************************************************************
- * 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.transport.mailets.delivery;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Vector;
-
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeMessage;
-
-import org.apache.commons.logging.Log;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.transport.mailets.jsieve.ActionDispatcher;
-import org.apache.james.transport.mailets.jsieve.CommonsLoggingAdapter;
-import org.apache.james.transport.mailets.jsieve.ResourceLocator;
-import org.apache.james.transport.mailets.jsieve.SieveMailAdapter;
-import org.apache.james.user.api.UsersRepository;
-import org.apache.james.user.api.UsersRepositoryException;
-import org.apache.jsieve.ConfigurationManager;
-import org.apache.jsieve.SieveConfigurationException;
-import org.apache.jsieve.SieveFactory;
-import org.apache.jsieve.exception.SieveException;
-import org.apache.jsieve.parser.generated.ParseException;
-import org.apache.jsieve.parser.generated.TokenMgrError;
-import org.apache.mailet.Mail;
-import org.apache.mailet.MailAddress;
-import org.apache.mailet.MailetContext;
-import org.apache.mailet.MailetException;
-import org.apache.mailet.base.GenericMailet;
-import org.apache.mailet.base.RFC2822Headers;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
-/**
- * Contains resource bindings.
- */
-public class SieveMailet  extends GenericMailet {
-
-    public static final String DELIVERED_TO = "Delivered-To";
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static class Builder {
-        private UsersRepository usersRepos;
-        private MailboxManager mailboxManager;
-        private String folder;
-        private ResourceLocator resourceLocator;
-        private MailetContext mailetContext;
-        private boolean consume;
-        private Optional<Boolean> verbose = Optional.absent();
-        private Optional<Boolean> quiet = Optional.absent();
-
-        public Builder userRepository(UsersRepository usersRepository) {
-            this.usersRepos = usersRepository;
-            return this;
-        }
-
-        public Builder mailboxManager(MailboxManager mailboxManager) {
-            this.mailboxManager = mailboxManager;
-            return this;
-        }
-
-        public Builder folder(String folder) {
-            this.folder = folder;
-            return this;
-        }
-
-        public Builder resourceLocator(ResourceLocator resourceLocator) {
-            this.resourceLocator = resourceLocator;
-            return this;
-        }
-
-        public Builder verbose(boolean verbose) {
-            this.verbose = Optional.of(verbose);
-            return this;
-        }
-
-        public Builder consume(boolean consume) {
-            this.consume = consume;
-            return this;
-        }
-
-        public Builder quiet(boolean quiet) {
-            this.quiet = Optional.of(quiet);
-            return this;
-        }
-
-        public Builder mailetContext(MailetContext mailetContext) {
-            this.mailetContext = mailetContext;
-            return this;
-        }
-
-        public SieveMailet build() throws MessagingException {
-            if (resourceLocator == null) {
-                throw new MailetException("Not initialised. Please ensure that 
the mailet container supports either setter or constructor injection");
-            }
-            return new SieveMailet(usersRepos, mailboxManager, 
resourceLocator, mailetContext, folder, consume, verbose.or(false), 
quiet.or(false));
-        }
-
-    }
-
-    private final UsersRepository usersRepos;
-    private final SievePoster sievePoster;
-    private final String folder;
-    private final ResourceLocator resourceLocator;
-    private final boolean isInfo;
-    private final boolean verbose;
-    private final boolean consume;
-    private final SieveFactory factory;
-    private final ActionDispatcher actionDispatcher;
-    private final Log log;
-
-    private SieveMailet(UsersRepository usersRepos, MailboxManager 
mailboxManager, ResourceLocator resourceLocator, MailetContext mailetContext, 
String folder,
-                        boolean consume, boolean verbose, boolean quiet) 
throws MessagingException {
-        this.sievePoster = new SievePoster(mailboxManager, folder, usersRepos, 
mailetContext);
-        this.usersRepos = usersRepos;
-        this.resourceLocator = resourceLocator;
-        this.folder = folder;
-        this.actionDispatcher = new ActionDispatcher();
-        this.consume = consume;
-        this.isInfo = verbose || !quiet;
-        this.verbose = verbose;
-        this.log = new CommonsLoggingAdapter(this, computeLogLevel(quiet, 
verbose));
-        try {
-            final ConfigurationManager configurationManager = new 
ConfigurationManager();
-            configurationManager.setLog(log);
-            factory = configurationManager.build();
-        } catch (SieveConfigurationException e) {
-            throw new MessagingException("Failed to load standard Sieve 
configuration.", e);
-        }
-    }
-
-    private int computeLogLevel(boolean quiet, boolean verbose) {
-        if (verbose) {
-            return CommonsLoggingAdapter.TRACE;
-        } else if (quiet) {
-            return CommonsLoggingAdapter.FATAL;
-        } else {
-            return CommonsLoggingAdapter.WARN;
-        }
-    }
-
-    protected String getUsername(MailAddress m) {
-        try {
-            if (usersRepos.supportVirtualHosting()) {
-                return m.toString();
-            } else {
-                return m.getLocalPart() + "@localhost";
-            }
-        } catch (UsersRepositoryException e) {
-            log.error("Unable to access UsersRepository", e);
-            return m.getLocalPart() + "@localhost";
-
-        }
-    }
-
-    public void storeMail(MailAddress sender, MailAddress recipient, Mail 
mail) throws MessagingException {
-        Preconditions.checkNotNull(recipient, "Recipient for mail to be 
spooled cannot be null.");
-        Preconditions.checkNotNull(mail.getMessage(), "Mail message to be 
spooled cannot be null.");
-
-        sieveMessage(recipient, mail);
-        // If no exception was thrown the message was successfully stored in 
the mailbox
-        log.info("Local delivered mail " + mail.getName() + " sucessfully from 
" + prettyPrint(sender) + " to " + prettyPrint(recipient)
-                + " in folder " + this.folder);
-    }
-
-    private String prettyPrint(MailAddress mailAddress) {
-        if (mailAddress != null) {
-            return  "<" + mailAddress.toString() + ">";
-        } else {
-            return  "<>";
-        }
-    }
-
-
-    /**
-     * Delivers a mail to a local mailbox.
-     *
-     * @param mail
-     *            the mail being processed
-     *
-     * @throws MessagingException
-     *             if an error occurs while storing the mail
-     */
-    @SuppressWarnings("unchecked")
-    @Override
-    public void service(Mail mail) throws MessagingException {
-        Collection<MailAddress> recipients = mail.getRecipients();
-        Collection<MailAddress> errors = new Vector<MailAddress>();
-
-        MimeMessage message = mail.getMessage();
-
-        // Set Return-Path and remove all other Return-Path headers from the
-        // message
-        // This only works because there is a placeholder inserted by
-        // MimeMessageWrapper
-        message.setHeader(RFC2822Headers.RETURN_PATH, 
prettyPrint(mail.getSender()));
-
-        List<String> deliveredToHeader = 
Collections.list(message.getMatchingHeaders(new String[] { DELIVERED_TO }));
-        message.removeHeader(DELIVERED_TO);
-
-        for (MailAddress recipient : recipients) {
-            try {
-                // Add qmail's de facto standard Delivered-To header
-                message.addHeader(DELIVERED_TO, recipient.toString());
-                storeMail(mail.getSender(), recipient, mail);
-                message.removeHeader(DELIVERED_TO);
-            } catch (Exception ex) {
-                log.error("Error while storing mail.", ex);
-                errors.add(recipient);
-            }
-        }
-        for (String deliveredTo : deliveredToHeader) {
-            message.addHeader(DELIVERED_TO, deliveredTo);
-        }
-        if (!errors.isEmpty()) {
-            // If there were errors, we redirect the email to the ERROR
-            // processor.
-            // In order for this server to meet the requirements of the SMTP
-            // specification, mails on the ERROR processor must be returned to
-            // the sender. Note that this email doesn't include any details
-            // regarding the details of the failure(s).
-            // In the future we may wish to address this.
-            getMailetContext().sendMail(mail.getSender(), errors, 
mail.getMessage(), Mail.ERROR);
-        }
-        if (consume) {
-            // Consume this message
-            mail.setState(Mail.GHOST);
-        }
-    }
-
-    protected void sieveMessage(MailAddress recipient, Mail aMail) throws 
MessagingException {
-        String username = getUsername(recipient);
-        try {
-            final ResourceLocator.UserSieveInformation userSieveInformation = 
resourceLocator.get(getScriptUri(recipient));
-            sieveMessageEvaluate(recipient, aMail, userSieveInformation);
-        } catch (Exception ex) {
-            // SIEVE is a mail filtering protocol.
-            // Rejecting the mail because it cannot be filtered
-            // seems very unfriendly.
-            // So just log and store in INBOX
-            if (isInfo) {
-                log.error("Cannot evaluate Sieve script. Storing mail in user 
INBOX.", ex);
-            }
-            storeMessageInbox(username, aMail.getMessage());
-        }
-    }
-
-    private void sieveMessageEvaluate(MailAddress recipient, Mail aMail, 
ResourceLocator.UserSieveInformation userSieveInformation) throws 
MessagingException, IOException {
-        try {
-            SieveMailAdapter aMailAdapter = new SieveMailAdapter(aMail,
-                getMailetContext(), actionDispatcher, sievePoster, 
userSieveInformation.getScriptActivationDate(),
-                userSieveInformation.getScriptInterpretationDate(), recipient);
-            aMailAdapter.setLog(log);
-            // This logging operation is potentially costly
-            if (verbose) {
-                log.error("Evaluating " + aMailAdapter.toString() + "against 
\""
-                    + getScriptUri(recipient) + "\"");
-            }
-            factory.evaluate(aMailAdapter, 
factory.parse(userSieveInformation.getScriptContent()));
-        } catch (SieveException ex) {
-            handleFailure(recipient, aMail, ex);
-        }
-        catch (ParseException ex) {
-            handleFailure(recipient, aMail, ex);
-        }
-        catch (TokenMgrError ex)
-        {
-            handleFailure(recipient, aMail, new SieveException(ex));
-        }
-    }
-
-    protected void storeMessageInbox(String username, MimeMessage message) 
throws MessagingException {
-        sievePoster.post("mailbox://" + username + "/", message);
-    }
-
-
-    /**
-     * Return the URI for the sieve script
-     *
-     * @param m
-     * @return
-     */
-    protected String getScriptUri(MailAddress m) {
-        return "//" + getUsername(m) + "/sieve";
-    }
-
-    protected void handleFailure(MailAddress recipient, Mail aMail, Exception 
ex) throws MessagingException, IOException {
-        String user = getUsername(recipient);
-        storeMessageInbox(user, 
SieveFailureMessageComposer.composeMessage(aMail, ex, user));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/ToRecipientFolder.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/ToRecipientFolder.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/ToRecipientFolder.java
index c26b3f1..e791d9a 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/ToRecipientFolder.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/ToRecipientFolder.java
@@ -18,21 +18,19 @@
  ****************************************************************/
 package org.apache.james.transport.mailets.delivery;
 
-import com.google.common.collect.Iterators;
+import org.apache.commons.logging.Log;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.sieverepository.api.SieveRepository;
 import org.apache.james.transport.mailets.ResourceLocatorImpl;
+import org.apache.james.transport.mailets.jsieve.CommonsLoggingAdapter;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailetConfig;
-import org.apache.mailet.MailetContext;
 import org.apache.mailet.base.GenericMailet;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.mail.MessagingException;
-import java.util.Arrays;
-import java.util.Iterator;
 
 /**
  * Receives a Mail from the Queue and takes care to deliver the message
@@ -74,34 +72,37 @@ public class ToRecipientFolder extends GenericMailet {
         this.usersRepository = usersRepository;
     }
 
-    private SieveMailet sieveMailet;  // Mailet that actually stores the 
message
+    private MailDispatcher mailDispatcher;
 
-    /**
-     * Delivers a mail to a local mailbox in a given folder.
-     * 
-     * @see 
org.apache.mailet.base.GenericMailet#service(org.apache.mailet.Mail)
-     */
     @Override
     public void service(Mail mail) throws MessagingException {
         if (!mail.getState().equals(Mail.GHOST)) {
-            sieveMailet.service(mail);
+            mailDispatcher.dispatch(mail);
         }
     }
 
     @Override
-    public void init(MailetConfig mailetConfig) throws MessagingException {
-        super.init(mailetConfig);
-        sieveMailet = SieveMailet.builder()
-            .mailboxManager(mailboxManager)
-            .userRepository(usersRepository)
-            .resourceLocator(ResourceLocatorImpl.instanciate(usersRepository, 
sieveRepository))
-            .mailetContext(getMailetContext())
-            .folder(getInitParameter(FOLDER_PARAMETER, "INBOX"))
-            .consume(getInitParameter(CONSUME_PARAMETER, false))
-            .verbose(getInitParameter("verbose", false))
+    public void init() throws MessagingException {
+        Log log = CommonsLoggingAdapter.builder()
+            .mailet(this)
             .quiet(getInitParameter("quiet", true))
+            .verbose(getInitParameter("verbose", false))
+            .build();
+        String folder = getInitParameter(FOLDER_PARAMETER, "INBOX");
+        mailDispatcher = MailDispatcher.builder()
+            .mailStorer(SieveMailStorer.builder()
+                .sievePoster(new SievePoster(mailboxManager, folder, 
usersRepository, getMailetContext()))
+                .usersRepository(usersRepository)
+                
.resourceLocator(ResourceLocatorImpl.instanciate(usersRepository, 
sieveRepository))
+                .mailetContext(getMailetContext())
+                .mailboxManager(mailboxManager)
+                .folder(folder)
+                .log(log)
+                .build())
+            .consume(getInitParameter(CONSUME_PARAMETER, false))
+            .mailetContext(getMailetContext())
+            .log(log)
             .build();
-        sieveMailet.init(mailetConfig);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/CommonsLoggingAdapter.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/CommonsLoggingAdapter.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/CommonsLoggingAdapter.java
index 66628af..20845ef 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/CommonsLoggingAdapter.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/CommonsLoggingAdapter.java
@@ -21,11 +21,54 @@ package org.apache.james.transport.mailets.jsieve;
 import org.apache.commons.logging.Log;
 import org.apache.mailet.base.GenericMailet;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
 /**
  * Adapts commons logging to mailet logging.
  */
 public class CommonsLoggingAdapter implements Log {
-    
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+        private Optional<Boolean> verbose = Optional.absent();
+        private Optional<Boolean> quiet = Optional.absent();
+        private GenericMailet genericMailet;
+
+        public Builder mailet(GenericMailet genericMailet) {
+            this.genericMailet = genericMailet;
+            return this;
+        }
+
+        public Builder verbose(boolean verbose) {
+            this.verbose = Optional.of(verbose);
+            return this;
+        }
+
+        public Builder quiet(boolean quiet) {
+            this.quiet = Optional.of(quiet);
+            return this;
+        }
+
+        public CommonsLoggingAdapter build() {
+            Preconditions.checkNotNull(genericMailet);
+            return new CommonsLoggingAdapter(genericMailet, 
computeLogLevel(quiet.or(false), verbose.or(false)));
+        }
+
+        private int computeLogLevel(boolean quiet, boolean verbose) {
+            if (verbose) {
+                return CommonsLoggingAdapter.TRACE;
+            } else if (quiet) {
+                return CommonsLoggingAdapter.FATAL;
+            } else {
+                return CommonsLoggingAdapter.WARN;
+            }
+        }
+    }
+
     public static final int TRACE = 6;
     public static final int DEBUG = 5;
     public static final int INFO = 4;

http://git-wip-us.apache.org/repos/asf/james-project/blob/ca85c529/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/DeliveryUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/DeliveryUtilsTest.java
 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/DeliveryUtilsTest.java
new file mode 100644
index 0000000..287f771
--- /dev/null
+++ 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/DeliveryUtilsTest.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.delivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.mailet.base.MailAddressFixture;
+import org.junit.Test;
+
+public class DeliveryUtilsTest {
+
+    @Test
+    public void prettyPrintShouldDisplayNullAddresses() {
+        assertThat(DeliveryUtils.prettyPrint(null)).isEqualTo("<>");
+    }
+
+    @Test
+    public void prettyPrintShouldDisplayAddresses() {
+        assertThat(DeliveryUtils.prettyPrint(MailAddressFixture.ANY_AT_JAMES))
+            .isEqualTo("<" + MailAddressFixture.ANY_AT_JAMES + ">");
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to