NET-611 FTP does not validate command reply syntax fully

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/net/trunk@1782002 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2a42c65..d745052 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -63,7 +63,30 @@
      -->
 
     <body>
-        <release version="3.6" date="TBA" description="">
+        <release version="3.6" date="TBA" description="
+This is mainly a bug-fix release. See further details below.
+
+
+ This release is binary compatible with previous releases.
+ However it is not source compatible with releases before 3.4, as some methods were added to the interface NtpV3Packet in 3.4
+
+  The code now requires a minimum of Java 1.6.
+
+  Changes to functionality:
+  The FTP client now performs stricter checks on non-multiline command replies.
+  The 3 digit code must now be followed by a space and some text, as per RFC 959.
+  To suppress this stricter checking, call FTP#setStrictReplyParsing(false). This should not be needed with a well-behaved server.
+  Note also that if strict checking is disabled, some functions may unconditionally strip the next character after the code,
+without checking it if is a space.
+
+
+  Notable additions:
+  The POP3Mail examples can now get password from console, stdin or an environment variable.
+  
+">
+            <action issue="NET-611" type="fix" dev="sebb">
+            FTP does not validate command reply syntax fully
+            </action>
             <action issue="NET-609" type="fix" dev="sebb" due-to="Tqup3">
             DefaultUnixFTPFileEntryParserFactory Issue (leading spaces removal configuration)
             </action>
diff --git a/src/main/java/org/apache/commons/net/ftp/FTP.java b/src/main/java/org/apache/commons/net/ftp/FTP.java
index 0ded5a0..bac6d7d 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTP.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTP.java
@@ -236,6 +236,14 @@
     protected boolean strictMultilineParsing = false;
 
     /**
+     * If this is true, then non-multiline replies must have the format:
+     * 3 digit code <space> <text>
+     * If false, then the 3 digit code does not have to be followed by space
+     * See section 4.2 of RFC 959 for details.
+     */
+    private boolean strictReplyParsing = true;
+
+    /**
      * Wraps SocketClient._input_ to facilitate the reading of text
      * from the FTP control connection.  Do not access the control
      * connection via SocketClient._input_.  This member starts
@@ -339,25 +347,37 @@
 
         _replyLines.add(line);
 
-        // Get extra lines if message continues.
-        if (length > REPLY_CODE_LEN && line.charAt(REPLY_CODE_LEN) == '-')
-        {
-            do
-            {
-                line = _controlInput_.readLine();
+        // Check the server reply type
+        if (length > REPLY_CODE_LEN) {
+            char sep = line.charAt(REPLY_CODE_LEN);
+            // Get extra lines if message continues.
+            if (sep == '-') {
+                do
+                {
+                    line = _controlInput_.readLine();
 
-                if (line == null) {
-                    throw new FTPConnectionClosedException(
-                        "Connection closed without indication.");
+                    if (line == null) {
+                        throw new FTPConnectionClosedException(
+                            "Connection closed without indication.");
+                    }
+
+                    _replyLines.add(line);
+
+                    // The length() check handles problems that could arise from readLine()
+                    // returning too soon after encountering a naked CR or some other
+                    // anomaly.
                 }
+                while ( isStrictMultilineParsing() ? __strictCheck(line, code) : __lenientCheck(line));
 
-                _replyLines.add(line);
-
-                // The length() check handles problems that could arise from readLine()
-                // returning too soon after encountering a naked CR or some other
-                // anomaly.
+            } else if (isStrictReplyParsing()) {
+                if (length == REPLY_CODE_LEN + 1) { // expecting some text
+                    throw new MalformedServerReplyException("Truncated server reply: '" + line +"'");                    
+                } else if (sep != ' ') {
+                    throw new MalformedServerReplyException("Invalid server reply: '" + line +"'");
+                }
             }
-            while ( isStrictMultilineParsing() ? __strictCheck(line, code) : __lenientCheck(line));
+        } else if (isStrictReplyParsing()) {
+            throw new MalformedServerReplyException("Truncated server reply: '" + line +"'");
         }
 
         if (reportReply) {
@@ -1803,6 +1823,36 @@
     }
 
     /**
+     * Return whether strict non-multiline parsing is enabled, as per RFC 959, section 4.2.
+     * <p>
+     * The default is true, which requires the 3 digit code be followed by space and some text.
+     * <br>
+     * If false, only the 3 digit code is required (as was the case for versions up to 3.5)
+     * <br>
+     * @return True if strict (default), false if additional checks are not made
+     * @since 3.6
+     */
+    public boolean isStrictReplyParsing() {
+        return strictReplyParsing;
+    }
+
+    /**
+     * Set strict non-multiline parsing.
+     * <p>
+     * If true, it requires the 3 digit code be followed by space and some text.
+     * <br>
+     * If false, only the 3 digit code is required (as was the case for versions up to 3.5)
+     * <p>
+     * <b>This should not be required by a well-behaved FTP server</b>
+     * <br>
+     * @param strictReplyParsing the setting
+     * @since 3.6
+     */
+    public void setStrictReplyParsing(boolean strictReplyParsing) {
+        this.strictReplyParsing = strictReplyParsing;
+    }
+
+    /**
      * Provide command support to super-class
      */
     @Override