james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From adup...@apache.org
Subject [4/5] james-project git commit: JAMES-1830 jsieve/mailets should be located in server/mailet/.../jsieve
Date Wed, 05 Oct 2016 06:36:00 GMT
JAMES-1830 jsieve/mailets should be located in server/mailet/.../jsieve


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

Branch: refs/heads/master
Commit: 0501c99a94dc8ddfdd691bf098135d8c02462c3c
Parents: 2fda869
Author: Benoit Tellier <btellier@linagora.com>
Authored: Mon Oct 3 16:51:37 2016 +0200
Committer: Benoit Tellier <btellier@linagora.com>
Committed: Tue Oct 4 15:57:27 2016 +0200

----------------------------------------------------------------------
 server/mailet/mailets/pom.xml                   |  18 +-
 .../james/transport/mailets/SieveMailet.java    |   6 +-
 .../transport/mailets/jsieve/ActionContext.java |  81 +++
 .../mailets/jsieve/ActionDispatcher.java        | 103 ++++
 .../transport/mailets/jsieve/ActionUtils.java   |  80 +++
 .../mailets/jsieve/CommonsLoggingAdapter.java   | 142 ++++++
 .../mailets/jsieve/FileIntoAction.java          | 142 ++++++
 .../transport/mailets/jsieve/KeepAction.java    |  65 +++
 .../transport/mailets/jsieve/MailAction.java    |  40 ++
 .../james/transport/mailets/jsieve/Poster.java  |  40 ++
 .../mailets/jsieve/RedirectAction.java          |  72 +++
 .../transport/mailets/jsieve/RejectAction.java  | 144 ++++++
 .../mailets/jsieve/ResourceLocator.java         |  92 ++++
 .../mailets/jsieve/SieveMailAdapter.java        | 504 +++++++++++++++++++
 .../mailets/jsieve/SieveMailboxMailet.java      | 464 +++++++++++++++++
 .../mailets/jsieve/VacationAction.java          | 100 ++++
 .../transport/mailets/jsieve/VacationReply.java | 169 +++++++
 .../mailets/jsieve/mdn/ActionModeAutomatic.java |  42 ++
 .../mailets/jsieve/mdn/ActionModeManual.java    |  47 ++
 .../mailets/jsieve/mdn/Disposition.java         | 176 +++++++
 .../jsieve/mdn/DispositionActionMode.java       |  29 ++
 .../mailets/jsieve/mdn/DispositionModifier.java |  29 ++
 .../jsieve/mdn/DispositionSendingMode.java      |  29 ++
 .../mailets/jsieve/mdn/DispositionType.java     |  29 ++
 .../mailets/jsieve/mdn/MDNFactory.java          | 115 +++++
 .../mailets/jsieve/mdn/ModifierError.java       |  44 ++
 .../mailets/jsieve/mdn/ModifierExpired.java     |  45 ++
 .../mailets/jsieve/mdn/ModifierFailed.java      |  45 ++
 .../jsieve/mdn/ModifierMailboxTerminated.java   |  45 ++
 .../mailets/jsieve/mdn/ModifierSuperseded.java  |  45 ++
 .../mailets/jsieve/mdn/ModifierWarning.java     |  45 ++
 .../jsieve/mdn/SendingModeAutomatic.java        |  45 ++
 .../mailets/jsieve/mdn/SendingModeManual.java   |  45 ++
 .../mailets/jsieve/mdn/TypeDeleted.java         |  45 ++
 .../mailets/jsieve/mdn/TypeDenied.java          |  44 ++
 .../mailets/jsieve/mdn/TypeDispatched.java      |  43 ++
 .../mailets/jsieve/mdn/TypeDisplayed.java       |  44 ++
 .../mailets/jsieve/mdn/TypeFailed.java          |  44 ++
 .../mailets/jsieve/mdn/TypeProcessed.java       |  44 ++
 .../mailets/delivery/SieveMailetTest.java       |   2 +-
 server/pom.xml                                  |  17 +-
 41 files changed, 3377 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/pom.xml
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/pom.xml b/server/mailet/mailets/pom.xml
index 78d7f12..ee2dc9d 100644
--- a/server/mailet/mailets/pom.xml
+++ b/server/mailet/mailets/pom.xml
@@ -36,6 +36,14 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.james</groupId>
+            <artifactId>apache-mime4j-dom</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>apache-mime4j-james-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
@@ -72,16 +80,6 @@
             <artifactId>james-server-core</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.apache.james</groupId>
-            <artifactId>apache-jsieve-mailet</artifactId>
-            <!--             <exclusions> -->
-            <!--                 <exclusion> -->
-            <!--                     <artifactId>apache-mime4j</artifactId> -->
-            <!--                     <groupId>org.apache.james</groupId> -->
-            <!--                 </exclusion> -->
-            <!--             </exclusions> -->
-        </dependency>
-        <dependency>
             <groupId>org.apache.james.protocols</groupId>
             <artifactId>protocols-managesieve</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SieveMailet.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SieveMailet.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SieveMailet.java
index dcf6f26..2526fc8 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SieveMailet.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SieveMailet.java
@@ -31,12 +31,12 @@ import org.apache.james.mailbox.exception.BadCredentialsException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.transport.mailets.jsieve.Poster;
+import org.apache.james.transport.mailets.jsieve.ResourceLocator;
+import org.apache.james.transport.mailets.jsieve.SieveMailboxMailet;
 import org.apache.james.transport.util.MailetContextLog;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
-import org.apache.jsieve.mailet.Poster;
-import org.apache.jsieve.mailet.ResourceLocator;
-import org.apache.jsieve.mailet.SieveMailboxMailet;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
 import org.apache.mailet.MailetConfig;

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionContext.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionContext.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionContext.java
new file mode 100644
index 0000000..0f78115
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionContext.java
@@ -0,0 +1,81 @@
+/****************************************************************
+ * 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.jsieve;
+
+import java.util.Collection;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.logging.Log;
+import org.apache.mailet.MailAddress;
+import org.joda.time.DateTime;
+
+/**
+ * Provides context for action execution.
+ */
+public interface ActionContext {
+
+    /**
+     * @return Date the script was activated
+     */
+    DateTime getScriptActivationDate();
+
+    /**
+     * @return Date the script is currently interpreted
+     */
+    DateTime getScriptInterpretationDate();
+
+    /**
+     * @return Recipient receiving the given eMail
+     */
+    MailAddress getRecipient();
+
+    /**
+     * Gets the log.
+     * @return not null
+     */
+    public Log getLog();
+    
+    /**
+     * Experimental mail delivery. 
+     * POST verb indicate that mail should be attached to the collection
+     * indicated by the given URI.
+     * 
+     * @param uri indicates the destination to which the mail to added. ATM 
+     * the value should be mailbox://<user>@localhost/<mailbox-path>
+     * @param mail not null
+     */
+    public void post(String uri, MimeMessage mail) throws MessagingException;
+
+    /**
+     * Posts the given mail.
+     * @param sender possibly null
+     * @param recipients not null
+     * @param mail not null
+     * @throws MessagingException when mail cannot be posted
+     */
+    public void post(MailAddress sender, Collection<MailAddress> recipients, MimeMessage mail) throws MessagingException;
+
+    /**
+     * Gets name (including version) of this server.
+     * @return not nul
+     */
+    public String getServerInfo();
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionDispatcher.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionDispatcher.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionDispatcher.java
new file mode 100644
index 0000000..61f8953
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionDispatcher.java
@@ -0,0 +1,103 @@
+/****************************************************************
+ * 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.jsieve;
+
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionFileInto;
+import org.apache.jsieve.mail.ActionKeep;
+import org.apache.jsieve.mail.ActionRedirect;
+import org.apache.jsieve.mail.ActionReject;
+import org.apache.jsieve.mail.optional.ActionVacation;
+import org.apache.mailet.Mail;
+
+import javax.mail.MessagingException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Dynamically dispatches an Action depending on the type of Action received at runtime.
+ * <h4>Thread Safety</h4>
+ * <p>An instance maybe safe accessed concurrently by multiple threads.</p>
+ */
+public class ActionDispatcher {
+    /**
+     * A Map keyed by the type of Action. The values are the methods to invoke to
+     * handle the Action.
+     * <Action, MailAction>
+     */
+    private ConcurrentMap<Class, MailAction> fieldMailActionMap;
+
+    /**
+     * Constructor for ActionDispatcher.
+     *
+     * @throws NoSuchMethodException
+     */
+    public ActionDispatcher() {
+        super();
+        setMethodMap(defaultMethodMap());
+    }
+
+    /**
+     * Method execute executes the passed Action by invoking the method mapped by the
+     * receiver with a parameter of the EXACT type of Action.
+     *
+     * @param anAction not null
+     * @param aMail    not null
+     * @param context  not null
+     * @throws MessagingException
+     */
+    public void execute(final Action anAction, final Mail aMail, final ActionContext context) throws MessagingException {
+        final MailAction mailAction = getMethodMap().get(anAction.getClass());
+        mailAction.execute(anAction, aMail, context);
+    }
+
+    /**
+     * Returns the methodMap.
+     *
+     * @return Map
+     */
+    public ConcurrentMap<Class, MailAction> getMethodMap() {
+        return fieldMailActionMap;
+    }
+
+    /**
+     * Returns a new methodMap.
+     *
+     * @return Map
+     */
+    private ConcurrentMap<Class, MailAction> defaultMethodMap() {
+        final ConcurrentMap<Class, MailAction> actionMap = new ConcurrentHashMap<Class, MailAction>(4);
+        actionMap.put(ActionFileInto.class, new FileIntoAction());
+        actionMap.put(ActionKeep.class, new KeepAction());
+        actionMap.put(ActionRedirect.class, new RedirectAction());
+        actionMap.put(ActionReject.class, new RejectAction());
+        actionMap.put(ActionVacation.class, new VacationAction());
+        return actionMap;
+    }
+
+    /**
+     * Sets the mail action mail.
+     *
+     * @param mailActionMap <Action, MailAction> not null
+     */
+    protected void setMethodMap(ConcurrentMap<Class, MailAction> mailActionMap) {
+        fieldMailActionMap = mailActionMap;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionUtils.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionUtils.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionUtils.java
new file mode 100644
index 0000000..3645e8c
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ActionUtils.java
@@ -0,0 +1,80 @@
+/****************************************************************
+ * 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.jsieve;
+
+import javax.mail.MessagingException;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+/**
+ * Utility methods helpful for actions.
+ */
+public class ActionUtils
+{
+    
+    private final static String ATTRIBUTE_PREFIX = ActionUtils.class.getPackage().getName() + ".";
+
+    /**
+     * Answers the sole intended recipient for aMail.
+     * 
+     * @param aMail
+     * @return String
+     * @throws MessagingException
+     */
+    public static MailAddress getSoleRecipient(Mail aMail) throws MessagingException
+    {
+        if (aMail.getRecipients() == null) {
+            throw new MessagingException("Invalid number of recipients - 0"
+                    + ". Exactly 1 recipient is expected.");
+        } else if (1 != aMail.getRecipients().size())
+            throw new MessagingException("Invalid number of recipients - "
+                    + Integer.toString(aMail.getRecipients().size())
+                    + ". Exactly 1 recipient is expected.");
+        return aMail.getRecipients().iterator().next();
+    }
+
+    /**
+     * Detect and handle locally looping mail. External loop detection is left
+     * to the MTA.
+     * 
+     * @param aMail
+     * @param context not null
+     * @param anAttributeSuffix
+     * @throws MessagingException
+     */
+    public static void detectAndHandleLocalLooping(Mail aMail, ActionContext context, String anAttributeSuffix)
+            throws MessagingException
+    {
+        MailAddress thisRecipient = getSoleRecipient(aMail);
+        MailAddress lastRecipient = (MailAddress) aMail
+                .getAttribute(ATTRIBUTE_PREFIX + anAttributeSuffix);
+        if (null != lastRecipient && lastRecipient.equals(thisRecipient))
+        {
+            MessagingException ex = new MessagingException(
+                    "This message is looping! Message ID: "
+                            + aMail.getMessage().getMessageID());
+            context.getLog().warn(ex.getMessage(), ex);
+            throw ex;
+        }
+        aMail.setAttribute(ATTRIBUTE_PREFIX + anAttributeSuffix,
+                thisRecipient);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/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
new file mode 100644
index 0000000..9999a99
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/CommonsLoggingAdapter.java
@@ -0,0 +1,142 @@
+/****************************************************************
+ * 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.jsieve;
+
+import org.apache.commons.logging.Log;
+import org.apache.mailet.base.GenericMailet;
+
+/**
+ * Adapts commons logging to mailet logging.
+ */
+class CommonsLoggingAdapter implements Log {
+    
+    public static final int TRACE = 6;
+    public static final int DEBUG = 5;
+    public static final int INFO = 4;
+    public static final int WARN = 3;
+    public static final int ERROR = 2;
+    public static final int FATAL = 1;
+    
+    private final GenericMailet mailet;
+    private final int level;
+    
+    public CommonsLoggingAdapter(final GenericMailet mailet, final int level) {
+        super();
+        this.mailet = mailet;
+        this.level = level;
+    }
+
+    public void debug(Object message) {
+        if (isDebugEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString());
+        }
+    }
+
+    public void debug(Object message, Throwable t) {
+        if (isDebugEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString(), t);
+        } 
+    }
+
+    public void error(Object message) {
+        if (isErrorEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString());
+        }
+    }
+
+    public void error(Object message, Throwable t) {
+        if (isErrorEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString(), t);
+        }
+    }
+
+    public void fatal(Object message) {
+        if (isFatalEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString());
+        }
+    }
+
+    public void fatal(Object message, Throwable t) {
+        if (isFatalEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString(), t);
+        }
+    }
+
+    public void info(Object message) {
+        if (isInfoEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString());
+        }
+    }
+
+    public void info(Object message, Throwable t) {
+        if (isInfoEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString(), t);
+        }
+    }
+
+    public boolean isDebugEnabled() {
+        return level <= DEBUG;
+    }
+
+    public boolean isErrorEnabled() {
+        return level <= ERROR;
+    }
+
+    public boolean isFatalEnabled() {
+        return level <= FATAL;
+    }
+
+    public boolean isInfoEnabled() {
+        return level <= INFO;
+    }
+
+    public boolean isTraceEnabled() {
+        return level <= TRACE;
+    }
+
+    public boolean isWarnEnabled() {
+        return level <= WARN;
+    }
+
+    public void trace(Object message) {
+        if (isTraceEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString());
+        }
+    }
+
+    public void trace(Object message, Throwable t) {
+        if (isTraceEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString(), t);
+        }
+    }
+
+    public void warn(Object message) {
+        if (isWarnEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString());
+        }
+    }
+
+    public void warn(Object message, Throwable t) {
+        if (isWarnEnabled()) {
+            mailet.log(message == null ? "NULL" : message.toString(), t);
+        }
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/FileIntoAction.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/FileIntoAction.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/FileIntoAction.java
new file mode 100644
index 0000000..3d2bcd7
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/FileIntoAction.java
@@ -0,0 +1,142 @@
+/****************************************************************
+ * 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.jsieve;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.logging.Log;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionFileInto;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+/**
+ * Performs the filing of a mail into a specified destination. 
+ * <h4>Thread Safety</h4>
+ * <p>An instance maybe safe accessed concurrently by multiple threads.</p>
+ */
+public class FileIntoAction implements MailAction {
+    
+    private static final char HIERARCHY_DELIMITER = '.';
+
+    public void execute(Action action, Mail mail, ActionContext context) throws MessagingException {
+        if (action instanceof ActionFileInto) {
+            final ActionFileInto fileIntoAction = (ActionFileInto) action;
+            execute(fileIntoAction, mail, context);
+        }
+    }
+
+    /**
+     * <p>
+     * Executes the passed ActionFileInto.
+     * </p>
+     * 
+     * <p>
+     * This implementation accepts any destination with the root of <code>INBOX</code>.
+     * </p>
+     * 
+     * <p>
+     * As the current POP3 server does not support sub-folders, the mail is
+     * stored in the INBOX for the recipient of the mail and the full intended
+     * destination added as a prefix to the message's subject.
+     * </p>
+     * 
+     * <p>
+     * When IMAP support is added to James, it will be possible to support
+     * sub-folders of <code>INBOX</code> fully.
+     * </p>
+     * 
+     * @param anAction
+     * @param aMail
+     * @param context not null
+     * @throws MessagingException
+     */
+    @SuppressWarnings("deprecation")
+    public void execute(ActionFileInto anAction, Mail aMail, final ActionContext context) throws MessagingException
+    {
+        String destinationMailbox = anAction.getDestination();
+        MailAddress recipient;
+        boolean delivered = false;
+        try
+        {
+            recipient = ActionUtils.getSoleRecipient(aMail);
+            MimeMessage localMessage = createMimeMessage(aMail, recipient);
+            
+            if (!(destinationMailbox.length() > 0 
+                    && destinationMailbox.charAt(0) == HIERARCHY_DELIMITER)) {
+                destinationMailbox =  HIERARCHY_DELIMITER + destinationMailbox;
+            }
+            
+            final String mailbox = destinationMailbox.replace(HIERARCHY_DELIMITER, '/');
+            final String host;
+            if (mailbox.charAt(0) == '/') {
+                host = "@localhost";
+            } else {
+                host = "@localhost/";
+            }
+            final String url = "mailbox://" + recipient.getUser() + host + mailbox;
+            //TODO: copying this message so many times seems a waste
+            context.post(url, localMessage);
+            delivered = true;
+        }
+        catch (MessagingException ex)
+        {
+            final Log log = context.getLog();
+            if (log.isDebugEnabled()) {
+                log.debug("Error while storing mail into. "+destinationMailbox, ex);
+            }
+            throw ex;
+        }
+        finally
+        {
+            // Ensure the mail is always ghosted
+            aMail.setState(Mail.GHOST);
+        }
+        if (delivered)
+        {
+            final Log log = context.getLog();
+            if (log.isDebugEnabled()) {
+                log.debug("Filed Message ID: "
+                    + aMail.getMessage().getMessageID()
+                    + " into destination: \""
+                    + destinationMailbox + "\"");
+            }
+        }
+    }
+    
+    private static MimeMessage createMimeMessage(Mail aMail, MailAddress recipient) throws MessagingException {
+        // Adapted from LocalDelivery Mailet
+        // Add qmail's de facto standard Delivered-To header
+        MimeMessage localMessage = new MimeMessage(aMail.getMessage())
+        {
+            protected void updateHeaders() throws MessagingException
+            {
+                if (getMessageID() == null)
+                    super.updateHeaders();
+                else
+                    modified = false;
+            }
+        };
+        localMessage.addHeader("Delivered-To", recipient.toString());
+
+        localMessage.saveChanges();
+        return localMessage;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/KeepAction.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/KeepAction.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/KeepAction.java
new file mode 100644
index 0000000..03bdfd0
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/KeepAction.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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.jsieve;
+
+import javax.mail.MessagingException;
+
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionFileInto;
+import org.apache.jsieve.mail.ActionKeep;
+import org.apache.mailet.Mail;
+
+/**
+ * Performs the filing of a mail into the inbox. 
+ * <h4>Thread Safety</h4>
+ * <p>An instance maybe safe accessed concurrently by multiple threads.</p>
+ */
+public class KeepAction extends FileIntoAction implements MailAction {
+    
+    private static final String INBOX = "INBOX";
+
+    public void execute(Action action, Mail mail, ActionContext context)
+            throws MessagingException {
+        if (action instanceof ActionKeep) {
+            final ActionKeep actionKeep = (ActionKeep) action;
+            execute(actionKeep, mail, context);
+        }
+    }
+
+    /**
+     * <p>
+     * Executes the passed ActionKeep.
+     * </p>
+     * 
+     * <p>
+     * In this implementation, "keep" is equivalent to "fileinto" with a
+     * destination of "INBOX".
+     * </p>
+     * 
+     * @param anAction not null
+     * @param aMail not null
+     * @param context not null
+     * @throws MessagingException
+     */
+    public void execute(ActionKeep anAction, Mail aMail, ActionContext context) throws MessagingException
+    {
+        final ActionFileInto action = new ActionFileInto(INBOX);
+        execute(action, aMail, context);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/MailAction.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/MailAction.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/MailAction.java
new file mode 100644
index 0000000..78d26f6
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/MailAction.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.jsieve;
+
+import javax.mail.MessagingException;
+
+import org.apache.jsieve.mail.Action;
+import org.apache.mailet.Mail;
+
+/**
+ * Executes a Sieve action.
+ * Implementations may be accessed concurrently by multiple threads.
+ */
+public interface MailAction {
+    
+    /**
+     * Executes the given action.
+     * @param action not null
+     * @param mail not null
+     * @param context not null
+     * @throws MessagingException when action cannot be executed
+     */
+    public void execute(final Action action, final Mail mail, final ActionContext context) throws MessagingException;
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/Poster.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/Poster.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/Poster.java
new file mode 100644
index 0000000..aa77e0b
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/Poster.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.jsieve;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Experimental interface.
+ */
+public interface Poster {
+    
+    /**
+     * Experimental mail delivery. 
+     * POST verb indicate that mail should be attached to the collection
+     * indicated by the given URI.
+     * 
+     * @param uri indicates the destination to which the mail to added. ATM 
+     * the value should be mailbox://<user>@localhost/<mailbox-path>
+     * @param mail not null
+     */
+    public void post(String uri, MimeMessage mail) throws MessagingException;
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RedirectAction.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RedirectAction.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RedirectAction.java
new file mode 100644
index 0000000..c56fe4f
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RedirectAction.java
@@ -0,0 +1,72 @@
+/****************************************************************
+ * 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.jsieve;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.commons.logging.Log;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionRedirect;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+/**
+ * Performs the redirection of a mail. 
+ * <h4>Thread Safety</h4>
+ * <p>An instance maybe safe accessed concurrently by multiple threads.</p>
+ */
+public class RedirectAction implements MailAction {
+
+    public void execute(Action action, Mail mail, ActionContext context)
+            throws MessagingException {
+        if (action instanceof ActionRedirect) {
+            final ActionRedirect actionRedirect = (ActionRedirect) action;
+            execute(actionRedirect, mail, context);
+        }
+
+    }
+
+    /**
+     * Method execute executes the passed ActionRedirect.
+     * 
+     * @param anAction not nul
+     * @param aMail not null
+     * @param context not null
+     * @throws MessagingException
+     */
+    public void execute(ActionRedirect anAction, Mail aMail, ActionContext context) throws MessagingException
+    {
+        ActionUtils.detectAndHandleLocalLooping(aMail, context, "redirect");
+        Collection<MailAddress> recipients = new ArrayList<MailAddress>(1);
+        recipients.add(new MailAddress(new InternetAddress(anAction.getAddress())));
+        MailAddress sender = aMail.getSender();
+        context.post(sender, recipients, aMail.getMessage());
+        aMail.setState(Mail.GHOST);
+        Log log = context.getLog();
+        if (log.isDebugEnabled()) {
+            log.debug("Redirected Message ID: "
+                + aMail.getMessage().getMessageID() + " to \""
+                + anAction.getAddress() + "\"");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
new file mode 100644
index 0000000..f792f82
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
@@ -0,0 +1,144 @@
+/****************************************************************
+ * 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.jsieve;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.mail.Address;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.james.transport.mailets.jsieve.mdn.ActionModeAutomatic;
+import org.apache.james.transport.mailets.jsieve.mdn.Disposition;
+import org.apache.james.transport.mailets.jsieve.mdn.DispositionModifier;
+import org.apache.james.transport.mailets.jsieve.mdn.MDNFactory;
+import org.apache.james.transport.mailets.jsieve.mdn.ModifierError;
+import org.apache.james.transport.mailets.jsieve.mdn.SendingModeAutomatic;
+import org.apache.james.transport.mailets.jsieve.mdn.TypeDeleted;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionReject;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+/**
+ * Performs the rejection of a mail, with a reply to the sender. 
+ * <h4>Thread Safety</h4>
+ * <p>An instance maybe safe accessed concurrently by multiple threads.</p>
+ */
+public class RejectAction implements MailAction {
+
+    public void execute(Action action, Mail mail, ActionContext context)
+            throws MessagingException {
+        if (action instanceof ActionReject) {
+            final ActionReject actionReject = (ActionReject) action;
+            execute(actionReject, mail, context);
+        }
+
+    }
+
+    /**
+     * <p>
+     * Method execute executes the passed ActionReject. It sends an RFC 2098
+     * compliant reject MDN back to the sender.
+     * </p>
+     * <p>
+     * NOTE: The Mimecontent type should be 'report', but as we do not yet have
+     * a DataHandler for this yet, its currently 'text'!
+     * 
+     * @param anAction not null
+     * @param aMail not null
+     * @param context not null
+     * @throws MessagingException
+     */
+    public void execute(ActionReject anAction, Mail aMail, ActionContext context) throws MessagingException
+    {
+        ActionUtils.detectAndHandleLocalLooping(aMail, context, "reject");
+
+        // Create the MDN part
+        StringBuilder humanText = new StringBuilder(128);
+        humanText.append("This message was refused by the recipient's mail filtering program.");
+        humanText.append("\r\n");
+        humanText.append("The reason given was:");
+        humanText.append("\r\n");
+        humanText.append("\r\n");
+        humanText.append(anAction.getMessage());
+
+        String reporting_UA_name = null;
+        try
+        {
+            reporting_UA_name = InetAddress.getLocalHost()
+                    .getCanonicalHostName();
+        }
+        catch (UnknownHostException ex)
+        {
+            reporting_UA_name = "localhost";
+        }
+
+        String reporting_UA_product = context.getServerInfo();
+
+        String[] originalRecipients = aMail.getMessage().getHeader(
+                "Original-Recipient");
+        String original_recipient = null;
+        if (null != originalRecipients && originalRecipients.length > 0)
+        {
+            original_recipient = originalRecipients[0];
+        }
+
+        MailAddress soleRecipient = ActionUtils.getSoleRecipient(aMail);
+        String final_recipient = soleRecipient.toString();
+
+        String original_message_id = aMail.getMessage().getMessageID();
+
+        DispositionModifier modifiers[] = {new ModifierError()};
+        Disposition disposition = new Disposition(new ActionModeAutomatic(),
+                new SendingModeAutomatic(), new TypeDeleted(), modifiers);
+
+        MimeMultipart multiPart = MDNFactory.create(humanText.toString(),
+                reporting_UA_name, reporting_UA_product, original_recipient,
+                final_recipient, original_message_id, disposition);
+
+        // Send the message
+        MimeMessage reply = (MimeMessage) aMail.getMessage().reply(false);
+        reply.setFrom(soleRecipient.toInternetAddress());
+        reply.setContent(multiPart);
+        reply.saveChanges();
+        Address[] recipientAddresses = reply.getAllRecipients();
+        if (null != recipientAddresses)
+        {
+            Collection<MailAddress> recipients = new ArrayList<MailAddress>(recipientAddresses.length);
+            for (Address recipientAddress : recipientAddresses) {
+                recipients.add(new MailAddress(
+                        (InternetAddress) recipientAddress));
+            }
+            context.post(null, recipients, reply);
+        }
+        else
+        {
+            context.getLog().info("Unable to send reject MDN. Could not determine the recipient.");
+        }
+        // Ghost the original mail
+        aMail.setState(Mail.GHOST);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ResourceLocator.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ResourceLocator.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ResourceLocator.java
new file mode 100644
index 0000000..01d38a5
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/ResourceLocator.java
@@ -0,0 +1,92 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.transport.mailets.jsieve;
+
+import org.joda.time.DateTime;
+
+import java.io.InputStream;
+
+/**
+ * <p>Experimental API locates resources. 
+ * Used to load Sieve scripts. The base for relative URLs
+ * should be taken to be the root of the James configuration.
+ * </p><p>
+ * Required schemas:
+ * </p>
+ * <ul>
+ * <li><strong>User sieve scripts</strong> - the relative URL scheme 
+ * <code>//<em>user</em>@<em>host</em>/<em>sieve</em> will be used to
+ * obtain the script
+ * </ul>
+ * <p>
+ * The advantage of using <code>URI</code>s 
+ * and verbs (for example <code>GET</code>, <code>POST</code>)
+ * are their uniformity. The same API can be used to interface radically
+ * different resource types and protocols. This allows concise, minimal,
+ * powerful APIs to be created. Their simplicity is easy to preserved 
+ * across versions. 
+ * </p><p>
+ * The disadvantage is that this free decouple means that there is 
+ * no gaurantee that the implementations decoupled by this interface
+ * actually support the same scheme. Issues will be caught only 
+ * at deployment and not at compile time.
+ * This places a larger burden on the deployer.
+ * </p><p>
+ * Either an understanding or a consistent URL mapping scheme may be 
+ * required. For example, <code>//john.smith@localhost/sieve</code>
+ * may need to be resolved to <code>../apps/james/var/sieve/john.smith@localhost.sieve</code>
+ * when using the file system to store scripts. Note that names <strong>MUST</strong>
+ * be normalised before resolving on a file system.
+ * </p>
+ */
+public interface ResourceLocator {
+
+    class UserSieveInformation {
+        private DateTime scriptActivationDate;
+        private DateTime scriptInterpretationDate;
+        private InputStream scriptContent;
+
+        public UserSieveInformation(DateTime scriptActivationDate, DateTime scriptInterpretationDate, InputStream scriptContent) {
+            this.scriptActivationDate = scriptActivationDate;
+            this.scriptInterpretationDate = scriptInterpretationDate;
+            this.scriptContent = scriptContent;
+        }
+
+        public DateTime getScriptActivationDate() {
+            return scriptActivationDate;
+        }
+
+        public DateTime getScriptInterpretationDate() {
+            return scriptInterpretationDate;
+        }
+
+        public InputStream getScriptContent() {
+            return scriptContent;
+        }
+    }
+
+    /**
+     * GET verb locates and loads a resource. 
+     * @param uri identifies the Sieve script 
+     * @return not null
+     * @throws Exception when the resource cannot be located
+     */
+    UserSieveInformation get(String uri) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailAdapter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailAdapter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailAdapter.java
new file mode 100644
index 0000000..897b6d6
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailAdapter.java
@@ -0,0 +1,504 @@
+/****************************************************************
+ * 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.jsieve;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.dom.address.AddressList;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.dom.address.MailboxList;
+import org.apache.james.mime4j.dom.field.ParseException;
+import org.apache.james.mime4j.field.address.DefaultAddressParser;
+import org.apache.james.mime4j.utils.search.MessageMatcher;
+import org.apache.jsieve.SieveContext;
+import org.apache.jsieve.exception.InternetAddressException;
+import org.apache.jsieve.exception.SieveException;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.AddressImpl;
+import org.apache.jsieve.mail.MailAdapter;
+import org.apache.jsieve.mail.MailUtils;
+import org.apache.jsieve.mail.SieveMailException;
+import org.apache.jsieve.mail.optional.EnvelopeAccessors;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+import org.joda.time.DateTime;
+
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+/**
+ * <p>
+ * Class <code>SieveMailAdapter</code> implements a <code>MailAdapter</code>
+ * for use in a Mailet environment.
+ * </p>
+ */
+public class SieveMailAdapter implements MailAdapter, EnvelopeAccessors, ActionContext
+{
+    private static final Log LOG = LogFactory.getLog(SieveMailAdapter.class);
+    
+    private Log log = LOG;
+    
+    /**
+     * The Mail being adapted.
+     */
+    private Mail fieldMail;
+    /**
+     * The MailetContext.
+     */
+    private MailetContext fieldMailetContext;
+    /**
+     * List of Actions to perform.
+     */
+    private List<Action> fieldActions;
+    
+    private final ActionDispatcher dispatcher;
+    
+    private final Poster poster;
+    private final DateTime scriptActivationDate;
+    private final DateTime scriptInterpretationDate;
+    private final MailAddress recipient;
+
+    /**
+     * Constructor for SieveMailAdapter.
+     * 
+     * @param aMail
+     * @param aMailetContext
+     */
+    public SieveMailAdapter(final Mail aMail, final MailetContext aMailetContext, final ActionDispatcher dispatcher, final Poster poster,
+                            DateTime scriptActivationDate, DateTime scriptInterpretationDate, MailAddress recipient)
+    {
+        this.poster = poster;
+        this.dispatcher = dispatcher;
+        this.scriptInterpretationDate = scriptInterpretationDate;
+        this.scriptActivationDate = scriptActivationDate;
+        this.recipient = recipient;
+        setMail(aMail);
+        setMailetContext(aMailetContext);
+    }
+
+    public DateTime getScriptActivationDate() {
+        return scriptActivationDate;
+    }
+
+    public DateTime getScriptInterpretationDate() {
+        return scriptInterpretationDate;
+    }
+
+    public MailAddress getRecipient() {
+        return recipient;
+    }
+
+    public void setLog(Log log) {
+        this.log = log;
+    }
+
+    /**
+     * Returns the message.
+     * 
+     * @return MimeMessage
+     */
+    protected MimeMessage getMessage() throws MessagingException
+    {
+        return getMail().getMessage();
+    }
+    /**
+     * Returns the List of actions.
+     * 
+     * @return List
+     */
+    public List<Action> getActions()
+    {
+        List<Action> actions = null;
+        if (null == (actions = getActionsBasic()))
+        {
+            updateActions();
+            return getActions();
+        }
+        return actions;
+    }
+    /**
+     * Returns a new List of actions.
+     * 
+     * @return List
+     */
+    protected List<Action> computeActions()
+    {
+        return new ArrayList<Action>();
+    }
+    /**
+     * Returns the List of actions.
+     * 
+     * @return List
+     */
+    private List<Action> getActionsBasic()
+    {
+        return fieldActions;
+    }
+    /**
+     * Adds an Action.
+     * 
+     * @param action The action to set
+     */
+    public void addAction(Action action)
+    {
+        getActions().add(action);
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#executeActions()
+     */
+    public void executeActions() throws SieveException
+    {
+        final List<Action> actions = getActions();
+        for (final Action action: actions) {
+            getMailetContext().log("Executing action: " + action.toString());
+            try
+            {
+                dispatcher.execute(action, getMail(), this);
+            }
+            catch (MessagingException e)
+            {
+                throw new SieveException(e);
+            }
+        }
+    }
+    /**
+     * Sets the actions.
+     * 
+     * @param actions The actions to set
+     */
+    protected void setActions(List<Action> actions)
+    {
+        fieldActions = actions;
+    }
+    
+    /**
+     * Updates the actions.
+     */
+    protected void updateActions()
+    {
+        setActions(computeActions());
+    }
+
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getHeader(String)
+     */
+    public List<String> getHeader(String name) throws SieveMailException
+    {
+        try
+        {
+            String[] headers = getMessage().getHeader(name);
+            return (headers == null ? new ArrayList<String>(0) : Arrays.asList(headers));
+        }
+        catch (MessagingException ex)
+        {
+            throw new SieveMailException(ex);
+        }
+    }
+    
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getHeaderNames()
+     */
+    public List<String> getHeaderNames() throws SieveMailException
+    {
+        Set<String> headerNames = new HashSet<String>();
+        try
+        {
+            Enumeration allHeaders = getMessage().getAllHeaders();
+            while (allHeaders.hasMoreElements())
+            {
+                headerNames.add(((Header) allHeaders.nextElement()).getName());
+            }
+            return new ArrayList<String>(headerNames);
+        }
+        catch (MessagingException ex)
+        {
+            throw new SieveMailException(ex);
+        }
+    }
+    
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getMatchingHeader(String)
+     */
+    public List<String> getMatchingHeader(String name) throws SieveMailException
+    {
+        return MailUtils.getMatchingHeader(this, name);
+    }
+    
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getSize()
+     */
+    public int getSize() throws SieveMailException
+    {
+        try
+        {
+            return (int) getMail().getMessageSize();
+        }
+        catch (MessagingException ex)
+        {
+            throw new SieveMailException(ex);
+        }
+    }
+    
+    /**
+     * Method getEnvelopes.
+     * 
+     * @return Map
+     */
+    protected Map<String, String> getEnvelopes()
+    {
+        Map<String, String> envelopes = new HashMap<String, String>(2);
+        if (null != getEnvelopeFrom())
+            envelopes.put("From", getEnvelopeFrom());
+        if (null != getEnvelopeTo())
+            envelopes.put("To", getEnvelopeTo());
+        return envelopes;
+    }
+    /**
+     * @see org.apache.jsieve.mail.optional.EnvelopeAccessors#getEnvelope(String)
+     */
+    public List<String> getEnvelope(String name) throws SieveMailException
+    {
+        List<String> values = new ArrayList<String>(1);
+        String value = getEnvelopes().get(name);
+        if (null != value)
+            values.add(value);
+        return values;
+    }
+    
+    /**
+     * @see org.apache.jsieve.mail.optional.EnvelopeAccessors#getEnvelopeNames()
+     */
+    public List<String> getEnvelopeNames() throws SieveMailException
+    {
+        return new ArrayList<String>(getEnvelopes().keySet());
+    }
+    
+    /**
+     * @see org.apache.jsieve.mail.optional.EnvelopeAccessors#getMatchingEnvelope(String)
+     */
+    public List<String> getMatchingEnvelope(String name) throws SieveMailException
+    {
+        final List<String> matchedEnvelopeValues = new ArrayList<String>(32);
+        for (String envelopeName: getEnvelopeNames()) {
+            if (envelopeName.trim().equalsIgnoreCase(name))
+                matchedEnvelopeValues.addAll(getEnvelope(envelopeName));
+        }
+        return matchedEnvelopeValues;
+    }
+    
+    /**
+     * Returns the from.
+     * 
+     * @return String
+     */
+    public String getEnvelopeFrom()
+    {
+        MailAddress sender = getMail().getSender(); 
+        return (null == sender ? "" : sender.toString());
+    }
+    
+    /**
+     * Returns the sole recipient or null if there isn't one.
+     * 
+     * @return String
+     */
+    public String getEnvelopeTo()
+    {
+        for (MailAddress mailAddress : getMail().getRecipients()) {
+            String recipient = mailAddress.toInternetAddress().getAddress();
+            if (recipient != null) {
+                return recipient;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Returns the mail.
+     * 
+     * @return Mail
+     */
+    public Mail getMail()
+    {
+        return fieldMail;
+    }
+    
+    /**
+     * Sets the mail.
+     * 
+     * @param mail The mail to set
+     */
+    protected void setMail(Mail mail)
+    {
+        fieldMail = mail;
+    }
+    
+    /**
+     * Returns the mailetContext.
+     * 
+     * @return MailetContext
+     */
+    public MailetContext getMailetContext()
+    {
+        return fieldMailetContext;
+    }
+    
+    /**
+     * Sets the mailetContext.
+     * 
+     * @param mailetContext The mailetContext to set
+     */
+    protected void setMailetContext(MailetContext mailetContext)
+    {
+        fieldMailetContext = mailetContext;
+    }
+    
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString()
+    {
+        String messageID = null;
+        try
+        {
+            messageID = getMail().getMessage().getMessageID();
+        }
+        catch (MessagingException e)
+        {
+            messageID = "<" + e.getMessage() + ">";
+        }
+        return getClass().getName() + " Envelope From: "
+                + (null == getEnvelopeFrom() ? "null" : getEnvelopeFrom())
+                + " Envelope To: "
+                + (null == getEnvelopeTo() ? "null" : getEnvelopeTo())
+                + " Message ID: " + (null == messageID ? "null" : messageID);
+    }
+    
+    public String getContentType() throws SieveMailException {
+        try {
+            return getMessage().getContentType();
+        } catch (MessagingException e) {
+            throw new SieveMailException(e);
+        }
+    }
+    
+    public Address[] parseAddresses(String arg) throws SieveMailException, InternetAddressException {
+        try {
+            List<String> headerValues = getHeader(arg);
+            List<MailboxList> mailboxes = new ArrayList<MailboxList>();
+            int size = 0;
+            for(String headerValue : headerValues) {
+                MailboxList mailboxList = new AddressList(DefaultAddressParser.DEFAULT.parseAddressList(headerValue), true).flatten();
+                size += mailboxList.size();
+                mailboxes.add(mailboxList);
+            }
+            int i = 0;
+            final Address[] results = new Address[size];
+            for(MailboxList mailboxList : mailboxes) {
+                for(Mailbox mailbox : mailboxList) {
+                    results[i] = new AddressImpl(mailbox.getLocalPart(), mailbox.getDomain());
+                    i++;
+                }
+            }
+            return results;
+        } catch (ParseException e) {
+            throw new InternetAddressException(e);
+        }
+    }
+
+    public Log getLog() {
+        return log;
+    }
+    
+    public String getServerInfo() {
+        return getMailetContext().getServerInfo();
+    }
+    public void post(String uri, MimeMessage mail) throws MessagingException {
+        poster.post(uri, mail);
+    }
+    
+    public void post(MailAddress sender, Collection recipients, MimeMessage mail) throws MessagingException {
+        getMailetContext().sendMail(sender, recipients, mail);
+    }
+
+    public boolean isInBodyText(List<String> phrasesCaseInsensitive) throws SieveMailException {
+        try {
+            return MessageMatcher.builder()
+                .contentTypes(Lists.newArrayList("text/plain"))
+                .includeHeaders(false)
+                .caseInsensitive(false)
+                .searchContents(Lists.transform(phrasesCaseInsensitive, new Function<String, CharSequence>() {
+                    public CharSequence apply(String s) {
+                        return s;
+                    }
+                })).build()
+                .messageMatches(getMail().getMessage().getInputStream());
+        } catch (Exception e) {
+            throw new SieveMailException("Error searching in the mail content", e);
+        }
+    }
+
+    public boolean isInBodyRaw(List<String> phrasesCaseInsensitive) throws SieveMailException {
+        try {
+            return MessageMatcher.builder()
+                .includeHeaders(false)
+                .caseInsensitive(false)
+                .ignoringMime(true)
+                .searchContents(Lists.transform(phrasesCaseInsensitive, new Function<String, CharSequence>() {
+                    public CharSequence apply(String s) {
+                        return s;
+                    }
+                })).build()
+                .messageMatches(getMail().getMessage().getInputStream());
+        } catch (Exception e) {
+            throw new SieveMailException("Error searching in the mail content", e);
+        }
+    }
+
+    public boolean isInBodyContent(List<String> contentTypes, List<String> phrasesCaseInsensitive) throws SieveMailException {
+        try {
+            return MessageMatcher.builder()
+                .contentTypes(contentTypes)
+                .includeHeaders(false)
+                .caseInsensitive(false)
+                .searchContents(Lists.transform(phrasesCaseInsensitive, new Function<String, CharSequence>() {
+                    public CharSequence apply(String s) {
+                        return s;
+                    }
+                })).build()
+                .messageMatches(getMail().getMessage().getInputStream());
+        } catch (Exception e) {
+            throw new SieveMailException("Error searching in the mail content", e);
+        }
+    }
+
+    public void setContext(SieveContext context) {}
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0501c99a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailboxMailet.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailboxMailet.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailboxMailet.java
new file mode 100644
index 0000000..33ef4d4
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/SieveMailboxMailet.java
@@ -0,0 +1,464 @@
+/****************************************************************
+ * 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.jsieve;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.commons.logging.Log;
+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.MailetConfig;
+import org.apache.mailet.MailetException;
+import org.apache.mailet.base.GenericMailet;
+import org.apache.mailet.base.RFC2822Headers;
+
+/**
+ * <p>Executes a <a href='http://www.rfc-editor.org/rfc/rfc3028.txt'>Sieve</a>
+ * script against incoming mail. The script applied is based on the recipient.</p>
+ * <h4>Init Parameters</h4>
+ * <table>
+ * <thead><tr><th>Name</th><th>Required</th><th>Values</th><th>Role</th></thead>
+ * <tr><td>verbose</td><td>No - defaults to false</td><td>true (ignoring case) to enable, otherwise disable</td>
+ * <td>
+ * Enables verbose logging.
+ * </td></tr>
+ * </table>
+ */
+public class SieveMailboxMailet extends GenericMailet {
+    
+    /**
+     * The delivery header
+     */
+    private String deliveryHeader;
+
+    /**
+     * resetReturnPath
+     */
+    private boolean resetReturnPath;
+    /** Experimental */
+    private Poster poster;
+    /** Experimental */
+    private ResourceLocator locator;
+    
+    /** Indicates whether this mailet should log verbosely */
+    private boolean verbose = false;
+    
+    private boolean consume = true;
+    /** Indicates whether this mailet should log minimal information */
+    private boolean quiet = true;
+
+    private SieveFactory factory;
+
+    private ActionDispatcher actionDispatcher;
+
+    private Log log;
+
+    /**
+     * For SDI
+     */
+    public SieveMailboxMailet() {}
+    
+    /**
+     * CDI
+     * @param poster not null
+     */
+    public SieveMailboxMailet(Poster poster, ResourceLocator locator) {
+        this();
+        this.poster = poster;
+        this.locator = locator;
+    }
+
+    
+    public ResourceLocator getLocator() {
+        return locator;
+    }
+
+    /**
+     * For SDI
+     * @param locator not null
+     */
+    public void setLocator(ResourceLocator locator) {
+        this.locator = locator;
+    }
+
+    public Poster getPoster() {
+        return poster;
+    }
+    
+    /**
+     * For SDI
+     * @param poster not null
+     */
+    public void setPoster(Poster poster) {
+        this.poster = poster;
+    }
+
+    /**
+     * Is this mailet GHOSTing all mail it processes?
+     * @return true when mailet consumes all mail, false otherwise
+     */
+    public boolean isConsume() {
+        return consume;
+    }
+
+    /**
+     * Sets whether this mailet should GHOST all mail.
+     * @param consume true when the mailet should consume all mail, 
+     * false otherwise
+     */
+    public void setConsume(boolean consume) {
+        this.consume = consume;
+    }
+
+    /**
+     * Is this mailet logging verbosely?
+     * This property is set by init parameters.
+     * @return true if logging should be verbose, false otherwise
+     */
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+
+    /**
+     * Sets whether logging should be verbose for this mailet.
+     * This property is set by init parameters.
+     * This setting overrides {@link #isQuiet()}.
+     * @param verbose true when logging should be verbose,
+     * false otherwise
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Is the logging for this mailet set to minimal?
+     * @return true
+     */
+    public boolean isQuiet() {
+        return quiet;
+    }
+
+    /**
+     * Sets the logging for this mailet to minimal.
+     * This is overriden by {@link #setVerbose(boolean)}.
+     * @param quiet true for minimal logging, false otherwise
+     */
+    public void setQuiet(boolean quiet) {
+        this.quiet = quiet;
+    }
+    
+   
+    /**
+     * Is informational logging turned on? 
+     * @return true when minimal logging is off,
+     * false when logging is minimal
+     */
+    public boolean isInfoLoggingOn() {
+        return verbose || !quiet;
+    }
+
+    @Override
+    public void init(MailetConfig config) throws MessagingException {
+        
+        super.init(config);
+
+        try {
+            final ConfigurationManager configurationManager = new ConfigurationManager();
+            final int logLevel;
+            if (verbose) {
+                logLevel = CommonsLoggingAdapter.TRACE;
+            } else if (quiet) {
+                logLevel = CommonsLoggingAdapter.FATAL;
+            } else {
+                logLevel = CommonsLoggingAdapter.WARN;
+            }
+            log = new CommonsLoggingAdapter(this, logLevel);
+            configurationManager.setLog(log);
+            factory = configurationManager.build();
+        } catch (SieveConfigurationException e) {
+            throw new MessagingException("Failed to load standard Sieve configuration.", e);
+        }
+    }
+
+    /**
+     * 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 = null;
+        if (deliveryHeader != null || resetReturnPath) {
+            message = mail.getMessage();
+        }
+
+        if (resetReturnPath) {
+            // 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,
+                    (mail.getSender() == null ? "<>" : "<" + mail.getSender()
+                            + ">"));
+        }
+
+        Enumeration headers;
+        InternetHeaders deliveredTo = new InternetHeaders();
+        if (deliveryHeader != null) {
+            // Copy any Delivered-To headers from the message
+            headers = message
+                    .getMatchingHeaders(new String[] { deliveryHeader });
+            while (headers.hasMoreElements()) {
+                Header header = (Header) headers.nextElement();
+                deliveredTo.addHeader(header.getName(), header.getValue());
+            }
+        }
+
+        for (Iterator<MailAddress> i = recipients.iterator(); i.hasNext();) {
+            MailAddress recipient = i.next();
+            try {
+                if (deliveryHeader != null) {
+                    // Add qmail's de facto standard Delivered-To header
+                    message.addHeader(deliveryHeader, recipient.toString());
+                }
+
+                storeMail(mail.getSender(), recipient, mail);
+
+                if (deliveryHeader != null) {
+                    if (i.hasNext()) {
+                        // Remove headers but leave all placeholders
+                        message.removeHeader(deliveryHeader);
+                        headers = deliveredTo.getAllHeaders();
+                        // And restore any original Delivered-To headers
+                        while (headers.hasMoreElements()) {
+                            Header header = (Header) headers.nextElement();
+                            message.addHeader(header.getName(), header
+                                    .getValue());
+                        }
+                    }
+                }
+            } catch (Exception ex) {
+                log("Error while storing mail.", ex);
+                errors.add(recipient);
+            }
+        }
+
+        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);
+        }
+    }
+
+    /**
+     * Return a string describing this mailet.
+     * 
+     * @return a string describing this mailet
+     */
+    @Override
+    public String getMailetInfo() {
+        return "Sieve Mailbox Mailet";
+    }
+
+    /**
+     * 
+     * @param sender
+     * @param recipient
+     * @param mail
+     * @throws MessagingException
+     */
+    public void storeMail(MailAddress sender, MailAddress recipient,
+            Mail mail) throws MessagingException {
+        if (recipient == null) {
+            throw new IllegalArgumentException(
+                    "Recipient for mail to be spooled cannot be null.");
+        }
+        if (mail.getMessage() == null) {
+            throw new IllegalArgumentException(
+                    "Mail message to be spooled cannot be null.");
+        }
+        
+        sieveMessage(recipient, mail);
+ 
+    }
+    
+    protected void sieveMessage(MailAddress recipient, Mail aMail) throws MessagingException {
+        String username = getUsername(recipient);
+        try {
+            final ResourceLocator.UserSieveInformation userSieveInformation = locator.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 (isInfoLoggingOn()) {
+                log("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, poster, userSieveInformation.getScriptActivationDate(),
+                    userSieveInformation.getScriptInterpretationDate(), recipient);
+                aMailAdapter.setLog(log);
+                // This logging operation is potentially costly
+                if (verbose) {
+                    log("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 {
+        String url = "mailbox://" + username + "/";
+        poster.post(url, message);
+    }
+
+    /**
+     * @see org.apache.mailet.base.GenericMailet#init()
+     */
+    @Override
+    public void init() throws MessagingException {
+        super.init();
+        if (poster == null || locator == null) {
+            throw new MailetException("Not initialised. Please ensure that the mailet container supports either" +
+                    " setter or constructor injection");
+        }
+        
+        this.deliveryHeader = getInitParameter("addDeliveryHeader");
+        this.resetReturnPath = getInitParameter("resetReturnPath", true);
+        this.consume = getInitParameter("consume", true);
+        this.verbose = getInitParameter("verbose", false);
+        this.quiet = getInitParameter("quiet", false);
+        
+        actionDispatcher = new ActionDispatcher();
+    }
+    
+    /**
+     * Return the username to use for sieve processing for the given MailAddress
+     * 
+     * @param m
+     * @return username
+     */
+    protected String getUsername(MailAddress m) {
+        return m.getLocalPart() + "@localhost";
+    }
+    
+    /**
+     * Return the URI for the sieve script
+     *
+     * @param m
+     * @return
+     */
+    protected String getScriptUri(MailAddress m) {
+        return "//" + getUsername(m) + "/sieve";
+    }
+    
+    /**
+     * Deliver the original mail as an attachment with the main part being an error report.
+     *
+     * @param recipient
+     * @param aMail
+     * @param ex
+     * @throws MessagingException
+     * @throws IOException 
+     */
+    protected void handleFailure(MailAddress recipient, Mail aMail, Exception ex)
+            throws MessagingException, IOException {
+        String user = getUsername(recipient);
+
+        MimeMessage originalMessage = aMail.getMessage();
+        MimeMessage message = new MimeMessage(originalMessage);
+        MimeMultipart multipart = new MimeMultipart();
+        
+        MimeBodyPart noticePart = new MimeBodyPart();
+        noticePart.setText("An error was encountered while processing this mail with the active sieve script for user \""
+                + user + "\". The error encountered was:\r\n" + ex.getLocalizedMessage() + "\r\n");
+        multipart.addBodyPart(noticePart);
+        
+        MimeBodyPart originalPart = new MimeBodyPart();
+        originalPart.setContent(originalMessage, "message/rfc822");
+        if ((originalMessage.getSubject() != null) && (!originalMessage.getSubject().trim().isEmpty())) {
+            originalPart.setFileName(originalMessage.getSubject().trim());
+        } else {
+            originalPart.setFileName("No Subject");
+        }
+        originalPart.setDisposition(MimeBodyPart.INLINE);
+        multipart.addBodyPart(originalPart);
+        
+        message.setContent(multipart);
+        message.setSubject("[SIEVE ERROR] " + originalMessage.getSubject());
+        message.setHeader("X-Priority", "1");
+        message.saveChanges();
+        
+        storeMessageInbox(user, message);
+    }
+   
+}


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


Mime
View raw message