You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/11/12 07:40:37 UTC

[james-project] branch master updated: JAMES-1618 Fix manage sieve implementation and test it with Thunderbird (#742)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 00fcf38  JAMES-1618 Fix manage sieve implementation and test it with Thunderbird (#742)
00fcf38 is described below

commit 00fcf384db0405900a2d766a5f126a7b3f19a849
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Fri Nov 12 14:40:28 2021 +0700

    JAMES-1618 Fix manage sieve implementation and test it with Thunderbird (#742)
---
 .../james/mpt/testsuite/AuthenticateContract.java  |  7 +++++
 .../james/managesieve/scripts/authenticate.test    |  4 +--
 .../{starttls.test => authenticateBase64.test}     | 17 ++---------
 .../james/managesieve/scripts/capability.test      |  2 +-
 .../james/managesieve/scripts/checkscript.test     | 11 +++----
 .../james/managesieve/scripts/deletescript.test    | 12 ++++----
 .../james/managesieve/scripts/getscript.test       |  7 +++--
 .../james/managesieve/scripts/havespace.test       |  2 +-
 .../james/managesieve/scripts/listscripts.test     |  8 ++---
 .../james/managesieve/scripts/putscript.test       |  6 ++--
 .../james/managesieve/scripts/renamescript.test    | 12 ++++----
 .../james/managesieve/scripts/setactive.test       |  4 +--
 .../apache/james/managesieve/scripts/starttls.test |  2 +-
 .../james/managesieve/scripts/unauthenticate.test  |  2 +-
 .../james/managesieve/core/CoreProcessor.java      |  4 +--
 .../core/PlainAuthenticationProcessor.java         | 18 +++++++----
 .../managesieve/transcode/ArgumentParser.java      | 25 ++++++++++++++--
 .../transcode/ManageSieveProcessor.java            | 10 +++++++
 .../transcode/NotEnoughDataException.java          | 24 +++++++++++++++
 .../apache/james/managesieve/util/ParserUtils.java | 12 ++++++++
 .../transport/mailets/jsieve/RedirectAction.java   | 10 +++++--
 .../transport/mailets/jsieve/RejectAction.java     | 10 +++++--
 .../transport/mailets/jsieve/VacationAction.java   | 10 +++++--
 .../mailets/managesieve/ManageSieveMailet.java     |  5 ++--
 .../netty/ChannelManageSieveResponseWriter.java    | 35 ++++++++++++++++++++--
 .../netty/ManageSieveChannelUpstreamHandler.java   | 15 ++++++++--
 .../netty/ManageSieveMDCContext.java               |  4 ++-
 27 files changed, 206 insertions(+), 72 deletions(-)

diff --git a/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateContract.java b/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateContract.java
index cc383f1..c74c67c 100644
--- a/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateContract.java
+++ b/mpt/impl/managesieve/core/src/main/java/org/apache/james/mpt/testsuite/AuthenticateContract.java
@@ -41,4 +41,11 @@ public interface AuthenticateContract extends HostSystemProvider {
             .withLocale(Locale.US)
             .run("authenticate");
     }
+
+    @Test
+    default void authenticateBase64ShouldWork() throws Exception {
+        authenticateContractProtocol()
+            .withLocale(Locale.US)
+            .run("authenticateBase64");
+    }
 }
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test
index 896c3d8..334699c 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticate.test
@@ -20,7 +20,7 @@
 C: AUTHENTICATE
 S: NO ManageSieve syntax is incorrect : You must specify a SASL mechanism as an argument of AUTHENTICATE command
 
-C: AUTHENTICATE UNKNOWN
+C: AUTHENTICATE "UNKNOWN"
 S: NO Unknown SASL mechanism UNKNOWN
 
 C: AUTHENTICATE "PLAIN"
@@ -34,4 +34,4 @@ S: NO authentication failed
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
\ No newline at end of file
+S: OK
\ No newline at end of file
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticateBase64.test
similarity index 80%
copy from mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test
copy to mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticateBase64.test
index 2706ff8..cee22fb 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/authenticateBase64.test
@@ -17,18 +17,5 @@
 # under the License.                                           #
 ################################################################
 
-C: STARTTLS
-S: OK
-
-C: STARTTLS
-S: NO You can't enable two time SSL encryption
-
-C: AUTHENTICATE "PLAIN"
-S: \+ ""
-C:  user password
-S: OK authentication successfull
-
-C: STARTTLS
-S: NO command STARTTLS is issued in the wrong state. It must be issued as you are unauthenticated
-
-C: LOGOUT
\ No newline at end of file
+C: AUTHENTICATE "PLAIN" "AHVzZXIAcGFzc3dvcmQ="
+S: OK
\ No newline at end of file
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/capability.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/capability.test
index 894d10c..1b2141b 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/capability.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/capability.test
@@ -40,7 +40,7 @@ S: OK
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: CAPABILITY
 SUB {
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/checkscript.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/checkscript.test
index cd48797..8fc76dd 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/checkscript.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/checkscript.test
@@ -23,8 +23,9 @@ S: NO " is an invalid size literal : it should be at least 4 char looking like \
 C: CHECKSCRIPT unquoted
 S: NO "unquoted is an invalid size literal : it should be at least 4 char looking like .*"
 
-C: CHECKSCRIPT {31+}
-S: NO "Missing argument: script content"
+# Now waits for extra data, the expected behaviour
+#C: CHECKSCRIPT {31+}
+#S: NO "Missing argument: script content"
 
 C: CHECKSCRIPT {31+}
 C: #comment
@@ -35,9 +36,9 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
-C: CHECKSCRIPT {110+}
+C: CHECKSCRIPT {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -49,5 +50,5 @@ C: CHECKSCRIPT {31+}
 C: #comment
 C: InvalidSieveCommand
 C:
-S: NO "Syntax Error: org.apache.jsieve.parser.generated.ParseException: Encountered "<EOF>" at line 2, column 19.
+S: NO "Syntax Error: org.apache.jsieve.parser.generated.ParseException: Encountered "<EOF>" at line 2, column 21.
 
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/deletescript.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/deletescript.test
index 72916ad..5352d2a 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/deletescript.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/deletescript.test
@@ -26,12 +26,12 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: DELETESCRIPT "foo"
 S: NO \(NONEXISTENT\) "There is no script by that name"
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -40,12 +40,13 @@ C: }
 S: OK
 
 C: GETSCRIPT "mysievescript"
-S: \{97\}
+S: \{99\}
 S: require \["fileinto"\];
 S:
 S: if envelope :contains "to" "tmartin\+sent" \{
 S:   fileinto "INBOX.sent";
 S: \}
+S:
 S: OK
 
 C: DELETESCRIPT "mysievescript"
@@ -54,7 +55,7 @@ S: OK
 C: GETSCRIPT "mysievescript"
 S: NO \(NONEXISTENT\) "There is no script by that name"
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -63,12 +64,13 @@ C: }
 S: OK
 
 C: GETSCRIPT "mysievescript"
-S: \{97\}
+S: \{99\}
 S: require \["fileinto"\];
 S:
 S: if envelope :contains "to" "tmartin\+sent" \{
 S:   fileinto "INBOX.sent";
 S: \}
+S:
 S: OK
 
 C: SETACTIVE "mysievescript"
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/getscript.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/getscript.test
index 19e0380..aa5ca1d 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/getscript.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/getscript.test
@@ -26,12 +26,12 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: GETSCRIPT "foo"
 S: NO \(NONEXISTENT\) "There is no script by that name"
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -40,12 +40,13 @@ C: }
 S: OK
 
 C: GETSCRIPT "mysievescript"
-S: \{97\}
+S: \{99\}
 S: require \["fileinto"\];
 S:
 S: if envelope :contains "to" "tmartin\+sent" \{
 S:   fileinto "INBOX.sent";
 S: \}
+S:
 S: OK
 
 
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/havespace.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/havespace.test
index 7eda344..2b09582 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/havespace.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/havespace.test
@@ -29,7 +29,7 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: HAVESPACE "scriptname" 49
 S: OK
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/listscripts.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/listscripts.test
index 14bb2e3..1539277 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/listscripts.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/listscripts.test
@@ -23,12 +23,12 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: LISTSCRIPTS
 S: OK
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -36,7 +36,7 @@ C:   fileinto "INBOX.sent";
 C: }
 S: OK
 
-C: PUTSCRIPT "toto1" {110+}
+C: PUTSCRIPT "toto1" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -44,7 +44,7 @@ C:   fileinto "INBOX.sent";
 C: }
 S: OK
 
-C: PUTSCRIPT "toto2" {110+}
+C: PUTSCRIPT "toto2" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/putscript.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/putscript.test
index 073db5a..e8e300f 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/putscript.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/putscript.test
@@ -41,9 +41,9 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {97+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -55,5 +55,5 @@ C: PUTSCRIPT "foo" {31+}
 C: #comment
 C: InvalidSieveCommand
 C:
-S: NO "Syntax Error: org.apache.jsieve.parser.generated.ParseException: Encountered "<EOF>" at line 2, column 19.
+S: NO "Syntax Error: org.apache.jsieve.parser.generated.ParseException: Encountered "<EOF>" at line 2, column 21.
 
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/renamescript.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/renamescript.test
index ea9886e..355cd84 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/renamescript.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/renamescript.test
@@ -29,9 +29,9 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
@@ -40,30 +40,32 @@ C: }
 S: OK
 
 C: GETSCRIPT "mysievescript"
-S: \{97\}
+S: \{99\}
 S: require \["fileinto"\];
 S:
 S: if envelope :contains "to" "tmartin\+sent" \{
 S:   fileinto "INBOX.sent";
 S: \}
+S:
 S: OK
 
 C: RENAMESCRIPT "mysievescript" "mysievescriptbis"
 S: OK
 
 C: GETSCRIPT "mysievescriptbis"
-S: \{97\}
+S: \{99\}
 S: require \["fileinto"\];
 S:
 S: if envelope :contains "to" "tmartin\+sent" \{
 S:   fileinto "INBOX.sent";
 S: \}
+S:
 S: OK
 
 C: GETSCRIPT "mysievescript"
 S: NO \(NONEXISTENT\) "There is no script by that name"
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/setactive.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/setactive.test
index 0174316..c05ff82e 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/setactive.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/setactive.test
@@ -26,12 +26,12 @@ S: NO
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: SETACTIVE "foo"
 S: NO \(NONEXISTENT\) "There is no script by that name"
 
-C: PUTSCRIPT "mysievescript" {110+}
+C: PUTSCRIPT "mysievescript" {99+}
 C: require ["fileinto"];
 C:
 C: if envelope :contains "to" "tmartin+sent" {
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test
index 2706ff8..7e97354 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/starttls.test
@@ -26,7 +26,7 @@ S: NO You can't enable two time SSL encryption
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: STARTTLS
 S: NO command STARTTLS is issued in the wrong state. It must be issued as you are unauthenticated
diff --git a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test
index 658a410..4acc436 100644
--- a/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test
+++ b/mpt/impl/managesieve/core/src/main/resources/org/apache/james/managesieve/scripts/unauthenticate.test
@@ -29,7 +29,7 @@ S: NO UNAUTHENTICATE command must be issued in authenticated state
 C: AUTHENTICATE "PLAIN"
 S: \+ ""
 C:  user password
-S: OK authentication successfull
+S: OK
 
 C: GETSCRIPT any
 S: NO \(NONEXISTENT\) "There is no script by that name"
diff --git a/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java b/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java
index bf28ac8..cc84451 100644
--- a/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java
+++ b/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/CoreProcessor.java
@@ -216,7 +216,7 @@ public class CoreProcessor implements CoreCommands {
             if (Strings.isNullOrEmpty(mechanism)) {
                 return "NO ManageSieve syntax is incorrect : You must specify a SASL mechanism as an argument of AUTHENTICATE command";
             }
-            String unquotedMechanism = ParserUtils.unquote(mechanism);
+            String unquotedMechanism = ParserUtils.unquoteFirst(mechanism);
             SupportedMechanism supportedMechanism = SupportedMechanism.retrieveMechanism(unquotedMechanism);
 
             session.setChoosedAuthenticationMechanism(supportedMechanism);
@@ -237,7 +237,7 @@ public class CoreProcessor implements CoreCommands {
             if (authenticatedUsername != null) {
                 session.setUser(authenticatedUsername);
                 session.setState(Session.State.AUTHENTICATED);
-                return "OK authentication successfull";
+                return "OK";
             } else {
                 session.setState(Session.State.UNAUTHENTICATED);
                 session.setUser(null);
diff --git a/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java b/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java
index f0d4f03..4c94818 100644
--- a/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java
+++ b/protocols/managesieve/src/main/java/org/apache/james/managesieve/core/PlainAuthenticationProcessor.java
@@ -20,6 +20,8 @@
 
 package org.apache.james.managesieve.core;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.Iterator;
 
 import org.apache.james.core.Username;
@@ -54,15 +56,21 @@ public class PlainAuthenticationProcessor implements AuthenticationProcessor {
 
     @Override
     public Username isAuthenticationSuccesfull(Session session, String suppliedClientData) throws SyntaxException, AuthenticationException {
-        if (suppliedClientData.contains("\u0000")) {
-            return authenticateWithSeparator(session, suppliedClientData, '\u0000');
-        } else {
-            return authenticateWithSeparator(session, suppliedClientData, ' ');
+        try {
+            byte[] decoded = Base64.getDecoder().decode(suppliedClientData.getBytes());
+            String decodedString = new String(decoded, StandardCharsets.US_ASCII);
+            return authenticateWithSeparator(session, decodedString, '\u0000');
+        } catch (Exception e) {
+            if (suppliedClientData.contains("\u0000")) {
+                return authenticateWithSeparator(session, suppliedClientData, '\u0000');
+            } else {
+                return authenticateWithSeparator(session, suppliedClientData, ' ');
+            }
         }
     }
 
     private Username authenticateWithSeparator(Session session, String suppliedClientData, char c) throws SyntaxException, AuthenticationException {
-        Iterator<String> it = Splitter.on(c).split(suppliedClientData).iterator();
+        Iterator<String> it = Splitter.on(c).omitEmptyStrings().split(suppliedClientData).iterator();
         if (!it.hasNext()) {
             throw new SyntaxException("You must supply a username for the authentication mechanism. Formal syntax : <NULL>username<NULL>password");
         }
diff --git a/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ArgumentParser.java b/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ArgumentParser.java
index eb8fa4e..e387707 100644
--- a/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ArgumentParser.java
+++ b/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ArgumentParser.java
@@ -40,10 +40,17 @@ import com.google.common.base.Strings;
 public class ArgumentParser {
     
     private final CoreCommands core;
+    private final boolean validatePutSize;
 
     @Inject
     public ArgumentParser(CoreCommands core) {
         this.core = core;
+        this.validatePutSize = true;
+    }
+
+    public ArgumentParser(CoreCommands core, boolean validatePutSize) {
+        this.core = core;
+        this.validatePutSize = validatePutSize;
     }
 
     public String getAdvertisedCapabilities() {
@@ -109,11 +116,12 @@ public class ArgumentParser {
         Iterator<String> firstLine = Splitter.on("\r\n").split(args.trim()).iterator();
         Iterator<String> arguments = Splitter.on(' ').split(firstLine.next().trim()).iterator();
 
+        long size;
         if (! arguments.hasNext()) {
             return "NO : Missing argument: script size";
         } else {
             try {
-                ParserUtils.getSize(arguments.next());
+                size = ParserUtils.getSize(arguments.next());
             } catch (ArgumentException e) {
                 return "NO \"" + e.getMessage() + "\"";
             }
@@ -122,6 +130,12 @@ public class ArgumentParser {
             return "NO \"Extra arguments not supported\"";
         } else {
             String content = Joiner.on("\r\n").join(firstLine);
+            if (validatePutSize) {
+                content += "\r\n";
+            }
+            if (content.length() < size && validatePutSize) {
+                throw new NotEnoughDataException();
+            }
             if (Strings.isNullOrEmpty(content)) {
                 return "NO \"Missing argument: script content\"";
             }
@@ -162,6 +176,7 @@ public class ArgumentParser {
         Iterator<String> arguments = Splitter.on(' ').split(firstLine.next().trim()).iterator();
 
         String scriptName;
+        long size;
         if (! arguments.hasNext()) {
              return "NO \"Missing argument: script name\"";
         } else {
@@ -174,7 +189,7 @@ public class ArgumentParser {
             return "NO \"Missing argument: script size\"";
         } else {
             try {
-                ParserUtils.getSize(arguments.next());
+                size = ParserUtils.getSize(arguments.next());
             } catch (ArgumentException e) {
                 return "NO \"" + e.getMessage() + "\"";
             }
@@ -183,6 +198,12 @@ public class ArgumentParser {
             return "NO \"Extra arguments not supported\"";
         } else {
             String content = Joiner.on("\r\n").join(firstLine);
+            if (validatePutSize) {
+                content += "\r\n";
+            }
+            if (content.length() < size && validatePutSize) {
+                throw new NotEnoughDataException();
+            }
             return core.putScript(session, ParserUtils.unquote(scriptName), content);
         }
     }
diff --git a/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java b/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java
index 85d00b6..52e68ed 100644
--- a/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java
+++ b/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/ManageSieveProcessor.java
@@ -22,6 +22,7 @@ package org.apache.james.managesieve.transcode;
 
 import javax.inject.Inject;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.james.managesieve.api.ManageSieveException;
 import org.apache.james.managesieve.api.Session;
 import org.apache.james.managesieve.api.SessionTerminatedException;
@@ -88,6 +89,15 @@ public class ManageSieveProcessor {
             return argumentParser.authenticate(session, arguments);
         }
         if (command.equalsIgnoreCase(AUTHENTICATE)) {
+            if (StringUtils.countMatches(arguments, "\"") == 4) {
+                argumentParser.chooseMechanism(session, arguments);
+                int bracket1 = arguments.indexOf('\"');
+                int bracket2 = arguments.indexOf('\"', bracket1 + 1);
+                int bracket3 = arguments.indexOf('\"', bracket2 + 1);
+                int bracket4 = arguments.indexOf('\"', bracket3 + 1);
+
+                return argumentParser.authenticate(session, arguments.substring(bracket3 + 1, bracket4));
+            }
             return argumentParser.chooseMechanism(session, arguments);
         } else if (command.equalsIgnoreCase(CAPABILITY)) {
             return argumentParser.capability(session, arguments);
diff --git a/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/NotEnoughDataException.java b/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/NotEnoughDataException.java
new file mode 100644
index 0000000..9797bbd
--- /dev/null
+++ b/protocols/managesieve/src/main/java/org/apache/james/managesieve/transcode/NotEnoughDataException.java
@@ -0,0 +1,24 @@
+/*
+ *   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.managesieve.transcode;
+
+public class NotEnoughDataException extends RuntimeException {
+}
diff --git a/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/ParserUtils.java b/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/ParserUtils.java
index 6c36c36..f071988 100644
--- a/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/ParserUtils.java
+++ b/protocols/managesieve/src/main/java/org/apache/james/managesieve/util/ParserUtils.java
@@ -50,4 +50,16 @@ public class ParserUtils {
         return result;
     }
 
+    public static String unquoteFirst(String quoted) {
+        if (quoted == null) {
+            return null;
+        }
+        if (quoted.length() > 2 && quoted.startsWith("\"") && quoted.indexOf('\"', 1) >= 0) {
+            return quoted.substring(1, quoted.indexOf('\"', 1));
+        } else if (quoted.startsWith("'") && quoted.endsWith("'")) {
+            return quoted.substring(1, quoted.length() - 1);
+        }
+        return null;
+    }
+
 }
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
index ef422ad..ec9c6d4 100644
--- 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
@@ -21,6 +21,7 @@ package org.apache.james.transport.mailets.jsieve;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
+import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.server.core.MailImpl;
 import org.apache.jsieve.mail.Action;
 import org.apache.jsieve.mail.ActionRedirect;
@@ -57,12 +58,17 @@ public class RedirectAction implements MailAction {
     public void execute(ActionRedirect anAction, Mail aMail, ActionContext context) throws MessagingException {
         ActionUtils.detectAndHandleLocalLooping(aMail, context, "redirect");
 
-        context.post(MailImpl.builder()
+        MailImpl redirectedMail = MailImpl.builder()
             .name("redirect-" + aMail.getName())
             .sender(aMail.getMaybeSender())
             .addRecipient(new MailAddress(anAction.getAddress()))
             .mimeMessage(aMail.getMessage())
-            .build());
+            .build();
+        try {
+            context.post(redirectedMail);
+        } finally {
+            LifecycleUtil.dispose(redirectedMail);
+        }
 
         LOGGER.debug("Redirected Message ID: {} to \"{}\"", aMail.getMessage().getMessageID(), anAction.getAddress());
         DiscardAction.removeRecipient(aMail, context);
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
index d75350b..eca6337 100644
--- 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
@@ -29,6 +29,7 @@ import javax.mail.internet.InternetAddress;
 import javax.mail.internet.MimeMessage;
 
 import org.apache.james.core.MailAddress;
+import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.mdn.MDN;
 import org.apache.james.mdn.MDNReport;
 import org.apache.james.mdn.action.mode.DispositionActionMode;
@@ -137,14 +138,19 @@ public class RejectAction implements MailAction {
         reply.saveChanges();
         Address[] recipientAddresses = reply.getAllRecipients();
         if (recipientAddresses != null) {
-            context.post(MailImpl.builder()
+            MailImpl mail = MailImpl.builder()
                 .name(MailImpl.getId())
                 .addRecipients(Arrays.stream(recipientAddresses)
                     .map(address -> (InternetAddress) address)
                     .map(Throwing.function(MailAddress::new))
                     .collect(ImmutableList.toImmutableList()))
                 .mimeMessage(reply)
-                .build());
+                .build();
+            try {
+                context.post(mail);
+            } finally {
+                LifecycleUtil.dispose(mail);
+            }
         } else {
             LOGGER.info("Unable to send reject MDN. Could not determine the recipient.");
         }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/VacationAction.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/VacationAction.java
index 10eab29..bef3040 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/VacationAction.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/VacationAction.java
@@ -28,6 +28,7 @@ import javax.mail.MessagingException;
 import javax.mail.internet.AddressException;
 
 import org.apache.james.core.MailAddress;
+import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.server.core.MailImpl;
 import org.apache.jsieve.mail.Action;
 import org.apache.jsieve.mail.optional.ActionVacation;
@@ -66,12 +67,17 @@ public class VacationAction implements MailAction {
             .subject(actionVacation.getSubject())
             .build();
 
-        context.post(MailImpl.builder()
+        MailImpl replyMail = MailImpl.builder()
             .name(MailImpl.getId())
             .sender(vacationReply.getSender())
             .addRecipients(vacationReply.getRecipients())
             .mimeMessage(vacationReply.getMimeMessage())
-            .build());
+            .build();
+        try {
+            context.post(replyMail);
+        } finally {
+            LifecycleUtil.dispose(replyMail);
+        }
     }
 
     private boolean isStillInVacation(ActionVacation actionVacation, int dayDifference) {
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/managesieve/ManageSieveMailet.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/managesieve/ManageSieveMailet.java
index 8225a8a..e1c6013 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/managesieve/ManageSieveMailet.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/managesieve/ManageSieveMailet.java
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.util.Scanner;
 
 import javax.inject.Inject;
@@ -115,7 +116,7 @@ public class ManageSieveMailet extends GenericMailet implements MessageToCoreToM
         setHelpURL(getInitParameter("helpURL"));
         cache = getInitParameter("cache", true);
         transcoder = new MessageToCoreToMessage(new ManageSieveProcessor(
-                new ArgumentParser(new CoreProcessor(sieveRepository, usersRepository, sieveParser))),
+                new ArgumentParser(new CoreProcessor(sieveRepository, usersRepository, sieveParser), false)),
             this);
     }
 
@@ -197,7 +198,7 @@ public class ManageSieveMailet extends GenericMailet implements MessageToCoreToM
         Scanner scanner = null;
         try {
             stream = helpURL.openStream();
-            scanner = new Scanner(stream, "UTF-8");
+            scanner = new Scanner(stream, StandardCharsets.UTF_8);
             return scanner.useDelimiter("\\A").next();
         } catch (IOException ex) {
             throw new MessagingException("Unable to access help URL: " + helpURL.toExternalForm(), ex);
diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
index 97b579b..d131efb 100644
--- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
+++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
@@ -20,24 +20,53 @@
 package org.apache.james.managesieveserver.netty;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 
+import org.apache.james.protocols.api.CommandDetectionSession;
 import org.jboss.netty.channel.Channel;
 import org.jboss.netty.handler.stream.ChunkedStream;
 
-public class ChannelManageSieveResponseWriter {
+public class ChannelManageSieveResponseWriter implements CommandDetectionSession {
     private final Channel channel;
+    private String cumulation = null;
 
     public ChannelManageSieveResponseWriter(Channel channel) {
         this.channel = channel;
     }
 
-    public void write(String response) throws IOException {
+    public void write(String response) {
         if (channel.isConnected()) {
             InputStream in = new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8));
             channel.write(new ChunkedStream(in));
         }
     }
+
+    @Override
+    public boolean needsCommandInjectionDetection() {
+        return false;
+    }
+
+    @Override
+    public void startDetectingCommandInjection() {
+
+    }
+
+    @Override
+    public void stopDetectingCommandInjection() {
+
+    }
+
+    public void resetCumulation() {
+        cumulation = null;
+    }
+
+    public String cumulate(String s) {
+        if (cumulation == null || cumulation.equals("\r\n")) {
+            cumulation = s;
+        } else {
+            cumulation += s;
+        }
+        return cumulation;
+    }
 }
diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
index bb7963a..3274b9b 100644
--- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
+++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
@@ -27,6 +27,7 @@ import javax.net.ssl.SSLContext;
 import org.apache.james.managesieve.api.Session;
 import org.apache.james.managesieve.api.SessionTerminatedException;
 import org.apache.james.managesieve.transcode.ManageSieveProcessor;
+import org.apache.james.managesieve.transcode.NotEnoughDataException;
 import org.apache.james.managesieve.util.SettableSession;
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.jboss.netty.channel.Channel;
@@ -64,16 +65,24 @@ public class ManageSieveChannelUpstreamHandler extends SimpleChannelUpstreamHand
 
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+        ChannelManageSieveResponseWriter attachment = (ChannelManageSieveResponseWriter) ctx.getAttachment();
         try (Closeable closeable = ManageSieveMDCContext.from(ctx, attributes)) {
-            String request = (String) e.getMessage();
+            String request = attachment.cumulate((String) e.getMessage());
+            if (request.isEmpty() || request.startsWith("\r\n")) {
+                return;
+            }
+
             Session manageSieveSession = attributes.get(ctx.getChannel());
             String responseString = manageSieveProcessor.handleRequest(manageSieveSession, request);
-            ((ChannelManageSieveResponseWriter) ctx.getAttachment()).write(responseString);
+            attachment.resetCumulation();
+            attachment.write(responseString);
             if (manageSieveSession.getState() == Session.State.SSL_NEGOCIATION) {
                 turnSSLon(ctx.getChannel());
                 manageSieveSession.setSslEnabled(true);
                 manageSieveSession.setState(Session.State.UNAUTHENTICATED);
             }
+        } catch (NotEnoughDataException ex) {
+            // Do nothing will keep the cumulation
         }
     }
 
@@ -116,7 +125,7 @@ public class ManageSieveChannelUpstreamHandler extends SimpleChannelUpstreamHand
             attributes.set(ctx.getChannel(), session);
             ctx.setAttachment(new ChannelManageSieveResponseWriter(ctx.getChannel()));
             super.channelBound(ctx, e);
-            ((ChannelManageSieveResponseWriter) ctx.getAttachment()).write(manageSieveProcessor.getAdvertisedCapabilities());
+            ((ChannelManageSieveResponseWriter) ctx.getAttachment()).write(manageSieveProcessor.getAdvertisedCapabilities() + "OK\r\n");
         }
     }
 
diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveMDCContext.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveMDCContext.java
index 52727c1..2e44aa2 100644
--- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveMDCContext.java
+++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveMDCContext.java
@@ -24,6 +24,7 @@ import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.Optional;
 
+import org.apache.james.core.Username;
 import org.apache.james.managesieve.api.Session;
 import org.apache.james.util.MDCBuilder;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -61,7 +62,8 @@ public class ManageSieveMDCContext {
     private static MDCBuilder from(Session session) {
         return Optional.ofNullable(session)
             .map(s -> MDCBuilder.create()
-                .addToContext(MDCBuilder.USER, s.getUser().asString()))
+                .addToContextIfPresent(MDCBuilder.USER, Optional.ofNullable(s.getUser())
+                    .map(Username::asString)))
             .orElse(MDCBuilder.create());
     }
 }

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