You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2021/02/20 22:30:09 UTC

[commons-net] 02/02: Sort methods.

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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-net.git

commit 0376449916aeae1c67348534f0e48b06d85dfa7f
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 20 17:30:01 2021 -0500

    Sort methods.
---
 .../apache/commons/net/DatagramSocketClient.java   |  242 +--
 .../apache/commons/net/DefaultSocketFactory.java   |  146 +-
 .../apache/commons/net/PrintCommandListener.java   |   30 +-
 .../apache/commons/net/ProtocolCommandEvent.java   |   66 +-
 .../apache/commons/net/ProtocolCommandSupport.java |   30 +-
 .../java/org/apache/commons/net/SocketClient.java  |  822 +++----
 .../org/apache/commons/net/bsd/RCommandClient.java |  202 +-
 .../org/apache/commons/net/bsd/RExecClient.java    |  114 +-
 .../org/apache/commons/net/bsd/RLoginClient.java   |   28 +-
 .../commons/net/chargen/CharGenUDPClient.java      |   50 +-
 .../commons/net/daytime/DaytimeUDPClient.java      |   20 +-
 .../commons/net/discard/DiscardUDPClient.java      |   48 +-
 .../org/apache/commons/net/echo/EchoUDPClient.java |   56 +-
 .../java/org/apache/commons/net/examples/Main.java |   16 +-
 .../commons/net/examples/ftp/FTPClientExample.java |   42 +-
 .../commons/net/examples/ftp/TFTPExample.java      |  116 +-
 .../commons/net/examples/mail/IMAPExportMbox.java  |  266 +--
 .../commons/net/examples/mail/IMAPImportMbox.java  |   66 +-
 .../commons/net/examples/mail/POP3ExportMbox.java  |   10 +-
 .../apache/commons/net/examples/mail/POP3Mail.java |   34 +-
 .../apache/commons/net/examples/mail/Utils.java    |    8 +-
 .../commons/net/examples/nntp/ExtendedNNTPOps.java |   28 +-
 .../net/examples/nntp/MessageThreading.java        |    6 +-
 .../apache/commons/net/examples/ntp/NTPClient.java |   62 +-
 .../commons/net/examples/ntp/SimpleNTPServer.java  |  166 +-
 .../commons/net/examples/ntp/TimeClient.java       |   48 +-
 .../apache/commons/net/examples/unix/rdate.java    |   46 +-
 .../apache/commons/net/finger/FingerClient.java    |   96 +-
 src/main/java/org/apache/commons/net/ftp/FTP.java  | 1630 +++++++-------
 .../apache/commons/net/ftp/FTPClientConfig.java    |  414 ++--
 .../org/apache/commons/net/ftp/FTPCommand.java     |   20 +-
 .../apache/commons/net/ftp/FTPFileEntryParser.java |   30 +-
 .../commons/net/ftp/FTPFileEntryParserImpl.java    |   32 +-
 .../org/apache/commons/net/ftp/FTPHTTPClient.java  |   50 +-
 .../apache/commons/net/ftp/FTPListParseEngine.java |  182 +-
 .../java/org/apache/commons/net/ftp/FTPReply.java  |   68 +-
 .../org/apache/commons/net/ftp/FTPSClient.java     | 1008 ++++-----
 .../apache/commons/net/ftp/FTPSSocketFactory.java  |   60 +-
 .../parser/ConfigurableFTPFileEntryParserImpl.java |   32 +-
 .../parser/DefaultFTPFileEntryParserFactory.java   |   80 +-
 .../net/ftp/parser/FTPFileEntryParserFactory.java  |   30 +-
 .../net/ftp/parser/FTPTimestampParserImpl.java     |  232 +-
 .../commons/net/ftp/parser/MLSxEntryParser.java    |  186 +-
 .../commons/net/ftp/parser/MVSFTPEntryParser.java  |  178 +-
 .../net/ftp/parser/MacOsPeterFTPEntryParser.java   |   28 +-
 .../commons/net/ftp/parser/NTFTPEntryParser.java   |   32 +-
 .../net/ftp/parser/NetwareFTPEntryParser.java      |   24 +-
 .../commons/net/ftp/parser/OS2FTPEntryParser.java  |   28 +-
 .../net/ftp/parser/OS400FTPEntryParser.java        |   56 +-
 .../ftp/parser/RegexFTPFileEntryParserImpl.java    |   78 +-
 .../commons/net/ftp/parser/UnixFTPEntryParser.java |   38 +-
 .../commons/net/ftp/parser/VMSFTPEntryParser.java  |   68 +-
 .../ftp/parser/VMSVersioningFTPEntryParser.java    |   14 +-
 .../commons/net/imap/AuthenticatingIMAPClient.java |  146 +-
 .../java/org/apache/commons/net/imap/IMAP.java     |  470 ++--
 .../org/apache/commons/net/imap/IMAPClient.java    |  740 +++----
 .../org/apache/commons/net/imap/IMAPCommand.java   |   30 +-
 .../org/apache/commons/net/imap/IMAPReply.java     |  114 +-
 .../org/apache/commons/net/imap/IMAPSClient.java   |  246 +--
 .../apache/commons/net/io/CopyStreamAdapter.java   |   22 +-
 .../org/apache/commons/net/io/CopyStreamEvent.java |   22 +-
 .../apache/commons/net/io/CopyStreamException.java |   18 +-
 .../commons/net/io/DotTerminatedMessageReader.java |   64 +-
 .../commons/net/io/DotTerminatedMessageWriter.java |  156 +-
 .../commons/net/io/FromNetASCIIInputStream.java    |   80 +-
 .../commons/net/io/FromNetASCIIOutputStream.java   |   98 +-
 .../apache/commons/net/io/SocketOutputStream.java  |   30 +-
 .../commons/net/io/ToNetASCIIInputStream.java      |   46 +-
 .../commons/net/io/ToNetASCIIOutputStream.java     |   66 +-
 src/main/java/org/apache/commons/net/io/Util.java  |  358 +--
 .../java/org/apache/commons/net/nntp/Article.java  |  316 +--
 .../apache/commons/net/nntp/ArticleIterator.java   |   10 +-
 .../java/org/apache/commons/net/nntp/NNTP.java     |  734 +++----
 .../org/apache/commons/net/nntp/NNTPClient.java    | 2288 ++++++++++----------
 .../org/apache/commons/net/nntp/NNTPCommand.java   |   10 +-
 .../org/apache/commons/net/nntp/NNTPReply.java     |   70 +-
 .../commons/net/nntp/NewGroupsOrNewsQuery.java     |  108 +-
 .../org/apache/commons/net/nntp/NewsgroupInfo.java |   84 +-
 .../apache/commons/net/nntp/NewsgroupIterator.java |   10 +-
 .../org/apache/commons/net/nntp/ReplyIterator.java |   18 +-
 .../apache/commons/net/nntp/SimpleNNTPHeader.java  |   46 +-
 .../org/apache/commons/net/nntp/Threadable.java    |    6 +-
 .../java/org/apache/commons/net/nntp/Threader.java |  252 +--
 .../org/apache/commons/net/ntp/NTPUDPClient.java   |   32 +-
 .../java/org/apache/commons/net/ntp/NtpUtils.java  |   60 +-
 .../java/org/apache/commons/net/ntp/NtpV3Impl.java |  578 ++---
 .../org/apache/commons/net/ntp/NtpV3Packet.java    |  154 +-
 .../java/org/apache/commons/net/ntp/TimeInfo.java  |  104 +-
 .../java/org/apache/commons/net/ntp/TimeStamp.java |  400 ++--
 .../commons/net/pop3/ExtendedPOP3Client.java       |   52 +-
 .../java/org/apache/commons/net/pop3/POP3.java     |  262 +--
 .../org/apache/commons/net/pop3/POP3Client.java    |  390 ++--
 .../org/apache/commons/net/pop3/POP3Command.java   |    8 +-
 .../org/apache/commons/net/pop3/POP3SClient.java   |  248 +--
 .../commons/net/smtp/AuthenticatingSMTPClient.java |  256 +--
 .../java/org/apache/commons/net/smtp/SMTP.java     |  436 ++--
 .../org/apache/commons/net/smtp/SMTPClient.java    |  252 +--
 .../org/apache/commons/net/smtp/SMTPCommand.java   |   10 +-
 .../org/apache/commons/net/smtp/SMTPReply.java     |   72 +-
 .../org/apache/commons/net/smtp/SMTPSClient.java   |  250 +--
 .../apache/commons/net/smtp/SimpleSMTPHeader.java  |   34 +-
 .../commons/net/telnet/EchoOptionHandler.java      |   18 +-
 .../commons/net/telnet/SimpleOptionHandler.java    |   22 +-
 .../net/telnet/SuppressGAOptionHandler.java        |   18 +-
 .../java/org/apache/commons/net/telnet/Telnet.java | 1008 ++++-----
 .../apache/commons/net/telnet/TelnetClient.java    |  332 +--
 .../commons/net/telnet/TelnetInputStream.java      |  482 ++---
 .../commons/net/telnet/TelnetOptionHandler.java    |  158 +-
 .../commons/net/telnet/TelnetOutputStream.java     |  100 +-
 .../net/telnet/TerminalTypeOptionHandler.java      |   34 +-
 .../net/telnet/WindowSizeOptionHandler.java        |   50 +-
 .../java/org/apache/commons/net/tftp/TFTP.java     |  132 +-
 .../org/apache/commons/net/tftp/TFTPAckPacket.java |   76 +-
 .../org/apache/commons/net/tftp/TFTPClient.java    |  144 +-
 .../apache/commons/net/tftp/TFTPDataPacket.java    |  176 +-
 .../apache/commons/net/tftp/TFTPErrorPacket.java   |  112 +-
 .../org/apache/commons/net/tftp/TFTPPacket.java    |   96 +-
 .../commons/net/tftp/TFTPReadRequestPacket.java    |   30 +-
 .../apache/commons/net/tftp/TFTPRequestPacket.java |   82 +-
 .../commons/net/tftp/TFTPWriteRequestPacket.java   |   30 +-
 .../org/apache/commons/net/time/TimeTCPClient.java |   36 +-
 .../org/apache/commons/net/time/TimeUDPClient.java |   76 +-
 .../java/org/apache/commons/net/util/Base64.java   | 1256 +++++------
 .../apache/commons/net/util/KeyManagerUtils.java   |  222 +-
 .../org/apache/commons/net/util/ListenerList.java  |   10 +-
 .../apache/commons/net/util/SSLContextUtils.java   |    8 +-
 .../apache/commons/net/util/SSLSocketUtils.java    |    8 +-
 .../org/apache/commons/net/util/SubnetUtils.java   |  446 ++--
 .../apache/commons/net/util/TrustManagerUtils.java |   20 +-
 .../org/apache/commons/net/whois/WhoisClient.java  |   30 +-
 .../commons/net/SocketClientFunctionalTest.java    |    4 +-
 .../org/apache/commons/net/SubnetUtilsTest.java    |  166 +-
 .../org/apache/commons/net/examples/MainTest.java  |   98 +-
 .../net/ftp/FTPClientConfigFunctionalTest.java     |   54 +-
 .../commons/net/ftp/FTPClientConfigTest.java       |  144 +-
 .../org/apache/commons/net/ftp/FTPClientTest.java  |  184 +-
 .../org/apache/commons/net/ftp/FTPSClientTest.java |   38 +-
 .../ftp/parser/CompositeFTPParseTestFramework.java |   46 +-
 .../DefaultFTPFileEntryParserFactoryTest.java      |   16 +-
 .../commons/net/ftp/parser/DownloadListings.java   |   92 +-
 .../parser/EnterpriseUnixFTPEntryParserTest.java   |  141 +-
 .../net/ftp/parser/FTPConfigEntryParserTest.java   |  112 +-
 .../net/ftp/parser/FTPParseTestFramework.java      |  148 +-
 .../net/ftp/parser/FTPTimestampParserImplTest.java |  542 ++---
 .../commons/net/ftp/parser/MLSDComparison.java     |  160 +-
 .../net/ftp/parser/MLSxEntryParserTest.java        |    8 +-
 .../net/ftp/parser/MVSFTPEntryParserTest.java      |   84 +-
 .../ftp/parser/MacOsPeterFTPEntryParserTest.java   |   64 +-
 .../net/ftp/parser/NTFTPEntryParserTest.java       |  346 +--
 .../net/ftp/parser/NetwareFTPEntryParserTest.java  |   12 +-
 .../net/ftp/parser/OS2FTPEntryParserTest.java      |   44 +-
 .../parser/OS400FTPEntryParserAdditionalTest.java  |   30 +-
 .../net/ftp/parser/OS400FTPEntryParserTest.java    |   80 +-
 .../net/ftp/parser/UnixFTPEntryParserTest.java     |  326 +--
 .../net/ftp/parser/VMSFTPEntryParserTest.java      |  208 +-
 .../net/io/DotTerminatedMessageReaderTest.java     |   96 +-
 .../commons/net/io/ToNetASCIIInputStreamTest.java  |   76 +-
 .../org/apache/commons/net/nntp/TestThreader.java  |   40 +-
 .../org/apache/commons/net/ntp/TestNtpPacket.java  |   62 +-
 .../org/apache/commons/net/ntp/TestTimeInfo.java   |   72 +-
 .../org/apache/commons/net/ntp/TimeStampTest.java  |   12 +-
 .../commons/net/pop3/POP3ClientCommandsTest.java   |  348 +--
 .../apache/commons/net/pop3/POP3ClientTest.java    |   50 +-
 .../commons/net/pop3/POP3ConstructorTest.java      |   20 +-
 .../commons/net/smtp/SimpleSMTPHeaderTestCase.java |  106 +-
 .../commons/net/telnet/EchoOptionHandlerTest.java  |   36 +-
 .../net/telnet/SimpleOptionHandlerTest.java        |   36 +-
 .../net/telnet/SuppressGAOptionHandlerTest.java    |   36 +-
 .../commons/net/telnet/TelnetClientTest.java       |  716 +++---
 .../telnet/TelnetOptionHandlerTestAbstract.java    |   34 +-
 .../commons/net/telnet/TelnetOptionTest.java       |   20 +-
 .../commons/net/telnet/TelnetTestSimpleServer.java |   74 +-
 .../net/telnet/TerminalTypeOptionHandlerTest.java  |   74 +-
 .../net/telnet/WindowSizeOptionHandlerTest.java    |   70 +-
 .../org/apache/commons/net/tftp/TFTPServer.java    | 1026 ++++-----
 .../apache/commons/net/tftp/TFTPServerMain.java    |   26 +-
 .../java/org/apache/commons/net/tftp/TFTPTest.java |  156 +-
 .../apache/commons/net/time/TimeTCPClientTest.java |   48 +-
 .../commons/net/time/TimeTestSimpleServer.java     |   56 +-
 .../org/apache/commons/net/util/Base64Test.java    |   82 +-
 .../java/org/apache/commons/net/util/UtilTest.java |  172 +-
 181 files changed, 14809 insertions(+), 14812 deletions(-)

diff --git a/src/main/java/org/apache/commons/net/DatagramSocketClient.java b/src/main/java/org/apache/commons/net/DatagramSocketClient.java
index d3abf93..2b081e5 100644
--- a/src/main/java/org/apache/commons/net/DatagramSocketClient.java
+++ b/src/main/java/org/apache/commons/net/DatagramSocketClient.java
@@ -89,68 +89,6 @@ public abstract class DatagramSocketClient
 
 
     /**
-     * Opens a DatagramSocket on the local host at the first available port.
-     * Also sets the timeout on the socket to the default timeout set
-     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
-     * <p>
-     * _isOpen_ is set to true after calling this method and _socket_
-     * is set to the newly opened socket.
-     *
-     * @throws SocketException If the socket could not be opened or the
-     *   timeout could not be set.
-     */
-    public void open() throws SocketException
-    {
-        _socket_ = _socketFactory_.createDatagramSocket();
-        _socket_.setSoTimeout(_timeout_);
-        _isOpen_ = true;
-    }
-
-
-    /**
-     * Opens a DatagramSocket on the local host at a specified port.
-     * Also sets the timeout on the socket to the default timeout set
-     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
-     * <p>
-     * _isOpen_ is set to true after calling this method and _socket_
-     * is set to the newly opened socket.
-     *
-     * @param port The port to use for the socket.
-     * @throws SocketException If the socket could not be opened or the
-     *   timeout could not be set.
-     */
-    public void open(final int port) throws SocketException
-    {
-        _socket_ = _socketFactory_.createDatagramSocket(port);
-        _socket_.setSoTimeout(_timeout_);
-        _isOpen_ = true;
-    }
-
-
-    /**
-     * Opens a DatagramSocket at the specified address on the local host
-     * at a specified port.
-     * Also sets the timeout on the socket to the default timeout set
-     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
-     * <p>
-     * _isOpen_ is set to true after calling this method and _socket_
-     * is set to the newly opened socket.
-     *
-     * @param port The port to use for the socket.
-     * @param laddr  The local address to use.
-     * @throws SocketException If the socket could not be opened or the
-     *   timeout could not be set.
-     */
-    public void open(final int port, final InetAddress laddr) throws SocketException
-    {
-        _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
-        _socket_.setSoTimeout(_timeout_);
-        _isOpen_ = true;
-    }
-
-
-
-    /**
      * Closes the DatagramSocket used for the connection.
      * You should call this method after you've finished using the class
      * instance and also before you call {@link #open open() }
@@ -167,33 +105,30 @@ public abstract class DatagramSocketClient
 
 
     /**
-     * Returns true if the client has a currently open socket.
+     * Gets the charset.
      *
-     * @return True if the client has a currently open socket, false otherwise.
+     * @return the charset.
+     * @since 3.3
      */
-    public boolean isOpen()
-    {
-        return _isOpen_;
+    public Charset getCharset() {
+        return charset;
     }
 
 
     /**
-     * Set the default timeout in milliseconds to use when opening a socket.
-     * After a call to open, the timeout for the socket is set using this value.
-     * This method should be used prior to a call to {@link #open open()}
-     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
-     * which operates on the currently open socket.  _timeout_ contains
-     * the new timeout value.
+     * Gets the charset name.
      *
-     * @param timeout  The timeout in milliseconds to use for the datagram socket
-     *                 connection.
+     * @return the charset name.
+     * @since 3.3
+     * @deprecated Use {@link #getCharset()} instead
      */
-    public void setDefaultTimeout(final int timeout)
-    {
-        _timeout_ = timeout;
+    @Deprecated
+    public String getCharsetName() {
+        return charset.name();
     }
 
 
+
     /**
      * Returns the default timeout in milliseconds that is used when
      * opening a socket.
@@ -208,17 +143,29 @@ public abstract class DatagramSocketClient
 
 
     /**
-     * Set the timeout in milliseconds of a currently open connection.
-     * Only call this method after a connection has been opened
-     * by {@link #open open()}.
+     * Returns the local address to which the client's socket is bound.
+     * If you call this method when the client socket is not open, a
+     * NullPointerException is thrown.
      *
-     * @param timeout  The timeout in milliseconds to use for the currently
-     *                 open datagram socket connection.
-     * @throws SocketException if an error setting the timeout
+     * @return The local address to which the client's socket is bound.
      */
-    public void setSoTimeout(final int timeout) throws SocketException
+    public InetAddress getLocalAddress()
     {
-        _socket_.setSoTimeout(timeout);
+        return _socket_.getLocalAddress();
+    }
+
+
+    /**
+     * Returns the port number of the open socket on the local host used
+     * for the connection.  If you call this method when the client socket
+     * is not open, a NullPointerException is thrown.
+     *
+     * @return The port number of the open socket on the local host used
+     *         for the connection.
+     */
+    public int getLocalPort()
+    {
+        return _socket_.getLocalPort();
     }
 
 
@@ -237,33 +184,88 @@ public abstract class DatagramSocketClient
 
 
     /**
-     * Returns the port number of the open socket on the local host used
-     * for the connection.  If you call this method when the client socket
-     * is not open, a NullPointerException is thrown.
+     * Returns true if the client has a currently open socket.
      *
-     * @return The port number of the open socket on the local host used
-     *         for the connection.
+     * @return True if the client has a currently open socket, false otherwise.
      */
-    public int getLocalPort()
+    public boolean isOpen()
     {
-        return _socket_.getLocalPort();
+        return _isOpen_;
     }
 
 
     /**
-     * Returns the local address to which the client's socket is bound.
-     * If you call this method when the client socket is not open, a
-     * NullPointerException is thrown.
+     * Opens a DatagramSocket on the local host at the first available port.
+     * Also sets the timeout on the socket to the default timeout set
+     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
+     * <p>
+     * _isOpen_ is set to true after calling this method and _socket_
+     * is set to the newly opened socket.
      *
-     * @return The local address to which the client's socket is bound.
+     * @throws SocketException If the socket could not be opened or the
+     *   timeout could not be set.
      */
-    public InetAddress getLocalAddress()
+    public void open() throws SocketException
     {
-        return _socket_.getLocalAddress();
+        _socket_ = _socketFactory_.createDatagramSocket();
+        _socket_.setSoTimeout(_timeout_);
+        _isOpen_ = true;
     }
 
 
     /**
+     * Opens a DatagramSocket on the local host at a specified port.
+     * Also sets the timeout on the socket to the default timeout set
+     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
+     * <p>
+     * _isOpen_ is set to true after calling this method and _socket_
+     * is set to the newly opened socket.
+     *
+     * @param port The port to use for the socket.
+     * @throws SocketException If the socket could not be opened or the
+     *   timeout could not be set.
+     */
+    public void open(final int port) throws SocketException
+    {
+        _socket_ = _socketFactory_.createDatagramSocket(port);
+        _socket_.setSoTimeout(_timeout_);
+        _isOpen_ = true;
+    }
+
+
+    /**
+     * Opens a DatagramSocket at the specified address on the local host
+     * at a specified port.
+     * Also sets the timeout on the socket to the default timeout set
+     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
+     * <p>
+     * _isOpen_ is set to true after calling this method and _socket_
+     * is set to the newly opened socket.
+     *
+     * @param port The port to use for the socket.
+     * @param laddr  The local address to use.
+     * @throws SocketException If the socket could not be opened or the
+     *   timeout could not be set.
+     */
+    public void open(final int port, final InetAddress laddr) throws SocketException
+    {
+        _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
+        _socket_.setSoTimeout(_timeout_);
+        _isOpen_ = true;
+    }
+
+
+    /**
+     * Sets the charset.
+     *
+     * @param charset the charset.
+     * @since 3.3
+     */
+    public void setCharset(final Charset charset) {
+        this.charset = charset;
+    }
+
+    /**
      * Sets the DatagramSocketFactory used by the DatagramSocketClient
      * to open DatagramSockets.  If the factory value is null, then a default
      * factory is used (only do this to reset the factory after having
@@ -282,34 +284,32 @@ public abstract class DatagramSocketClient
     }
 
     /**
-     * Gets the charset name.
-     *
-     * @return the charset name.
-     * @since 3.3
-     * @deprecated Use {@link #getCharset()} instead
-     */
-    @Deprecated
-    public String getCharsetName() {
-        return charset.name();
-    }
-
-    /**
-     * Gets the charset.
+     * Set the default timeout in milliseconds to use when opening a socket.
+     * After a call to open, the timeout for the socket is set using this value.
+     * This method should be used prior to a call to {@link #open open()}
+     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
+     * which operates on the currently open socket.  _timeout_ contains
+     * the new timeout value.
      *
-     * @return the charset.
-     * @since 3.3
+     * @param timeout  The timeout in milliseconds to use for the datagram socket
+     *                 connection.
      */
-    public Charset getCharset() {
-        return charset;
+    public void setDefaultTimeout(final int timeout)
+    {
+        _timeout_ = timeout;
     }
 
     /**
-     * Sets the charset.
+     * Set the timeout in milliseconds of a currently open connection.
+     * Only call this method after a connection has been opened
+     * by {@link #open open()}.
      *
-     * @param charset the charset.
-     * @since 3.3
+     * @param timeout  The timeout in milliseconds to use for the currently
+     *                 open datagram socket connection.
+     * @throws SocketException if an error setting the timeout
      */
-    public void setCharset(final Charset charset) {
-        this.charset = charset;
+    public void setSoTimeout(final int timeout) throws SocketException
+    {
+        _socket_.setSoTimeout(timeout);
     }
 }
diff --git a/src/main/java/org/apache/commons/net/DefaultSocketFactory.java b/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
index 7eda470..8214165 100644
--- a/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
+++ b/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
@@ -65,6 +65,57 @@ public class DefaultSocketFactory extends SocketFactory
     }
 
     /**
+     * Creates a ServerSocket bound to a specified port.  A port
+     * of 0 will create the ServerSocket on a system-determined free port.
+     *
+     * @param port  The port on which to listen, or 0 to use any free port.
+     * @return A ServerSocket that will listen on a specified port.
+     * @throws IOException If an I/O error occurs while creating
+     *                        the ServerSocket.
+     */
+    public ServerSocket createServerSocket(final int port) throws IOException
+    {
+        return new ServerSocket(port);
+    }
+
+    /**
+     * Creates a ServerSocket bound to a specified port with a given
+     * maximum queue length for incoming connections.  A port of 0 will
+     * create the ServerSocket on a system-determined free port.
+     *
+     * @param port  The port on which to listen, or 0 to use any free port.
+     * @param backlog  The maximum length of the queue for incoming connections.
+     * @return A ServerSocket that will listen on a specified port.
+     * @throws IOException If an I/O error occurs while creating
+     *                        the ServerSocket.
+     */
+    public ServerSocket createServerSocket(final int port, final int backlog)
+    throws IOException
+    {
+        return new ServerSocket(port, backlog);
+    }
+
+    /**
+     * Creates a ServerSocket bound to a specified port on a given local
+     * address with a given maximum queue length for incoming connections.
+     * A port of 0 will
+     * create the ServerSocket on a system-determined free port.
+     *
+     * @param port  The port on which to listen, or 0 to use any free port.
+     * @param backlog  The maximum length of the queue for incoming connections.
+     * @param bindAddr  The local address to which the ServerSocket should bind.
+     * @return A ServerSocket that will listen on a specified port.
+     * @throws IOException If an I/O error occurs while creating
+     *                        the ServerSocket.
+     */
+    public ServerSocket createServerSocket(final int port, final int backlog,
+                                           final InetAddress bindAddr)
+    throws IOException
+    {
+        return new ServerSocket(port, backlog, bindAddr);
+    }
+
+    /**
      * Creates an unconnected Socket.
      *
      * @return A new unconnected Socket.
@@ -84,147 +135,96 @@ public class DefaultSocketFactory extends SocketFactory
     /**
      * Creates a Socket connected to the given host and port.
      *
-     * @param host The hostname to connect to.
+     * @param address The address of the host to connect to.
      * @param port The port to connect to.
      * @return A Socket connected to the given host and port.
-     * @throws UnknownHostException  If the hostname cannot be resolved.
      * @throws IOException If an I/O error occurs while creating the Socket.
      */
     @Override
-    public Socket createSocket(final String host, final int port)
-    throws UnknownHostException, IOException
+    public Socket createSocket(final InetAddress address, final int port)
+    throws IOException
     {
         if (connProxy != null)
         {
             final Socket s = new Socket(connProxy);
-            s.connect(new InetSocketAddress(host, port));
+            s.connect(new InetSocketAddress(address, port));
             return s;
         }
-        return new Socket(host, port);
+        return new Socket(address, port);
     }
 
     /**
-     * Creates a Socket connected to the given host and port.
+     * Creates a Socket connected to the given host and port and
+     * originating from the specified local address and port.
      *
      * @param address The address of the host to connect to.
      * @param port The port to connect to.
+     * @param localAddr  The local address to use.
+     * @param localPort  The local port to use.
      * @return A Socket connected to the given host and port.
      * @throws IOException If an I/O error occurs while creating the Socket.
      */
     @Override
-    public Socket createSocket(final InetAddress address, final int port)
+    public Socket createSocket(final InetAddress address, final int port,
+                               final InetAddress localAddr, final int localPort)
     throws IOException
     {
         if (connProxy != null)
         {
             final Socket s = new Socket(connProxy);
+            s.bind(new InetSocketAddress(localAddr, localPort));
             s.connect(new InetSocketAddress(address, port));
             return s;
         }
-        return new Socket(address, port);
+        return new Socket(address, port, localAddr, localPort);
     }
 
     /**
-     * Creates a Socket connected to the given host and port and
-     * originating from the specified local address and port.
+     * Creates a Socket connected to the given host and port.
      *
      * @param host The hostname to connect to.
      * @param port The port to connect to.
-     * @param localAddr  The local address to use.
-     * @param localPort  The local port to use.
      * @return A Socket connected to the given host and port.
      * @throws UnknownHostException  If the hostname cannot be resolved.
      * @throws IOException If an I/O error occurs while creating the Socket.
      */
     @Override
-    public Socket createSocket(final String host, final int port,
-                               final InetAddress localAddr, final int localPort)
+    public Socket createSocket(final String host, final int port)
     throws UnknownHostException, IOException
     {
         if (connProxy != null)
         {
             final Socket s = new Socket(connProxy);
-            s.bind(new InetSocketAddress(localAddr, localPort));
             s.connect(new InetSocketAddress(host, port));
             return s;
         }
-        return new Socket(host, port, localAddr, localPort);
+        return new Socket(host, port);
     }
 
     /**
      * Creates a Socket connected to the given host and port and
      * originating from the specified local address and port.
      *
-     * @param address The address of the host to connect to.
+     * @param host The hostname to connect to.
      * @param port The port to connect to.
      * @param localAddr  The local address to use.
      * @param localPort  The local port to use.
      * @return A Socket connected to the given host and port.
+     * @throws UnknownHostException  If the hostname cannot be resolved.
      * @throws IOException If an I/O error occurs while creating the Socket.
      */
     @Override
-    public Socket createSocket(final InetAddress address, final int port,
+    public Socket createSocket(final String host, final int port,
                                final InetAddress localAddr, final int localPort)
-    throws IOException
+    throws UnknownHostException, IOException
     {
         if (connProxy != null)
         {
             final Socket s = new Socket(connProxy);
             s.bind(new InetSocketAddress(localAddr, localPort));
-            s.connect(new InetSocketAddress(address, port));
+            s.connect(new InetSocketAddress(host, port));
             return s;
         }
-        return new Socket(address, port, localAddr, localPort);
-    }
-
-    /**
-     * Creates a ServerSocket bound to a specified port.  A port
-     * of 0 will create the ServerSocket on a system-determined free port.
-     *
-     * @param port  The port on which to listen, or 0 to use any free port.
-     * @return A ServerSocket that will listen on a specified port.
-     * @throws IOException If an I/O error occurs while creating
-     *                        the ServerSocket.
-     */
-    public ServerSocket createServerSocket(final int port) throws IOException
-    {
-        return new ServerSocket(port);
-    }
-
-    /**
-     * Creates a ServerSocket bound to a specified port with a given
-     * maximum queue length for incoming connections.  A port of 0 will
-     * create the ServerSocket on a system-determined free port.
-     *
-     * @param port  The port on which to listen, or 0 to use any free port.
-     * @param backlog  The maximum length of the queue for incoming connections.
-     * @return A ServerSocket that will listen on a specified port.
-     * @throws IOException If an I/O error occurs while creating
-     *                        the ServerSocket.
-     */
-    public ServerSocket createServerSocket(final int port, final int backlog)
-    throws IOException
-    {
-        return new ServerSocket(port, backlog);
-    }
-
-    /**
-     * Creates a ServerSocket bound to a specified port on a given local
-     * address with a given maximum queue length for incoming connections.
-     * A port of 0 will
-     * create the ServerSocket on a system-determined free port.
-     *
-     * @param port  The port on which to listen, or 0 to use any free port.
-     * @param backlog  The maximum length of the queue for incoming connections.
-     * @param bindAddr  The local address to which the ServerSocket should bind.
-     * @return A ServerSocket that will listen on a specified port.
-     * @throws IOException If an I/O error occurs while creating
-     *                        the ServerSocket.
-     */
-    public ServerSocket createServerSocket(final int port, final int backlog,
-                                           final InetAddress bindAddr)
-    throws IOException
-    {
-        return new ServerSocket(port, backlog, bindAddr);
+        return new Socket(host, port, localAddr, localPort);
     }
 }
diff --git a/src/main/java/org/apache/commons/net/PrintCommandListener.java b/src/main/java/org/apache/commons/net/PrintCommandListener.java
index 3b6e087..c3f4d76 100644
--- a/src/main/java/org/apache/commons/net/PrintCommandListener.java
+++ b/src/main/java/org/apache/commons/net/PrintCommandListener.java
@@ -147,6 +147,21 @@ public class PrintCommandListener implements ProtocolCommandListener
         this.directionMarker = showDirection;
     }
 
+    private String getPrintableString(final String msg){
+        if (eolMarker == 0) {
+            return msg;
+        }
+        final int pos = msg.indexOf(SocketClient.NETASCII_EOL);
+        if (pos > 0) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(msg.substring(0,pos));
+            sb.append(eolMarker);
+            sb.append(msg.substring(pos));
+            return sb.toString();
+        }
+        return msg;
+    }
+
     @Override
     public void protocolCommandSent(final ProtocolCommandEvent event)
     {
@@ -175,21 +190,6 @@ public class PrintCommandListener implements ProtocolCommandListener
         writer.flush();
     }
 
-    private String getPrintableString(final String msg){
-        if (eolMarker == 0) {
-            return msg;
-        }
-        final int pos = msg.indexOf(SocketClient.NETASCII_EOL);
-        if (pos > 0) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append(msg.substring(0,pos));
-            sb.append(eolMarker);
-            sb.append(msg.substring(pos));
-            return sb.toString();
-        }
-        return msg;
-    }
-
     @Override
     public void protocolReplyReceived(final ProtocolCommandEvent event)
     {
diff --git a/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java b/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java
index 921fbde..5cb7491 100644
--- a/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java
+++ b/src/main/java/org/apache/commons/net/ProtocolCommandEvent.java
@@ -41,28 +41,6 @@ public class ProtocolCommandEvent extends EventObject
     private final String message, command;
 
     /**
-     * Creates a ProtocolCommandEvent signalling a command was sent to
-     * the server.  ProtocolCommandEvents created with this constructor
-     * should only be sent after a command has been sent, but before the
-     * reply has been received.
-     *
-     * @param source  The source of the event.
-     * @param command The string representation of the command type sent, not
-     *      including the arguments (e.g., "STAT" or "GET").
-     * @param message The entire command string verbatim as sent to the server,
-     *        including all arguments.
-     */
-    public ProtocolCommandEvent(final Object source, final String command, final String message)
-    {
-        super(source);
-        this.replyCode = 0;
-        this.message = message;
-        this.isCommand = true;
-        this.command = command;
-    }
-
-
-    /**
      * Creates a ProtocolCommandEvent signalling a reply to a command was
      * received.  ProtocolCommandEvents created with this constructor
      * should only be sent after a complete command reply has been received
@@ -85,6 +63,28 @@ public class ProtocolCommandEvent extends EventObject
         this.command = null;
     }
 
+
+    /**
+     * Creates a ProtocolCommandEvent signalling a command was sent to
+     * the server.  ProtocolCommandEvents created with this constructor
+     * should only be sent after a command has been sent, but before the
+     * reply has been received.
+     *
+     * @param source  The source of the event.
+     * @param command The string representation of the command type sent, not
+     *      including the arguments (e.g., "STAT" or "GET").
+     * @param message The entire command string verbatim as sent to the server,
+     *        including all arguments.
+     */
+    public ProtocolCommandEvent(final Object source, final String command, final String message)
+    {
+        super(source);
+        this.replyCode = 0;
+        this.message = message;
+        this.isCommand = true;
+        this.command = command;
+    }
+
     /**
      * Returns the string representation of the command type sent (e.g., "STAT"
      * or "GET").  If the ProtocolCommandEvent is a reply event, then null
@@ -100,6 +100,17 @@ public class ProtocolCommandEvent extends EventObject
 
 
     /**
+     * Returns the entire message sent to or received from the server.
+     * Includes the line terminator.
+     *
+     * @return The entire message sent to or received from the server.
+     */
+    public String getMessage()
+    {
+        return message;
+    }
+
+    /**
      * Returns the reply code of the received server reply.  Undefined if
      * this is not a reply event.
      *
@@ -134,15 +145,4 @@ public class ProtocolCommandEvent extends EventObject
     {
         return !isCommand();
     }
-
-    /**
-     * Returns the entire message sent to or received from the server.
-     * Includes the line terminator.
-     *
-     * @return The entire message sent to or received from the server.
-     */
-    public String getMessage()
-    {
-        return message;
-    }
 }
diff --git a/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java b/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java
index c55a9d3..c2e409d 100644
--- a/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java
+++ b/src/main/java/org/apache/commons/net/ProtocolCommandSupport.java
@@ -54,6 +54,16 @@ public class ProtocolCommandSupport implements Serializable
 
 
     /**
+     * Adds a ProtocolCommandListener.
+     *
+     * @param listener  The ProtocolCommandListener to add.
+     */
+    public void addProtocolCommandListener(final ProtocolCommandListener listener)
+    {
+        listeners.addListener(listener);
+    }
+
+    /**
      * Fires a ProtocolCommandEvent signalling the sending of a command to all
      * registered listeners, invoking their
      * {@link org.apache.commons.net.ProtocolCommandListener#protocolCommandSent protocolCommandSent() }
@@ -101,15 +111,16 @@ public class ProtocolCommandSupport implements Serializable
     }
 
     /**
-     * Adds a ProtocolCommandListener.
+     * Returns the number of ProtocolCommandListeners currently registered.
      *
-     * @param listener  The ProtocolCommandListener to add.
+     * @return The number of ProtocolCommandListeners currently registered.
      */
-    public void addProtocolCommandListener(final ProtocolCommandListener listener)
+    public int getListenerCount()
     {
-        listeners.addListener(listener);
+        return listeners.getListenerCount();
     }
 
+
     /**
      * Removes a ProtocolCommandListener.
      *
@@ -120,16 +131,5 @@ public class ProtocolCommandSupport implements Serializable
         listeners.removeListener(listener);
     }
 
-
-    /**
-     * Returns the number of ProtocolCommandListeners currently registered.
-     *
-     * @return The number of ProtocolCommandListeners currently registered.
-     */
-    public int getListenerCount()
-    {
-        return listeners.getListenerCount();
-    }
-
 }
 
diff --git a/src/main/java/org/apache/commons/net/SocketClient.java b/src/main/java/org/apache/commons/net/SocketClient.java
index 33068db..eddf9ab 100644
--- a/src/main/java/org/apache/commons/net/SocketClient.java
+++ b/src/main/java/org/apache/commons/net/SocketClient.java
@@ -68,6 +68,9 @@ public abstract class SocketClient
     private static final ServerSocketFactory DEFAULT_SERVER_SOCKET_FACTORY =
             ServerSocketFactory.getDefault();
 
+    /** The socket's connect timeout (0 = infinite timeout) */
+    private static final int DEFAULT_CONNECT_TIMEOUT = 60000;
+
     /**
      * A ProtocolCommandSupport object used to manage the registering of
      * ProtocolCommandListeners and the firing of ProtocolCommandEvents.
@@ -98,9 +101,6 @@ public abstract class SocketClient
     /** The socket's ServerSocket Factory. */
     protected ServerSocketFactory _serverSocketFactory_;
 
-    /** The socket's connect timeout (0 = infinite timeout) */
-    private static final int DEFAULT_CONNECT_TIMEOUT = 60000;
-
     protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
 
     /** Hint for SO_RCVBUF size */
@@ -137,6 +137,24 @@ public abstract class SocketClient
     }
 
 
+    // helper method to allow code to be shared with connect(String,...) methods
+    private void _connect(final InetAddress host, final int port, final InetAddress localAddr, final int localPort)
+        throws SocketException, IOException
+    {
+        _socket_ = _socketFactory_.createSocket();
+        if (receiveBufferSize != -1) {
+            _socket_.setReceiveBufferSize(receiveBufferSize);
+        }
+        if (sendBufferSize != -1) {
+            _socket_.setSendBufferSize(sendBufferSize);
+        }
+        if (localAddr != null) {
+            _socket_.bind(new InetSocketAddress(localAddr, localPort));
+        }
+        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
+        _connectAction_();
+    }
+
     /**
      * Because there are so many connect() methods, the _connectAction_()
      * method is provided as a means of performing some action immediately
@@ -162,6 +180,16 @@ public abstract class SocketClient
     }
 
     /**
+     * Adds a ProtocolCommandListener.
+     *
+     * @param listener  The ProtocolCommandListener to add.
+     * @since 3.0
+     */
+    public void addProtocolCommandListener(final ProtocolCommandListener listener) {
+        getCommandSupport().addProtocolCommandListener(listener);
+    }
+
+    /**
      * Applies socket attributes.
      *
      * @throws SocketException if there is an error in the underlying protocol, such as a TCP error.
@@ -171,45 +199,64 @@ public abstract class SocketClient
         _socket_.setSoTimeout(_timeout_);
     }
 
+
+    private void closeQuietly(final Closeable close){
+        if (close != null){
+            try {
+                close.close();
+            } catch (final IOException e) {
+                // Ignored
+            }
+        }
+    }
+
+    private void closeQuietly(final Socket socket) {
+        if (socket != null){
+            try {
+                socket.close();
+            } catch (final IOException e) {
+                // Ignored
+            }
+        }
+    }
+
     /**
-     * Opens a Socket connected to a remote host at the specified port and
-     * originating from the current host at a system assigned port.
+     * Opens a Socket connected to a remote host at the current default port
+     * and originating from the current host at a system assigned port.
      * Before returning, {@link #_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      * <p>
      * @param host  The remote host.
-     * @param port  The port to connect to on the remote host.
      * @throws SocketException If the socket timeout could not be set.
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
      */
-    public void connect(final InetAddress host, final int port)
-    throws SocketException, IOException
+    public void connect(final InetAddress host) throws SocketException, IOException
     {
         _hostname_ = null;
-        _connect(host, port, null, -1);
+        connect(host, _defaultPort_);
     }
 
+
     /**
      * Opens a Socket connected to a remote host at the specified port and
      * originating from the current host at a system assigned port.
      * Before returning, {@link #_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      * <p>
-     * @param hostname  The name of the remote host.
+     * @param host  The remote host.
      * @param port  The port to connect to on the remote host.
      * @throws SocketException If the socket timeout could not be set.
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
-     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
      */
-    public void connect(final String hostname, final int port)
+    public void connect(final InetAddress host, final int port)
     throws SocketException, IOException
     {
-        _hostname_ = hostname;
-        _connect(InetAddress.getByName(hostname), port, null, -1);
+        _hostname_ = null;
+        _connect(host, port, null, -1);
     }
 
 
@@ -236,86 +283,75 @@ public abstract class SocketClient
         _connect(host, port, localAddr, localPort);
     }
 
-    // helper method to allow code to be shared with connect(String,...) methods
-    private void _connect(final InetAddress host, final int port, final InetAddress localAddr, final int localPort)
-        throws SocketException, IOException
-    {
-        _socket_ = _socketFactory_.createSocket();
-        if (receiveBufferSize != -1) {
-            _socket_.setReceiveBufferSize(receiveBufferSize);
-        }
-        if (sendBufferSize != -1) {
-            _socket_.setSendBufferSize(sendBufferSize);
-        }
-        if (localAddr != null) {
-            _socket_.bind(new InetSocketAddress(localAddr, localPort));
-        }
-        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
-        _connectAction_();
-    }
 
     /**
-     * Opens a Socket connected to a remote host at the specified port and
-     * originating from the specified local address and port.
+     * Opens a Socket connected to a remote host at the current default
+     * port and originating from the current host at a system assigned port.
      * Before returning, {@link #_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      * <p>
      * @param hostname  The name of the remote host.
-     * @param port  The port to connect to on the remote host.
-     * @param localAddr  The local address to use.
-     * @param localPort  The local port to use.
      * @throws SocketException If the socket timeout could not be set.
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
      * @throws java.net.UnknownHostException If the hostname cannot be resolved.
      */
-    public void connect(final String hostname, final int port,
-                        final InetAddress localAddr, final int localPort)
-    throws SocketException, IOException
+    public void connect(final String hostname) throws SocketException, IOException
     {
-        _hostname_ = hostname;
-       _connect(InetAddress.getByName(hostname), port, localAddr, localPort);
+        connect(hostname, _defaultPort_);
     }
 
-
     /**
-     * Opens a Socket connected to a remote host at the current default port
-     * and originating from the current host at a system assigned port.
+     * Opens a Socket connected to a remote host at the specified port and
+     * originating from the current host at a system assigned port.
      * Before returning, {@link #_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      * <p>
-     * @param host  The remote host.
+     * @param hostname  The name of the remote host.
+     * @param port  The port to connect to on the remote host.
      * @throws SocketException If the socket timeout could not be set.
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
+     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
      */
-    public void connect(final InetAddress host) throws SocketException, IOException
+    public void connect(final String hostname, final int port)
+    throws SocketException, IOException
     {
-        _hostname_ = null;
-        connect(host, _defaultPort_);
+        _hostname_ = hostname;
+        _connect(InetAddress.getByName(hostname), port, null, -1);
     }
 
-
     /**
-     * Opens a Socket connected to a remote host at the current default
-     * port and originating from the current host at a system assigned port.
+     * Opens a Socket connected to a remote host at the specified port and
+     * originating from the specified local address and port.
      * Before returning, {@link #_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      * <p>
      * @param hostname  The name of the remote host.
+     * @param port  The port to connect to on the remote host.
+     * @param localAddr  The local address to use.
+     * @param localPort  The local port to use.
      * @throws SocketException If the socket timeout could not be set.
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
      * @throws java.net.UnknownHostException If the hostname cannot be resolved.
      */
-    public void connect(final String hostname) throws SocketException, IOException
+    public void connect(final String hostname, final int port,
+                        final InetAddress localAddr, final int localPort)
+    throws SocketException, IOException
     {
-        connect(hostname, _defaultPort_);
+        _hostname_ = hostname;
+       _connect(InetAddress.getByName(hostname), port, localAddr, localPort);
+    }
+    /**
+     * Create the CommandSupport instance if required
+     */
+    protected void createCommandSupport(){
+        commandSupport = new ProtocolCommandSupport(this);
     }
-
 
     /**
      * Disconnects the socket connection.
@@ -338,132 +374,89 @@ public abstract class SocketClient
         _output_ = null;
     }
 
-    private void closeQuietly(final Socket socket) {
-        if (socket != null){
-            try {
-                socket.close();
-            } catch (final IOException e) {
-                // Ignored
-            }
-        }
-    }
-
-    private void closeQuietly(final Closeable close){
-        if (close != null){
-            try {
-                close.close();
-            } catch (final IOException e) {
-                // Ignored
-            }
-        }
-    }
     /**
-     * Returns true if the client is currently connected to a server.
-     * <p>
-     * Delegates to {@link Socket#isConnected()}
-     * @return True if the client is currently connected to a server,
-     *         false otherwise.
+     * If there are any listeners, send them the command details.
+     *
+     * @param command the command name
+     * @param message the complete message, including command name
+     * @since 3.0
      */
-    public boolean isConnected()
-    {
-        if (_socket_ == null) {
-            return false;
+    protected void fireCommandSent(final String command, final String message) {
+        if (getCommandSupport().getListenerCount() > 0) {
+            getCommandSupport().fireCommandSent(command, message);
         }
-
-        return _socket_.isConnected();
     }
 
     /**
-     * Make various checks on the socket to test if it is available for use.
-     * Note that the only sure test is to use it, but these checks may help
-     * in some cases.
-     * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
-     * @return {@code true} if the socket appears to be available for use
+     * If there are any listeners, send them the reply details.
+     *
+     * @param replyCode the code extracted from the reply
+     * @param reply the full reply text
      * @since 3.0
      */
-    @SuppressWarnings("resource")
-    public boolean isAvailable(){
-        if (isConnected()) {
-            try
-            {
-                if (_socket_.getInetAddress() == null) {
-                    return false;
-                }
-                if (_socket_.getPort() == 0) {
-                    return false;
-                }
-                if (_socket_.getRemoteSocketAddress() == null) {
-                    return false;
-                }
-                if (_socket_.isClosed()) {
-                    return false;
-                }
-                /* these aren't exact checks (a Socket can be half-open),
-                   but since we usually require two-way data transfer,
-                   we check these here too: */
-                if (_socket_.isInputShutdown()) {
-                    return false;
-                }
-                if (_socket_.isOutputShutdown()) {
-                    return false;
-                }
-                /* ignore the result, catch exceptions: */
-                // No need to close
-                _socket_.getInputStream();
-                // No need to close
-                _socket_.getOutputStream();
-            }
-            catch (final IOException ioex)
-            {
-                return false;
-            }
-            return true;
+    protected void fireReplyReceived(final int replyCode, final String reply) {
+        if (getCommandSupport().getListenerCount() > 0) {
+            getCommandSupport().fireReplyReceived(replyCode, reply);
         }
-        return false;
     }
 
+
     /**
-     * Sets the default port the SocketClient should connect to when a port
-     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
-     * variable stores this value.  If never set, the default port is equal
-     * to zero.
-     * <p>
-     * @param port  The default port to set.
+     * Gets the charset.
+     *
+     * @return the charset.
+     * @since 3.3
      */
-    public void setDefaultPort(final int port)
-    {
-        _defaultPort_ = port;
+    public Charset getCharset() {
+        return charset;
     }
 
+
     /**
-     * Returns the current value of the default port (stored in
-     * {@link #_defaultPort_  _defaultPort_ }).
-     * <p>
-     * @return The current value of the default port.
-     */
-    public int getDefaultPort()
-    {
-        return _defaultPort_;
+     * Gets the charset name.
+     *
+     * @return the charset.
+     * @since 3.3
+     * @deprecated Since the code now requires Java 1.6 as a mininmum
+     */
+    @Deprecated
+    public String getCharsetName() {
+        return charset.name();
     }
 
 
     /**
-     * Set the default timeout in milliseconds to use when opening a socket.
-     * This value is only used previous to a call to
-     * {@link #connect connect()}
-     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
-     * which operates on an the currently opened socket.  _timeout_ contains
-     * the new timeout value.
+     * Subclasses can override this if they need to provide their own
+     * instance field for backwards compatibilty.
+     *
+     * @return the CommandSupport instance, may be {@code null}
+     * @since 3.0
+     */
+    protected ProtocolCommandSupport getCommandSupport() {
+        return commandSupport;
+    }
+
+
+    /**
+     * Get the underlying socket connection timeout.
+     * @return timeout (in ms)
+     * @since 2.0
+     */
+    public int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    /**
+     * Returns the current value of the default port (stored in
+     * {@link #_defaultPort_  _defaultPort_ }).
      * <p>
-     * @param timeout  The timeout in milliseconds to use for the socket
-     *                 connection.
+     * @return The current value of the default port.
      */
-    public void setDefaultTimeout(final int timeout)
+    public int getDefaultPort()
     {
-        _timeout_ = timeout;
+        return _defaultPort_;
     }
 
-
     /**
      * Returns the default timeout in milliseconds that is used when
      * opening a socket.
@@ -476,54 +469,51 @@ public abstract class SocketClient
         return _timeout_;
     }
 
-
     /**
-     * Set the timeout in milliseconds of a currently open connection.
-     * Only call this method after a connection has been opened
-     * by {@link #connect connect()}.
-     * <p>
-     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
-     *
-     * @param timeout  The timeout in milliseconds to use for the currently
-     *                 open socket connection.
-     * @throws SocketException If the operation fails.
+     * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
+     * Delegates to {@link Socket#getKeepAlive()}
+     * @return True if SO_KEEPALIVE is enabled.
+     * @throws SocketException if there is a problem with the socket
      * @throws NullPointerException if the socket is not currently open
+     * @since 2.2
      */
-    public void setSoTimeout(final int timeout) throws SocketException
-    {
-        _socket_.setSoTimeout(timeout);
+    public boolean getKeepAlive() throws SocketException {
+        return _socket_.getKeepAlive();
     }
 
-
     /**
-     * Set the underlying socket send buffer size.
+     * Returns the local address  to which the client's socket is bound.
+     * Delegates to {@link Socket#getLocalAddress()}
      * <p>
-     * @param size The size of the buffer in bytes.
-     * @throws SocketException never thrown, but subclasses might want to do so
-     * @since 2.0
+     * @return The local address to which the client's socket is bound.
+     * @throws NullPointerException if the socket is not currently open
      */
-    public void setSendBufferSize(final int size) throws SocketException {
-        sendBufferSize = size;
+    public InetAddress getLocalAddress()
+    {
+        return _socket_.getLocalAddress();
     }
 
     /**
-     * Get the current sendBuffer size
-     * @return the size, or -1 if not initialized
-     * @since 3.0
+     * Returns the port number of the open socket on the local host used
+     * for the connection.
+     * Delegates to {@link Socket#getLocalPort()}
+     * <p>
+     * @return The port number of the open socket on the local host used
+     *         for the connection.
+     * @throws NullPointerException if the socket is not currently open
      */
-    protected int getSendBufferSize(){
-        return sendBufferSize;
+    public int getLocalPort()
+    {
+        return _socket_.getLocalPort();
     }
 
+
     /**
-     * Sets the underlying socket receive buffer size.
-     * <p>
-     * @param size The size of the buffer in bytes.
-     * @throws SocketException never (but subclasses may wish to do so)
-     * @since 2.0
+     * Gets the proxy for use with all the connections.
+     * @return the current proxy for connections.
      */
-    public void setReceiveBufferSize(final int size) throws SocketException  {
-        receiveBufferSize = size;
+    public Proxy getProxy() {
+        return connProxy;
     }
 
     /**
@@ -536,84 +526,47 @@ public abstract class SocketClient
     }
 
     /**
-     * Returns the timeout in milliseconds of the currently opened socket.
-     * <p>
-     * @return The timeout in milliseconds of the currently opened socket.
-     * @throws SocketException If the operation fails.
+     * @return The remote address to which the client is connected.
+     * Delegates to {@link Socket#getInetAddress()}
      * @throws NullPointerException if the socket is not currently open
      */
-    public int getSoTimeout() throws SocketException
+    public InetAddress getRemoteAddress()
     {
-        return _socket_.getSoTimeout();
+        return _socket_.getInetAddress();
     }
 
     /**
-     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
-     * currently opened socket.
+     * Returns the port number of the remote host to which the client is
+     * connected.
+     * Delegates to {@link Socket#getPort()}
      * <p>
-     * @param on  True if Nagle's algorithm is to be enabled, false if not.
-     * @throws SocketException If the operation fails.
+     * @return The port number of the remote host to which the client is
+     *         connected.
      * @throws NullPointerException if the socket is not currently open
      */
-    public void setTcpNoDelay(final boolean on) throws SocketException
+    public int getRemotePort()
     {
-        _socket_.setTcpNoDelay(on);
+        return _socket_.getPort();
     }
 
 
     /**
-     * Returns true if Nagle's algorithm is enabled on the currently opened
-     * socket.
-     * <p>
-     * @return True if Nagle's algorithm is enabled on the currently opened
-     *        socket, false otherwise.
-     * @throws SocketException If the operation fails.
-     * @throws NullPointerException if the socket is not currently open
+     * Get the current sendBuffer size
+     * @return the size, or -1 if not initialized
+     * @since 3.0
      */
-    public boolean getTcpNoDelay() throws SocketException
-    {
-        return _socket_.getTcpNoDelay();
+    protected int getSendBufferSize(){
+        return sendBufferSize;
     }
 
-    /**
-     * Sets the SO_KEEPALIVE flag on the currently opened socket.
-     *
-     * From the Javadocs, the default keepalive time is 2 hours (although this is
-     * implementation  dependent). It looks as though the Windows WSA sockets implementation
-     * allows a specific keepalive value to be set, although this seems not to be the case on
-     * other systems.
-     * @param  keepAlive If true, keepAlive is turned on
-     * @throws SocketException if there is a problem with the socket
-     * @throws NullPointerException if the socket is not currently open
-     * @since 2.2
-     */
-    public void setKeepAlive(final boolean keepAlive) throws SocketException {
-        _socket_.setKeepAlive(keepAlive);
-    }
 
     /**
-     * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
-     * Delegates to {@link Socket#getKeepAlive()}
-     * @return True if SO_KEEPALIVE is enabled.
-     * @throws SocketException if there is a problem with the socket
-     * @throws NullPointerException if the socket is not currently open
+     * Get the underlying {@link ServerSocketFactory}
+     * @return The server socket factory
      * @since 2.2
      */
-    public boolean getKeepAlive() throws SocketException {
-        return _socket_.getKeepAlive();
-    }
-
-    /**
-     * Sets the SO_LINGER timeout on the currently opened socket.
-     * <p>
-     * @param on  True if linger is to be enabled, false if not.
-     * @param val The linger timeout (in hundredths of a second?)
-     * @throws SocketException If the operation fails.
-     * @throws NullPointerException if the socket is not currently open
-     */
-    public void setSoLinger(final boolean on, final int val) throws SocketException
-    {
-        _socket_.setSoLinger(on, val);
+    public ServerSocketFactory getServerSocketFactory() {
+        return _serverSocketFactory_;
     }
 
 
@@ -630,119 +583,118 @@ public abstract class SocketClient
         return _socket_.getSoLinger();
     }
 
-
     /**
-     * Returns the port number of the open socket on the local host used
-     * for the connection.
-     * Delegates to {@link Socket#getLocalPort()}
+     * Returns the timeout in milliseconds of the currently opened socket.
      * <p>
-     * @return The port number of the open socket on the local host used
-     *         for the connection.
+     * @return The timeout in milliseconds of the currently opened socket.
+     * @throws SocketException If the operation fails.
      * @throws NullPointerException if the socket is not currently open
      */
-    public int getLocalPort()
+    public int getSoTimeout() throws SocketException
     {
-        return _socket_.getLocalPort();
+        return _socket_.getSoTimeout();
     }
 
 
     /**
-     * Returns the local address  to which the client's socket is bound.
-     * Delegates to {@link Socket#getLocalAddress()}
-     * <p>
-     * @return The local address to which the client's socket is bound.
-     * @throws NullPointerException if the socket is not currently open
-     */
-    public InetAddress getLocalAddress()
-    {
-        return _socket_.getLocalAddress();
-    }
-
-    /**
-     * Returns the port number of the remote host to which the client is
-     * connected.
-     * Delegates to {@link Socket#getPort()}
+     * Returns true if Nagle's algorithm is enabled on the currently opened
+     * socket.
      * <p>
-     * @return The port number of the remote host to which the client is
-     *         connected.
-     * @throws NullPointerException if the socket is not currently open
-     */
-    public int getRemotePort()
-    {
-        return _socket_.getPort();
-    }
-
-
-    /**
-     * @return The remote address to which the client is connected.
-     * Delegates to {@link Socket#getInetAddress()}
+     * @return True if Nagle's algorithm is enabled on the currently opened
+     *        socket, false otherwise.
+     * @throws SocketException If the operation fails.
      * @throws NullPointerException if the socket is not currently open
      */
-    public InetAddress getRemoteAddress()
+    public boolean getTcpNoDelay() throws SocketException
     {
-        return _socket_.getInetAddress();
+        return _socket_.getTcpNoDelay();
     }
 
 
     /**
-     * Verifies that the remote end of the given socket is connected to the
-     * the same host that the SocketClient is currently connected to.  This
-     * is useful for doing a quick security check when a client needs to
-     * accept a connection from a server, such as an FTP data connection or
-     * a BSD R command standard error stream.
-     * <p>
-     * @param socket the item to check against
-     * @return True if the remote hosts are the same, false if not.
+     * Make various checks on the socket to test if it is available for use.
+     * Note that the only sure test is to use it, but these checks may help
+     * in some cases.
+     * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
+     * @return {@code true} if the socket appears to be available for use
+     * @since 3.0
      */
-    public boolean verifyRemote(final Socket socket)
-    {
-        final InetAddress host1;
-        final InetAddress host2;
-
-        host1 = socket.getInetAddress();
-        host2 = getRemoteAddress();
-
-        return host1.equals(host2);
+    @SuppressWarnings("resource")
+    public boolean isAvailable(){
+        if (isConnected()) {
+            try
+            {
+                if (_socket_.getInetAddress() == null) {
+                    return false;
+                }
+                if (_socket_.getPort() == 0) {
+                    return false;
+                }
+                if (_socket_.getRemoteSocketAddress() == null) {
+                    return false;
+                }
+                if (_socket_.isClosed()) {
+                    return false;
+                }
+                /* these aren't exact checks (a Socket can be half-open),
+                   but since we usually require two-way data transfer,
+                   we check these here too: */
+                if (_socket_.isInputShutdown()) {
+                    return false;
+                }
+                if (_socket_.isOutputShutdown()) {
+                    return false;
+                }
+                /* ignore the result, catch exceptions: */
+                // No need to close
+                _socket_.getInputStream();
+                // No need to close
+                _socket_.getOutputStream();
+            }
+            catch (final IOException ioex)
+            {
+                return false;
+            }
+            return true;
+        }
+        return false;
     }
 
 
     /**
-     * Sets the SocketFactory used by the SocketClient to open socket
-     * connections.  If the factory value is null, then a default
-     * factory is used (only do this to reset the factory after having
-     * previously altered it).
-     * Any proxy setting is discarded.
+     * Returns true if the client is currently connected to a server.
      * <p>
-     * @param factory  The new SocketFactory the SocketClient should use.
+     * Delegates to {@link Socket#isConnected()}
+     * @return True if the client is currently connected to a server,
+     *         false otherwise.
      */
-    public void setSocketFactory(final SocketFactory factory)
+    public boolean isConnected()
     {
-        if (factory == null) {
-            _socketFactory_ = DEFAULT_SOCKET_FACTORY;
-        } else {
-            _socketFactory_ = factory;
+        if (_socket_ == null) {
+            return false;
         }
-        // re-setting the socket factory makes the proxy setting useless,
-        // so set the field to null so that getProxy() doesn't return a
-        // Proxy that we're actually not using.
-        connProxy = null;
+
+        return _socket_.isConnected();
     }
 
     /**
-     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
-     * connections.  If the factory value is null, then a default
-     * factory is used (only do this to reset the factory after having
-     * previously altered it).
-     * <p>
-     * @param factory  The new ServerSocketFactory the SocketClient should use.
-     * @since 2.0
+     * Removes a ProtocolCommandListener.
+     *
+     * @param listener  The ProtocolCommandListener to remove.
+     * @since 3.0
      */
-    public void setServerSocketFactory(final ServerSocketFactory factory) {
-        if (factory == null) {
-            _serverSocketFactory_ = DEFAULT_SERVER_SOCKET_FACTORY;
-        } else {
-            _serverSocketFactory_ = factory;
-        }
+    public void removeProtocolCommandListener(final ProtocolCommandListener listener) {
+        getCommandSupport().removeProtocolCommandListener(listener);
+    }
+
+    /**
+     * Sets the charset.
+     *
+     * @param charset the charset.
+     * @since 3.3
+     */
+    public void setCharset(final Charset charset) {
+        this.charset = charset;
     }
 
     /**
@@ -756,139 +708,187 @@ public abstract class SocketClient
     }
 
     /**
-     * Get the underlying socket connection timeout.
-     * @return timeout (in ms)
-     * @since 2.0
-     */
-    public int getConnectTimeout() {
-        return connectTimeout;
-    }
-
-    /**
-     * Get the underlying {@link ServerSocketFactory}
-     * @return The server socket factory
-     * @since 2.2
+     * Sets the default port the SocketClient should connect to when a port
+     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
+     * variable stores this value.  If never set, the default port is equal
+     * to zero.
+     * <p>
+     * @param port  The default port to set.
      */
-    public ServerSocketFactory getServerSocketFactory() {
-        return _serverSocketFactory_;
+    public void setDefaultPort(final int port)
+    {
+        _defaultPort_ = port;
     }
 
 
     /**
-     * Adds a ProtocolCommandListener.
-     *
-     * @param listener  The ProtocolCommandListener to add.
-     * @since 3.0
+     * Set the default timeout in milliseconds to use when opening a socket.
+     * This value is only used previous to a call to
+     * {@link #connect connect()}
+     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
+     * which operates on an the currently opened socket.  _timeout_ contains
+     * the new timeout value.
+     * <p>
+     * @param timeout  The timeout in milliseconds to use for the socket
+     *                 connection.
      */
-    public void addProtocolCommandListener(final ProtocolCommandListener listener) {
-        getCommandSupport().addProtocolCommandListener(listener);
+    public void setDefaultTimeout(final int timeout)
+    {
+        _timeout_ = timeout;
     }
 
     /**
-     * Removes a ProtocolCommandListener.
+     * Sets the SO_KEEPALIVE flag on the currently opened socket.
      *
-     * @param listener  The ProtocolCommandListener to remove.
-     * @since 3.0
+     * From the Javadocs, the default keepalive time is 2 hours (although this is
+     * implementation  dependent). It looks as though the Windows WSA sockets implementation
+     * allows a specific keepalive value to be set, although this seems not to be the case on
+     * other systems.
+     * @param  keepAlive If true, keepAlive is turned on
+     * @throws SocketException if there is a problem with the socket
+     * @throws NullPointerException if the socket is not currently open
+     * @since 2.2
      */
-    public void removeProtocolCommandListener(final ProtocolCommandListener listener) {
-        getCommandSupport().removeProtocolCommandListener(listener);
+    public void setKeepAlive(final boolean keepAlive) throws SocketException {
+        _socket_.setKeepAlive(keepAlive);
     }
 
     /**
-     * If there are any listeners, send them the reply details.
+     * Sets the proxy for use with all the connections.
+     * The proxy is used for connections established after the
+     * call to this method.
      *
-     * @param replyCode the code extracted from the reply
-     * @param reply the full reply text
-     * @since 3.0
+     * @param proxy the new proxy for connections.
+     * @since 3.2
      */
-    protected void fireReplyReceived(final int replyCode, final String reply) {
-        if (getCommandSupport().getListenerCount() > 0) {
-            getCommandSupport().fireReplyReceived(replyCode, reply);
-        }
+    public void setProxy(final Proxy proxy) {
+        setSocketFactory(new DefaultSocketFactory(proxy));
+        connProxy = proxy;
     }
 
     /**
-     * If there are any listeners, send them the command details.
-     *
-     * @param command the command name
-     * @param message the complete message, including command name
-     * @since 3.0
+     * Sets the underlying socket receive buffer size.
+     * <p>
+     * @param size The size of the buffer in bytes.
+     * @throws SocketException never (but subclasses may wish to do so)
+     * @since 2.0
      */
-    protected void fireCommandSent(final String command, final String message) {
-        if (getCommandSupport().getListenerCount() > 0) {
-            getCommandSupport().fireCommandSent(command, message);
-        }
+    public void setReceiveBufferSize(final int size) throws SocketException  {
+        receiveBufferSize = size;
     }
 
     /**
-     * Create the CommandSupport instance if required
+     * Set the underlying socket send buffer size.
+     * <p>
+     * @param size The size of the buffer in bytes.
+     * @throws SocketException never thrown, but subclasses might want to do so
+     * @since 2.0
      */
-    protected void createCommandSupport(){
-        commandSupport = new ProtocolCommandSupport(this);
+    public void setSendBufferSize(final int size) throws SocketException {
+        sendBufferSize = size;
     }
 
     /**
-     * Subclasses can override this if they need to provide their own
-     * instance field for backwards compatibilty.
-     *
-     * @return the CommandSupport instance, may be {@code null}
-     * @since 3.0
+     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
+     * connections.  If the factory value is null, then a default
+     * factory is used (only do this to reset the factory after having
+     * previously altered it).
+     * <p>
+     * @param factory  The new ServerSocketFactory the SocketClient should use.
+     * @since 2.0
      */
-    protected ProtocolCommandSupport getCommandSupport() {
-        return commandSupport;
+    public void setServerSocketFactory(final ServerSocketFactory factory) {
+        if (factory == null) {
+            _serverSocketFactory_ = DEFAULT_SERVER_SOCKET_FACTORY;
+        } else {
+            _serverSocketFactory_ = factory;
+        }
     }
 
     /**
-     * Sets the proxy for use with all the connections.
-     * The proxy is used for connections established after the
-     * call to this method.
-     *
-     * @param proxy the new proxy for connections.
-     * @since 3.2
+     * Sets the SocketFactory used by the SocketClient to open socket
+     * connections.  If the factory value is null, then a default
+     * factory is used (only do this to reset the factory after having
+     * previously altered it).
+     * Any proxy setting is discarded.
+     * <p>
+     * @param factory  The new SocketFactory the SocketClient should use.
      */
-    public void setProxy(final Proxy proxy) {
-        setSocketFactory(new DefaultSocketFactory(proxy));
-        connProxy = proxy;
+    public void setSocketFactory(final SocketFactory factory)
+    {
+        if (factory == null) {
+            _socketFactory_ = DEFAULT_SOCKET_FACTORY;
+        } else {
+            _socketFactory_ = factory;
+        }
+        // re-setting the socket factory makes the proxy setting useless,
+        // so set the field to null so that getProxy() doesn't return a
+        // Proxy that we're actually not using.
+        connProxy = null;
     }
 
     /**
-     * Gets the proxy for use with all the connections.
-     * @return the current proxy for connections.
+     * Sets the SO_LINGER timeout on the currently opened socket.
+     * <p>
+     * @param on  True if linger is to be enabled, false if not.
+     * @param val The linger timeout (in hundredths of a second?)
+     * @throws SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
-    public Proxy getProxy() {
-        return connProxy;
+    public void setSoLinger(final boolean on, final int val) throws SocketException
+    {
+        _socket_.setSoLinger(on, val);
     }
 
     /**
-     * Gets the charset name.
+     * Set the timeout in milliseconds of a currently open connection.
+     * Only call this method after a connection has been opened
+     * by {@link #connect connect()}.
+     * <p>
+     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
      *
-     * @return the charset.
-     * @since 3.3
-     * @deprecated Since the code now requires Java 1.6 as a mininmum
+     * @param timeout  The timeout in milliseconds to use for the currently
+     *                 open socket connection.
+     * @throws SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
-    @Deprecated
-    public String getCharsetName() {
-        return charset.name();
+    public void setSoTimeout(final int timeout) throws SocketException
+    {
+        _socket_.setSoTimeout(timeout);
     }
 
     /**
-     * Gets the charset.
-     *
-     * @return the charset.
-     * @since 3.3
+     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
+     * currently opened socket.
+     * <p>
+     * @param on  True if Nagle's algorithm is to be enabled, false if not.
+     * @throws SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
-    public Charset getCharset() {
-        return charset;
+    public void setTcpNoDelay(final boolean on) throws SocketException
+    {
+        _socket_.setTcpNoDelay(on);
     }
 
     /**
-     * Sets the charset.
-     *
-     * @param charset the charset.
-     * @since 3.3
+     * Verifies that the remote end of the given socket is connected to the
+     * the same host that the SocketClient is currently connected to.  This
+     * is useful for doing a quick security check when a client needs to
+     * accept a connection from a server, such as an FTP data connection or
+     * a BSD R command standard error stream.
+     * <p>
+     * @param socket the item to check against
+     * @return True if the remote hosts are the same, false if not.
      */
-    public void setCharset(final Charset charset) {
-        this.charset = charset;
+    public boolean verifyRemote(final Socket socket)
+    {
+        final InetAddress host1;
+        final InetAddress host2;
+
+        host1 = socket.getInetAddress();
+        host2 = getRemoteAddress();
+
+        return host1.equals(host2);
     }
 
     /*
diff --git a/src/main/java/org/apache/commons/net/bsd/RCommandClient.java b/src/main/java/org/apache/commons/net/bsd/RCommandClient.java
index bfdab17..e5ce9fe 100644
--- a/src/main/java/org/apache/commons/net/bsd/RCommandClient.java
+++ b/src/main/java/org/apache/commons/net/bsd/RCommandClient.java
@@ -104,54 +104,6 @@ public class RCommandClient extends RExecClient
      */
     public static final int MAX_CLIENT_PORT = 1023;
 
-    // Overrides method in RExecClient in order to implement proper
-    // port number limitations.
-    @Override
-    InputStream createErrorStream() throws IOException
-    {
-        int localPort;
-        ServerSocket server;
-        final Socket socket;
-
-        localPort = MAX_CLIENT_PORT;
-        server = null; // Keep compiler from barfing
-
-        for (localPort = MAX_CLIENT_PORT; localPort >= MIN_CLIENT_PORT; --localPort)
-        {
-            try
-            {
-                server = _serverSocketFactory_.createServerSocket(localPort, 1,
-                         getLocalAddress());
-                break; // got a socket
-            }
-            catch (final SocketException e)
-            {
-                continue;
-            }
-        }
-
-        if (server == null) {
-            throw new BindException("All ports in use.");
-        }
-
-        _output_.write(Integer.toString(server.getLocalPort()).getBytes(StandardCharsets.UTF_8)); // $NON-NLS
-        _output_.write(NULL_CHAR);
-        _output_.flush();
-
-        socket = server.accept();
-        server.close();
-
-        if (isRemoteVerificationEnabled() && !verifyRemote(socket))
-        {
-            socket.close();
-            throw new IOException(
-                "Security violation: unexpected connection attempt by " +
-                socket.getInetAddress().getHostAddress());
-        }
-
-        return new SocketInputStream(socket, socket.getInputStream());
-    }
-
     /**
      * The default RCommandClient constructor.  Initializes the
      * default port to <code> DEFAULT_PORT </code>.
@@ -161,6 +113,28 @@ public class RCommandClient extends RExecClient
         setDefaultPort(DEFAULT_PORT);
     }
 
+    /**
+     * Opens a Socket connected to a remote host at the specified port and
+     * originating from the current host at a port in a range acceptable
+     * to the BSD rshell daemon.
+     * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_  _connectAction_() }
+     * is called to perform connection initialization actions.
+     *
+     * @param host  The remote host.
+     * @param port  The port to connect to on the remote host.
+     * @throws SocketException If the socket timeout could not be set.
+     * @throws BindException If all acceptable rshell ports are in use.
+     * @throws IOException If the socket could not be opened.  In most
+     *  cases you will only want to catch IOException since SocketException is
+     *  derived from it.
+     */
+    @Override
+    public void connect(final InetAddress host, final int port)
+    throws SocketException, IOException
+    {
+        connect(host, port, InetAddress.getLocalHost());
+    }
+
 
     /**
      * Opens a Socket connected to a remote host at the specified port and
@@ -210,24 +184,33 @@ public class RCommandClient extends RExecClient
 
     /**
      * Opens a Socket connected to a remote host at the specified port and
-     * originating from the current host at a port in a range acceptable
-     * to the BSD rshell daemon.
+     * originating from the specified local address and port. The
+     * local port must lie between <code> MIN_CLIENT_PORT </code> and
+     * <code> MAX_CLIENT_PORT </code> or an IllegalArgumentException will
+     * be thrown.
      * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      *
      * @param host  The remote host.
      * @param port  The port to connect to on the remote host.
+     * @param localAddr  The local address to use.
+     * @param localPort  The local port to use.
      * @throws SocketException If the socket timeout could not be set.
-     * @throws BindException If all acceptable rshell ports are in use.
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
+     * @throws IllegalArgumentException If an invalid local port number
+     *            is specified.
      */
     @Override
-    public void connect(final InetAddress host, final int port)
-    throws SocketException, IOException
+    public void connect(final InetAddress host, final int port,
+                        final InetAddress localAddr, final int localPort)
+    throws SocketException, IOException, IllegalArgumentException
     {
-        connect(host, port, InetAddress.getLocalHost());
+        if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) {
+            throw new IllegalArgumentException("Invalid port number " + localPort);
+        }
+        super.connect(host, port, localAddr, localPort);
     }
 
 
@@ -287,7 +270,7 @@ public class RCommandClient extends RExecClient
      * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_  _connectAction_() }
      * is called to perform connection initialization actions.
      *
-     * @param host  The remote host.
+     * @param hostname  The name of the remote host.
      * @param port  The port to connect to on the remote host.
      * @param localAddr  The local address to use.
      * @param localPort  The local port to use.
@@ -295,51 +278,84 @@ public class RCommandClient extends RExecClient
      * @throws IOException If the socket could not be opened.  In most
      *  cases you will only want to catch IOException since SocketException is
      *  derived from it.
+     * @throws UnknownHostException If the hostname cannot be resolved.
      * @throws IllegalArgumentException If an invalid local port number
      *            is specified.
      */
     @Override
-    public void connect(final InetAddress host, final int port,
+    public void connect(final String hostname, final int port,
                         final InetAddress localAddr, final int localPort)
-    throws SocketException, IOException, IllegalArgumentException
+    throws SocketException, IOException, IllegalArgumentException, UnknownHostException
     {
         if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) {
             throw new IllegalArgumentException("Invalid port number " + localPort);
         }
-        super.connect(host, port, localAddr, localPort);
+        super.connect(hostname, port, localAddr, localPort);
     }
 
 
-    /**
-     * Opens a Socket connected to a remote host at the specified port and
-     * originating from the specified local address and port. The
-     * local port must lie between <code> MIN_CLIENT_PORT </code> and
-     * <code> MAX_CLIENT_PORT </code> or an IllegalArgumentException will
-     * be thrown.
-     * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_  _connectAction_() }
-     * is called to perform connection initialization actions.
-     *
-     * @param hostname  The name of the remote host.
-     * @param port  The port to connect to on the remote host.
-     * @param localAddr  The local address to use.
-     * @param localPort  The local port to use.
-     * @throws SocketException If the socket timeout could not be set.
-     * @throws IOException If the socket could not be opened.  In most
-     *  cases you will only want to catch IOException since SocketException is
-     *  derived from it.
-     * @throws UnknownHostException If the hostname cannot be resolved.
-     * @throws IllegalArgumentException If an invalid local port number
-     *            is specified.
-     */
+    // Overrides method in RExecClient in order to implement proper
+    // port number limitations.
     @Override
-    public void connect(final String hostname, final int port,
-                        final InetAddress localAddr, final int localPort)
-    throws SocketException, IOException, IllegalArgumentException, UnknownHostException
+    InputStream createErrorStream() throws IOException
     {
-        if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) {
-            throw new IllegalArgumentException("Invalid port number " + localPort);
+        int localPort;
+        ServerSocket server;
+        final Socket socket;
+
+        localPort = MAX_CLIENT_PORT;
+        server = null; // Keep compiler from barfing
+
+        for (localPort = MAX_CLIENT_PORT; localPort >= MIN_CLIENT_PORT; --localPort)
+        {
+            try
+            {
+                server = _serverSocketFactory_.createServerSocket(localPort, 1,
+                         getLocalAddress());
+                break; // got a socket
+            }
+            catch (final SocketException e)
+            {
+                continue;
+            }
         }
-        super.connect(hostname, port, localAddr, localPort);
+
+        if (server == null) {
+            throw new BindException("All ports in use.");
+        }
+
+        _output_.write(Integer.toString(server.getLocalPort()).getBytes(StandardCharsets.UTF_8)); // $NON-NLS
+        _output_.write(NULL_CHAR);
+        _output_.flush();
+
+        socket = server.accept();
+        server.close();
+
+        if (isRemoteVerificationEnabled() && !verifyRemote(socket))
+        {
+            socket.close();
+            throw new IOException(
+                "Security violation: unexpected connection attempt by " +
+                socket.getInetAddress().getHostAddress());
+        }
+
+        return new SocketInputStream(socket, socket.getInputStream());
+    }
+
+
+    /**
+     * Same as
+     * <code> rcommand(localUsername, remoteUsername, command, false); </code>
+     * @param localUsername the local user
+     * @param remoteUsername the remote user
+     * @param command the command
+     * @throws IOException on error
+     */
+    public void rcommand(final String localUsername, final String remoteUsername,
+                         final String command)
+    throws IOException
+    {
+        rcommand(localUsername, remoteUsername, command, false);
     }
 
 
@@ -386,21 +402,5 @@ public class RCommandClient extends RExecClient
         rexec(localUsername, remoteUsername, command, separateErrorStream);
     }
 
-
-    /**
-     * Same as
-     * <code> rcommand(localUsername, remoteUsername, command, false); </code>
-     * @param localUsername the local user
-     * @param remoteUsername the remote user
-     * @param command the command
-     * @throws IOException on error
-     */
-    public void rcommand(final String localUsername, final String remoteUsername,
-                         final String command)
-    throws IOException
-    {
-        rcommand(localUsername, remoteUsername, command, false);
-    }
-
 }
 
diff --git a/src/main/java/org/apache/commons/net/bsd/RExecClient.java b/src/main/java/org/apache/commons/net/bsd/RExecClient.java
index 2d63f86..b16560a 100644
--- a/src/main/java/org/apache/commons/net/bsd/RExecClient.java
+++ b/src/main/java/org/apache/commons/net/bsd/RExecClient.java
@@ -85,6 +85,17 @@ public class RExecClient extends SocketClient
      */
     protected InputStream _errorStream_;
 
+    /**
+     * The default RExecClient constructor.  Initializes the
+     * default port to <code> DEFAULT_PORT </code>.
+     */
+    public RExecClient()
+    {
+        _errorStream_ = null;
+        setDefaultPort(DEFAULT_PORT);
+    }
+
+
     // This can be overridden in local package to implement port range
     // limitations of rcmd and rlogin
     InputStream createErrorStream() throws IOException
@@ -114,13 +125,35 @@ public class RExecClient extends SocketClient
 
 
     /**
-     * The default RExecClient constructor.  Initializes the
-     * default port to <code> DEFAULT_PORT </code>.
+     * Disconnects from the server, closing all associated open sockets and
+     * streams.
+     *
+     * @throws IOException If there an error occurs while disconnecting.
      */
-    public RExecClient()
+    @Override
+    public void disconnect() throws IOException
     {
+        if (_errorStream_ != null) {
+            _errorStream_.close();
+        }
         _errorStream_ = null;
-        setDefaultPort(DEFAULT_PORT);
+        super.disconnect();
+    }
+
+
+    /**
+     * Returns the InputStream from which the standard error of the remote
+     * process can be read if a separate error stream is requested from
+     * the server.  Otherwise, null will be returned.  The error stream
+     * will only be set after a successful rexec() invocation.
+     *
+     * @return The InputStream from which the standard error of the remote
+     * process can be read if a separate error stream is requested from
+     * the server.  Otherwise, null will be returned.
+     */
+    public InputStream getErrorStream()
+    {
+        return _errorStream_;
     }
 
 
@@ -153,18 +186,29 @@ public class RExecClient extends SocketClient
 
 
     /**
-     * Returns the InputStream from which the standard error of the remote
-     * process can be read if a separate error stream is requested from
-     * the server.  Otherwise, null will be returned.  The error stream
-     * will only be set after a successful rexec() invocation.
+     * Return whether or not verification of the remote host providing a
+     * separate error stream is enabled.  The default behavior is for
+     * verification to be enabled.
      *
-     * @return The InputStream from which the standard error of the remote
-     * process can be read if a separate error stream is requested from
-     * the server.  Otherwise, null will be returned.
+     * @return True if verification is enabled, false if not.
      */
-    public InputStream getErrorStream()
+    public final boolean isRemoteVerificationEnabled()
     {
-        return _errorStream_;
+        return remoteVerificationEnabled;
+    }
+
+    /**
+     * Same as <code> rexec(username, password, command, false); </code>
+     * @param username the user name
+     * @param password the password
+     * @param command the command to run
+     * @throws IOException if an error occurs
+     */
+    public void rexec(final String username, final String password,
+                      final String command)
+    throws IOException
+    {
+        rexec(username, password, command, false);
     }
 
 
@@ -237,38 +281,6 @@ public class RExecClient extends SocketClient
         }
     }
 
-
-    /**
-     * Same as <code> rexec(username, password, command, false); </code>
-     * @param username the user name
-     * @param password the password
-     * @param command the command to run
-     * @throws IOException if an error occurs
-     */
-    public void rexec(final String username, final String password,
-                      final String command)
-    throws IOException
-    {
-        rexec(username, password, command, false);
-    }
-
-    /**
-     * Disconnects from the server, closing all associated open sockets and
-     * streams.
-     *
-     * @throws IOException If there an error occurs while disconnecting.
-     */
-    @Override
-    public void disconnect() throws IOException
-    {
-        if (_errorStream_ != null) {
-            _errorStream_.close();
-        }
-        _errorStream_ = null;
-        super.disconnect();
-    }
-
-
     /**
      * Enable or disable verification that the remote host connecting to
      * create a separate error stream is the same as the host to which
@@ -283,17 +295,5 @@ public class RExecClient extends SocketClient
         remoteVerificationEnabled = enable;
     }
 
-    /**
-     * Return whether or not verification of the remote host providing a
-     * separate error stream is enabled.  The default behavior is for
-     * verification to be enabled.
-     *
-     * @return True if verification is enabled, false if not.
-     */
-    public final boolean isRemoteVerificationEnabled()
-    {
-        return remoteVerificationEnabled;
-    }
-
 }
 
diff --git a/src/main/java/org/apache/commons/net/bsd/RLoginClient.java b/src/main/java/org/apache/commons/net/bsd/RLoginClient.java
index 249e160..f7aa6ed 100644
--- a/src/main/java/org/apache/commons/net/bsd/RLoginClient.java
+++ b/src/main/java/org/apache/commons/net/bsd/RLoginClient.java
@@ -81,6 +81,20 @@ public class RLoginClient extends RCommandClient
 
 
     /**
+     * Same as the other rlogin method, but no terminal speed is defined.
+     * @param localUsername the local user
+     * @param remoteUsername the remote user
+     * @param terminalType the terminal type
+     * @throws IOException on error
+     */
+    public void rlogin(final String localUsername, final String remoteUsername,
+                       final String terminalType)
+    throws IOException
+    {
+        rexec(localUsername, remoteUsername, terminalType, false);
+    }
+
+    /**
      * Logins into a remote machine through the rlogind daemon on the server
      * to which the RLoginClient is connected.  After calling this method,
      * you may interact with the remote login shell through its standard input
@@ -116,18 +130,4 @@ public class RLoginClient extends RCommandClient
               false);
     }
 
-    /**
-     * Same as the other rlogin method, but no terminal speed is defined.
-     * @param localUsername the local user
-     * @param remoteUsername the remote user
-     * @param terminalType the terminal type
-     * @throws IOException on error
-     */
-    public void rlogin(final String localUsername, final String remoteUsername,
-                       final String terminalType)
-    throws IOException
-    {
-        rexec(localUsername, remoteUsername, terminalType, false);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/chargen/CharGenUDPClient.java b/src/main/java/org/apache/commons/net/chargen/CharGenUDPClient.java
index 79d8c16..25a5a77 100644
--- a/src/main/java/org/apache/commons/net/chargen/CharGenUDPClient.java
+++ b/src/main/java/org/apache/commons/net/chargen/CharGenUDPClient.java
@@ -82,31 +82,6 @@ public final class CharGenUDPClient extends DatagramSocketClient
 
 
     /**
-     * Sends the data initiation datagram.  This data in the packet is ignored
-     * by the server, and merely serves to signal that the server should send
-     * its reply.
-     *
-     * @param host The address of the server.
-     * @param port The port of the service.
-     * @throws IOException If an error occurs while sending the datagram.
-     */
-    public void send(final InetAddress host, final int port) throws IOException
-    {
-        sendPacket.setAddress(host);
-        sendPacket.setPort(port);
-        _socket_.send(sendPacket);
-    }
-
-    /** Same as <code>send(host, CharGenUDPClient.DEFAULT_PORT);</code>
-     * @param host the destination host
-     * @throws IOException on error
-     */
-    public void send(final InetAddress host) throws IOException
-    {
-        send(host, DEFAULT_PORT);
-    }
-
-    /**
      * Receive the reply data from the server.  This will always be 512 bytes
      * or less.  Chargen and quote of the day only return one packet.  Netstat
      * and systat require multiple calls to receive() with timeout detection.
@@ -127,5 +102,30 @@ public final class CharGenUDPClient extends DatagramSocketClient
         return result;
     }
 
+    /** Same as <code>send(host, CharGenUDPClient.DEFAULT_PORT);</code>
+     * @param host the destination host
+     * @throws IOException on error
+     */
+    public void send(final InetAddress host) throws IOException
+    {
+        send(host, DEFAULT_PORT);
+    }
+
+    /**
+     * Sends the data initiation datagram.  This data in the packet is ignored
+     * by the server, and merely serves to signal that the server should send
+     * its reply.
+     *
+     * @param host The address of the server.
+     * @param port The port of the service.
+     * @throws IOException If an error occurs while sending the datagram.
+     */
+    public void send(final InetAddress host, final int port) throws IOException
+    {
+        sendPacket.setAddress(host);
+        sendPacket.setPort(port);
+        _socket_.send(sendPacket);
+    }
+
 }
 
diff --git a/src/main/java/org/apache/commons/net/daytime/DaytimeUDPClient.java b/src/main/java/org/apache/commons/net/daytime/DaytimeUDPClient.java
index 5da5046..a41a7ee 100644
--- a/src/main/java/org/apache/commons/net/daytime/DaytimeUDPClient.java
+++ b/src/main/java/org/apache/commons/net/daytime/DaytimeUDPClient.java
@@ -47,6 +47,16 @@ public final class DaytimeUDPClient extends DatagramSocketClient
     // Received dates should be less than 256 bytes
     private final byte[] timeData = new byte[256];
 
+    /** Same as <code>getTime(host, DaytimeUDPClient.DEFAULT_PORT);</code>
+     * @param host the host
+     * @return  the time
+     * @throws IOException on error
+     */
+    public String getTime(final InetAddress host) throws IOException
+    {
+        return getTime(host, DEFAULT_PORT);
+    }
+
     /**
      * Retrieves the time string from the specified server and port and
      * returns it.
@@ -71,15 +81,5 @@ public final class DaytimeUDPClient extends DatagramSocketClient
         return new String(receivePacket.getData(), 0, receivePacket.getLength(), getCharset()); // Java 1.6 can use getCharset()
     }
 
-    /** Same as <code>getTime(host, DaytimeUDPClient.DEFAULT_PORT);</code>
-     * @param host the host
-     * @return  the time
-     * @throws IOException on error
-     */
-    public String getTime(final InetAddress host) throws IOException
-    {
-        return getTime(host, DEFAULT_PORT);
-    }
-
 }
 
diff --git a/src/main/java/org/apache/commons/net/discard/DiscardUDPClient.java b/src/main/java/org/apache/commons/net/discard/DiscardUDPClient.java
index e290a11..b6b4610 100644
--- a/src/main/java/org/apache/commons/net/discard/DiscardUDPClient.java
+++ b/src/main/java/org/apache/commons/net/discard/DiscardUDPClient.java
@@ -51,24 +51,16 @@ public class DiscardUDPClient extends DatagramSocketClient
 
 
     /**
-     * Sends the specified data to the specified server at the specified port.
-     *
-     * @param data  The discard data to send.
-     * @param length  The length of the data to send.  Should be less than
-     *    or equal to the length of the data byte array.
-     * @param host  The address of the server.
-     * @param port  The service port.
-     * @throws IOException If an error occurs during the datagram send
-     *            operation.
+     * Same as
+     * <code>send(data, data.length, host. DiscardUDPClient.DEFAULT_PORT)</code>.
+     * @param data the buffer to send
+     * @param host the target host
+     * @see #send(byte[], int, InetAddress, int)
+     * @throws IOException if an error occurs
      */
-    public void send(final byte[] data, final int length, final InetAddress host, final int port)
-    throws IOException
+    public void send(final byte[] data, final InetAddress host) throws IOException
     {
-        sendPacket.setData(data);
-        sendPacket.setLength(length);
-        sendPacket.setAddress(host);
-        sendPacket.setPort(port);
-        _socket_.send(sendPacket);
+        send(data, data.length, host, DEFAULT_PORT);
     }
 
 
@@ -89,16 +81,24 @@ public class DiscardUDPClient extends DatagramSocketClient
 
 
     /**
-     * Same as
-     * <code>send(data, data.length, host. DiscardUDPClient.DEFAULT_PORT)</code>.
-     * @param data the buffer to send
-     * @param host the target host
-     * @see #send(byte[], int, InetAddress, int)
-     * @throws IOException if an error occurs
+     * Sends the specified data to the specified server at the specified port.
+     *
+     * @param data  The discard data to send.
+     * @param length  The length of the data to send.  Should be less than
+     *    or equal to the length of the data byte array.
+     * @param host  The address of the server.
+     * @param port  The service port.
+     * @throws IOException If an error occurs during the datagram send
+     *            operation.
      */
-    public void send(final byte[] data, final InetAddress host) throws IOException
+    public void send(final byte[] data, final int length, final InetAddress host, final int port)
+    throws IOException
     {
-        send(data, data.length, host, DEFAULT_PORT);
+        sendPacket.setData(data);
+        sendPacket.setLength(length);
+        sendPacket.setAddress(host);
+        sendPacket.setPort(port);
+        _socket_.send(sendPacket);
     }
 
 }
diff --git a/src/main/java/org/apache/commons/net/echo/EchoUDPClient.java b/src/main/java/org/apache/commons/net/echo/EchoUDPClient.java
index f7d2e4f..902616e 100644
--- a/src/main/java/org/apache/commons/net/echo/EchoUDPClient.java
+++ b/src/main/java/org/apache/commons/net/echo/EchoUDPClient.java
@@ -46,30 +46,14 @@ public final class EchoUDPClient extends DiscardUDPClient
 
     private final DatagramPacket receivePacket = new DatagramPacket(NetConstants.EMPTY_BTYE_ARRAY, 0);
 
-    /**
-     * Sends the specified data to the specified server at the default echo
-     * port.
-     *
-     * @param data  The echo data to send.
-     * @param length  The length of the data to send.  Should be less than
-     *    or equal to the length of the data byte array.
-     * @param host  The address of the server.
-     * @throws IOException If an error occurs during the datagram send
-     *     operation.
+    /** Same as <code> receive(data, data.length)</code>
+     * @param data the buffer to receive the input
+     * @return the number of bytes
+     * @throws IOException on error
      */
-    @Override
-    public void send(final byte[] data, final int length, final InetAddress host)
-    throws IOException
-    {
-        send(data, length, host, DEFAULT_PORT);
-    }
-
-
-    /** Same as <code> send(data, data.length, host) </code> */
-    @Override
-    public void send(final byte[] data, final InetAddress host) throws IOException
+    public int receive(final byte[] data) throws IOException
     {
-        send(data, data.length, host, DEFAULT_PORT);
+        return receive(data, data.length);
     }
 
 
@@ -92,14 +76,30 @@ public final class EchoUDPClient extends DiscardUDPClient
         return receivePacket.getLength();
     }
 
-    /** Same as <code> receive(data, data.length)</code>
-     * @param data the buffer to receive the input
-     * @return the number of bytes
-     * @throws IOException on error
+
+    /** Same as <code> send(data, data.length, host) </code> */
+    @Override
+    public void send(final byte[] data, final InetAddress host) throws IOException
+    {
+        send(data, data.length, host, DEFAULT_PORT);
+    }
+
+    /**
+     * Sends the specified data to the specified server at the default echo
+     * port.
+     *
+     * @param data  The echo data to send.
+     * @param length  The length of the data to send.  Should be less than
+     *    or equal to the length of the data byte array.
+     * @param host  The address of the server.
+     * @throws IOException If an error occurs during the datagram send
+     *     operation.
      */
-    public int receive(final byte[] data) throws IOException
+    @Override
+    public void send(final byte[] data, final int length, final InetAddress host)
+    throws IOException
     {
-        return receive(data, data.length);
+        send(data, length, host, DEFAULT_PORT);
     }
 
 }
diff --git a/src/main/java/org/apache/commons/net/examples/Main.java b/src/main/java/org/apache/commons/net/examples/Main.java
index 1b35cc0..442316d 100644
--- a/src/main/java/org/apache/commons/net/examples/Main.java
+++ b/src/main/java/org/apache/commons/net/examples/Main.java
@@ -31,6 +31,14 @@ import java.util.Properties;
  */
 public class Main {
 
+    private static boolean fromJar() {
+        final CodeSource codeSource = Main.class.getProtectionDomain().getCodeSource();
+        if ( codeSource != null) {
+            return codeSource.getLocation().getFile().endsWith(".jar");
+        }
+        return false; // No idea if this can happen
+    }
+
     /**
      * Helper application for example classes.
      * Lists available classes, and provides shorthand invocation.
@@ -102,12 +110,4 @@ public class Main {
             System.out.println(e);
         }
     }
-
-    private static boolean fromJar() {
-        final CodeSource codeSource = Main.class.getProtectionDomain().getCodeSource();
-        if ( codeSource != null) {
-            return codeSource.getLocation().getFile().endsWith(".jar");
-        }
-        return false; // No idea if this can happen
-    }
 }
diff --git a/src/main/java/org/apache/commons/net/examples/ftp/FTPClientExample.java b/src/main/java/org/apache/commons/net/examples/ftp/FTPClientExample.java
index 269a91f..c721089 100644
--- a/src/main/java/org/apache/commons/net/examples/ftp/FTPClientExample.java
+++ b/src/main/java/org/apache/commons/net/examples/ftp/FTPClientExample.java
@@ -88,6 +88,27 @@ public final class FTPClientExample
         "\t-PrP password - HTTP Proxy server password\n" +
         "\t-# - add hash display during transfers\n";
 
+    private static CopyStreamListener createListener(){
+        return new CopyStreamListener(){
+            private long megsTotal;
+
+            @Override
+            public void bytesTransferred(final CopyStreamEvent event) {
+                bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
+            }
+
+            @Override
+            public void bytesTransferred(final long totalBytesTransferred,
+                    final int bytesTransferred, final long streamSize) {
+                final long megs = totalBytesTransferred / 1000000;
+                for (long l = megsTotal; l < megs; l++) {
+                    System.err.print("#");
+                }
+                megsTotal = megs;
+            }
+        };
+    }
+
     public static void main(final String[] args) throws UnknownHostException
     {
         boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false;
@@ -551,26 +572,5 @@ __main:
         System.out.println("CslDebug="+Arrays.toString(stats));
 
     }
-
-    private static CopyStreamListener createListener(){
-        return new CopyStreamListener(){
-            private long megsTotal;
-
-            @Override
-            public void bytesTransferred(final CopyStreamEvent event) {
-                bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
-            }
-
-            @Override
-            public void bytesTransferred(final long totalBytesTransferred,
-                    final int bytesTransferred, final long streamSize) {
-                final long megs = totalBytesTransferred / 1000000;
-                for (long l = megsTotal; l < megs; l++) {
-                    System.err.print("#");
-                }
-                megsTotal = megs;
-            }
-        };
-    }
 }
 
diff --git a/src/main/java/org/apache/commons/net/examples/ftp/TFTPExample.java b/src/main/java/org/apache/commons/net/examples/ftp/TFTPExample.java
index cc4483e..ea78266 100644
--- a/src/main/java/org/apache/commons/net/examples/ftp/TFTPExample.java
+++ b/src/main/java/org/apache/commons/net/examples/ftp/TFTPExample.java
@@ -64,6 +64,25 @@ public final class TFTPExample
         "\t-v Verbose (trace packets)\n"
         ;
 
+    private static boolean close(final TFTPClient tftp, final Closeable output) {
+        boolean closed;
+        tftp.close();
+        try
+        {
+            if (output != null) {
+                output.close();
+            }
+            closed = true;
+        }
+        catch (final IOException e)
+        {
+            closed = false;
+            System.err.println("Error: error closing file.");
+            System.err.println(e.getMessage());
+        }
+        return closed;
+    }
+
     public static void main(final String[] args)
     {
         boolean receiveFile = true, closed;
@@ -153,53 +172,15 @@ public final class TFTPExample
         System.out.println("OK");
     }
 
-    private static boolean send(final int transferMode, final String hostname, final String localFilename,
-            final String remoteFilename, final TFTPClient tftp) {
-        final boolean closed;
-        FileInputStream input = null;
-
-        // Try to open local file for reading
-        try
-        {
-            input = new FileInputStream(localFilename);
-        }
-        catch (final IOException e)
-        {
-            tftp.close();
-            throw new RuntimeException("Error: could not open local file for reading.", e);
-        }
-
-        open(tftp);
-
-        // Try to send local file via TFTP
+    private static void open(final TFTPClient tftp) {
         try
         {
-            final String [] parts = hostname.split(":");
-            if (parts.length == 2) {
-                tftp.sendFile(remoteFilename, transferMode, input, parts[0], Integer.parseInt(parts[1]));
-            } else {
-                tftp.sendFile(remoteFilename, transferMode, input, hostname);
-            }
-        }
-        catch (final UnknownHostException e)
-        {
-            System.err.println("Error: could not resolve hostname.");
-            System.err.println(e.getMessage());
-            System.exit(1);
-        }
-        catch (final IOException e)
-        {
-            System.err.println("Error: I/O exception occurred while sending file.");
-            System.err.println(e.getMessage());
-            System.exit(1);
+            tftp.open();
         }
-        finally
+        catch (final SocketException e)
         {
-            // Close local socket and input file
-            closed = close(tftp, input);
+            throw new RuntimeException("Error: could not open local UDP socket.", e);
         }
-
-        return closed;
     }
 
     private static boolean receive(final int transferMode, final String hostname, final String localFilename,
@@ -262,34 +243,53 @@ public final class TFTPExample
         return closed;
     }
 
-    private static boolean close(final TFTPClient tftp, final Closeable output) {
-        boolean closed;
-        tftp.close();
+    private static boolean send(final int transferMode, final String hostname, final String localFilename,
+            final String remoteFilename, final TFTPClient tftp) {
+        final boolean closed;
+        FileInputStream input = null;
+
+        // Try to open local file for reading
         try
         {
-            if (output != null) {
-                output.close();
-            }
-            closed = true;
+            input = new FileInputStream(localFilename);
         }
         catch (final IOException e)
         {
-            closed = false;
-            System.err.println("Error: error closing file.");
-            System.err.println(e.getMessage());
+            tftp.close();
+            throw new RuntimeException("Error: could not open local file for reading.", e);
         }
-        return closed;
-    }
 
-    private static void open(final TFTPClient tftp) {
+        open(tftp);
+
+        // Try to send local file via TFTP
         try
         {
-            tftp.open();
+            final String [] parts = hostname.split(":");
+            if (parts.length == 2) {
+                tftp.sendFile(remoteFilename, transferMode, input, parts[0], Integer.parseInt(parts[1]));
+            } else {
+                tftp.sendFile(remoteFilename, transferMode, input, hostname);
+            }
         }
-        catch (final SocketException e)
+        catch (final UnknownHostException e)
         {
-            throw new RuntimeException("Error: could not open local UDP socket.", e);
+            System.err.println("Error: could not resolve hostname.");
+            System.err.println(e.getMessage());
+            System.exit(1);
+        }
+        catch (final IOException e)
+        {
+            System.err.println("Error: I/O exception occurred while sending file.");
+            System.err.println(e.getMessage());
+            System.exit(1);
         }
+        finally
+        {
+            // Close local socket and input file
+            closed = close(tftp, input);
+        }
+
+        return closed;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/net/examples/mail/IMAPExportMbox.java b/src/main/java/org/apache/commons/net/examples/mail/IMAPExportMbox.java
index 54f5f15..bfb41f4 100644
--- a/src/main/java/org/apache/commons/net/examples/mail/IMAPExportMbox.java
+++ b/src/main/java/org/apache/commons/net/examples/mail/IMAPExportMbox.java
@@ -81,17 +81,144 @@ import org.apache.commons.net.imap.IMAPReply;
 public final class IMAPExportMbox
 {
 
+    private static class MboxListener implements IMAPChunkListener {
+
+        private final BufferedWriter bufferedWriter;
+        volatile AtomicInteger total = new AtomicInteger();
+        volatile String lastFetched;
+        volatile List<String> missingIds = new ArrayList<>();
+        volatile long lastSeq = -1;
+        private final String lineSeparator;
+        private final SimpleDateFormat DATE_FORMAT // for mbox From_ lines
+            = new SimpleDateFormat("EEE MMM dd HH:mm:ss YYYY");
+
+        // e.g. INTERNALDATE "27-Oct-2013 07:43:24 +0000"
+        // for parsing INTERNALDATE
+        private final SimpleDateFormat IDPARSE = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z");
+        private final boolean printHash;
+        private final boolean printMarker;
+        private final boolean checkSequence;
+
+        MboxListener(final BufferedWriter bufferedWriter, final String lineSeparator, final boolean printHash,
+            final boolean printMarker, final boolean checkSequence) {
+            this.lineSeparator = lineSeparator;
+            this.printHash = printHash;
+            this.printMarker = printMarker;
+            DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
+            this.bufferedWriter = bufferedWriter;
+            this.checkSequence = checkSequence;
+        }
+
+        @Override
+        public boolean chunkReceived(final IMAP imap) {
+            final String[] replyStrings = imap.getReplyStrings();
+            Date received = new Date();
+            final String firstLine = replyStrings[0];
+            Matcher m = PATID.matcher(firstLine);
+            if (m.lookingAt()) { // found a match
+                final String date = m.group(PATID_DATE_GROUP);
+                try {
+                    received=IDPARSE.parse(date);
+                } catch (final ParseException e) {
+                    System.err.println(e);
+                }
+            } else {
+                System.err.println("No timestamp found in: " + firstLine + "  - using current time");
+            }
+            String replyTo = "MAILER-DAEMON"; // default
+            for(int i=1; i< replyStrings.length - 1; i++) {
+                final String line = replyStrings[i];
+                if (line.startsWith("Return-Path: ")) {
+                    final String[] parts = line.split(" ", 2);
+                    if (!parts[1].equals("<>")) {// Don't replace default with blank
+                        replyTo = parts[1];
+                        if (replyTo.startsWith("<")) {
+                            if (replyTo.endsWith(">")) {
+                                replyTo = replyTo.substring(1,replyTo.length()-1); // drop <> wrapper
+                            } else {
+                                System.err.println("Unexpected Return-path: '" + line+ "' in " + firstLine);
+                            }
+                        }
+                    }
+                    break;
+                }
+            }
+            try {
+                // Add initial mbox header line
+                bufferedWriter.append("From ");
+                bufferedWriter.append(replyTo);
+                bufferedWriter.append(' ');
+                bufferedWriter.append(DATE_FORMAT.format(received));
+                bufferedWriter.append(lineSeparator);
+                // Debug
+                bufferedWriter.append("X-IMAP-Response: ").append(firstLine).append(lineSeparator);
+                if (printMarker) {
+                    System.err.println("[" + total + "] " + firstLine);
+                }
+                // Skip first and last lines
+                for(int i=1; i< replyStrings.length - 1; i++) {
+                    final String line = replyStrings[i];
+                        if (startsWith(line, PATFROM)) {
+                            bufferedWriter.append('>'); // Escape a From_ line
+                        }
+                        bufferedWriter.append(line);
+                        bufferedWriter.append(lineSeparator);
+                }
+                // The last line ends with the trailing closing ")" which needs to be stripped
+                final String lastLine = replyStrings[replyStrings.length-1];
+                final int lastLength = lastLine.length();
+                if (lastLength > 1) { // there's some content, we need to save it
+                    bufferedWriter.append(lastLine, 0, lastLength-1);
+                    bufferedWriter.append(lineSeparator);
+                }
+                bufferedWriter.append(lineSeparator); // blank line between entries
+            } catch (final IOException e) {
+                e.printStackTrace();
+                throw new RuntimeException(e); // chunkReceived cannot throw a checked Exception
+            }
+            lastFetched = firstLine;
+            total.incrementAndGet();
+            if (checkSequence) {
+                m = PATSEQ.matcher(firstLine);
+                if (m.lookingAt()) { // found a match
+                    final long msgSeq = Long.parseLong(m.group(PATSEQ_SEQUENCE_GROUP)); // Cannot fail to parse
+                    if (lastSeq != -1) {
+                        final long missing = msgSeq - lastSeq - 1;
+                        if (missing != 0) {
+                            for(long j = lastSeq + 1; j < msgSeq; j++) {
+                                missingIds.add(String.valueOf(j));
+                            }
+                            System.err.println(
+                                "*** Sequence error: current=" + msgSeq + " previous=" + lastSeq + " Missing=" + missing);
+                        }
+                    }
+                    lastSeq = msgSeq;
+                }
+            }
+            if (printHash) {
+                System.err.print(".");
+            }
+            return true;
+        }
+
+        public void close() throws IOException {
+            if (bufferedWriter != null) {
+                bufferedWriter.close();
+            }
+        }
+    }
     private static final String CRLF = "\r\n";
     private static final String LF = "\n";
-    private static final String EOL_DEFAULT = System.getProperty("line.separator");
 
+    private static final String EOL_DEFAULT = System.getProperty("line.separator");
     private static final Pattern PATFROM = Pattern.compile(">*From "); // unescaped From_
     // e.g. * nnn (INTERNALDATE "27-Oct-2013 07:43:24 +0000"  BODY[] {nn} ...)
     private static final Pattern PATID = // INTERNALDATE
             Pattern.compile(".*INTERNALDATE \"(\\d\\d-\\w{3}-\\d{4} \\d\\d:\\d\\d:\\d\\d [+-]\\d+)\"");
-    private static final int PATID_DATE_GROUP = 1;
 
+    private static final int PATID_DATE_GROUP = 1;
     private static final Pattern PATSEQ = Pattern.compile("\\* (\\d+) "); // Sequence number
+
     private static final int PATSEQ_SEQUENCE_GROUP = 1;
 
     // e.g. * 382 EXISTS
@@ -99,8 +226,8 @@ public final class IMAPExportMbox
 
     // AAAC NO [TEMPFAIL] FETCH Temporary failure on server [CODE: WBL]
     private static final Pattern PATTEMPFAIL = Pattern.compile("[A-Z]{4} NO \\[TEMPFAIL\\] FETCH .*");
-
     private static final int CONNECT_TIMEOUT = 10; // Seconds
+
     private static final int READ_TIMEOUT = 10;
 
     public static void main(final String[] args) throws IOException, URISyntaxException
@@ -316,11 +443,6 @@ public final class IMAPExportMbox
         }
     }
 
-    private static boolean startsWith(final String input, final Pattern pat) {
-        final Matcher m = pat.matcher(input);
-        return m.lookingAt();
-    }
-
     private static String matches(final String input, final Pattern pat, final int index) {
         final Matcher m = pat.matcher(input);
         if (m.lookingAt()) {
@@ -329,130 +451,8 @@ public final class IMAPExportMbox
         return null;
     }
 
-    private static class MboxListener implements IMAPChunkListener {
-
-        private final BufferedWriter bufferedWriter;
-        volatile AtomicInteger total = new AtomicInteger();
-        volatile String lastFetched;
-        volatile List<String> missingIds = new ArrayList<>();
-        volatile long lastSeq = -1;
-        private final String lineSeparator;
-        private final SimpleDateFormat DATE_FORMAT // for mbox From_ lines
-            = new SimpleDateFormat("EEE MMM dd HH:mm:ss YYYY");
-
-        // e.g. INTERNALDATE "27-Oct-2013 07:43:24 +0000"
-        // for parsing INTERNALDATE
-        private final SimpleDateFormat IDPARSE = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z");
-        private final boolean printHash;
-        private final boolean printMarker;
-        private final boolean checkSequence;
-
-        MboxListener(final BufferedWriter bufferedWriter, final String lineSeparator, final boolean printHash,
-            final boolean printMarker, final boolean checkSequence) {
-            this.lineSeparator = lineSeparator;
-            this.printHash = printHash;
-            this.printMarker = printMarker;
-            DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
-            this.bufferedWriter = bufferedWriter;
-            this.checkSequence = checkSequence;
-        }
-
-        @Override
-        public boolean chunkReceived(final IMAP imap) {
-            final String[] replyStrings = imap.getReplyStrings();
-            Date received = new Date();
-            final String firstLine = replyStrings[0];
-            Matcher m = PATID.matcher(firstLine);
-            if (m.lookingAt()) { // found a match
-                final String date = m.group(PATID_DATE_GROUP);
-                try {
-                    received=IDPARSE.parse(date);
-                } catch (final ParseException e) {
-                    System.err.println(e);
-                }
-            } else {
-                System.err.println("No timestamp found in: " + firstLine + "  - using current time");
-            }
-            String replyTo = "MAILER-DAEMON"; // default
-            for(int i=1; i< replyStrings.length - 1; i++) {
-                final String line = replyStrings[i];
-                if (line.startsWith("Return-Path: ")) {
-                    final String[] parts = line.split(" ", 2);
-                    if (!parts[1].equals("<>")) {// Don't replace default with blank
-                        replyTo = parts[1];
-                        if (replyTo.startsWith("<")) {
-                            if (replyTo.endsWith(">")) {
-                                replyTo = replyTo.substring(1,replyTo.length()-1); // drop <> wrapper
-                            } else {
-                                System.err.println("Unexpected Return-path: '" + line+ "' in " + firstLine);
-                            }
-                        }
-                    }
-                    break;
-                }
-            }
-            try {
-                // Add initial mbox header line
-                bufferedWriter.append("From ");
-                bufferedWriter.append(replyTo);
-                bufferedWriter.append(' ');
-                bufferedWriter.append(DATE_FORMAT.format(received));
-                bufferedWriter.append(lineSeparator);
-                // Debug
-                bufferedWriter.append("X-IMAP-Response: ").append(firstLine).append(lineSeparator);
-                if (printMarker) {
-                    System.err.println("[" + total + "] " + firstLine);
-                }
-                // Skip first and last lines
-                for(int i=1; i< replyStrings.length - 1; i++) {
-                    final String line = replyStrings[i];
-                        if (startsWith(line, PATFROM)) {
-                            bufferedWriter.append('>'); // Escape a From_ line
-                        }
-                        bufferedWriter.append(line);
-                        bufferedWriter.append(lineSeparator);
-                }
-                // The last line ends with the trailing closing ")" which needs to be stripped
-                final String lastLine = replyStrings[replyStrings.length-1];
-                final int lastLength = lastLine.length();
-                if (lastLength > 1) { // there's some content, we need to save it
-                    bufferedWriter.append(lastLine, 0, lastLength-1);
-                    bufferedWriter.append(lineSeparator);
-                }
-                bufferedWriter.append(lineSeparator); // blank line between entries
-            } catch (final IOException e) {
-                e.printStackTrace();
-                throw new RuntimeException(e); // chunkReceived cannot throw a checked Exception
-            }
-            lastFetched = firstLine;
-            total.incrementAndGet();
-            if (checkSequence) {
-                m = PATSEQ.matcher(firstLine);
-                if (m.lookingAt()) { // found a match
-                    final long msgSeq = Long.parseLong(m.group(PATSEQ_SEQUENCE_GROUP)); // Cannot fail to parse
-                    if (lastSeq != -1) {
-                        final long missing = msgSeq - lastSeq - 1;
-                        if (missing != 0) {
-                            for(long j = lastSeq + 1; j < msgSeq; j++) {
-                                missingIds.add(String.valueOf(j));
-                            }
-                            System.err.println(
-                                "*** Sequence error: current=" + msgSeq + " previous=" + lastSeq + " Missing=" + missing);
-                        }
-                    }
-                    lastSeq = msgSeq;
-                }
-            }
-            if (printHash) {
-                System.err.print(".");
-            }
-            return true;
-        }
-
-        public void close() throws IOException {
-            if (bufferedWriter != null) {
-                bufferedWriter.close();
-            }
-        }
+    private static boolean startsWith(final String input, final Pattern pat) {
+        final Matcher m = pat.matcher(input);
+        return m.lookingAt();
     }
 }
diff --git a/src/main/java/org/apache/commons/net/examples/mail/IMAPImportMbox.java b/src/main/java/org/apache/commons/net/examples/mail/IMAPImportMbox.java
index 373a990..2214c98 100644
--- a/src/main/java/org/apache/commons/net/examples/mail/IMAPImportMbox.java
+++ b/src/main/java/org/apache/commons/net/examples/mail/IMAPImportMbox.java
@@ -51,6 +51,34 @@ public final class IMAPImportMbox
     private static final String CRLF = "\r\n";
     private static final Pattern PATFROM = Pattern.compile(">+From "); // escaped From
 
+    private static String getDate(final String msg) {
+                                              // From SENDER Fri Sep 13 17:04:01 2019
+        final Pattern FROM_RE = Pattern.compile("From \\S+ +\\S+ (\\S+)  ?(\\S+) (\\S+) (\\S+)");
+        //                                                 [Fri]   Sep      13     HMS   2019
+        // output date: 13-Sep-2019 17:04:01 +0000
+        String date = null;
+        final Matcher m = FROM_RE.matcher(msg);
+        if (m.lookingAt()) {
+            date = m.group(2)+"-"+m.group(1)+"-"+m.group(4)+" "+m.group(3)+" +0000";
+        }
+        return date;
+    }
+
+    /**
+     * Is at least one entry in the list contained in the string?
+     * @param contains the list of strings to look for
+     * @param string the String to check against
+     * @return true if at least one entry in the contains list is contained in the string
+     */
+    private static boolean listContains(final List<String> contains, final String string) {
+        for(final String entry : contains) {
+            if (string.contains(entry)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static void main(final String[] args) throws IOException
     {
         if (args.length < 2)
@@ -146,24 +174,6 @@ public final class IMAPImportMbox
         System.out.println("Processed " + total + " messages, loaded " + loaded);
     }
 
-    private static boolean startsWith(final String input, final Pattern pat) {
-        final Matcher m = pat.matcher(input);
-        return m.lookingAt();
-    }
-
-    private static String getDate(final String msg) {
-                                              // From SENDER Fri Sep 13 17:04:01 2019
-        final Pattern FROM_RE = Pattern.compile("From \\S+ +\\S+ (\\S+)  ?(\\S+) (\\S+) (\\S+)");
-        //                                                 [Fri]   Sep      13     HMS   2019
-        // output date: 13-Sep-2019 17:04:01 +0000
-        String date = null;
-        final Matcher m = FROM_RE.matcher(msg);
-        if (m.lookingAt()) {
-            date = m.group(2)+"-"+m.group(1)+"-"+m.group(4)+" "+m.group(3)+" +0000";
-        }
-        return date;
-    }
-
     private static boolean process(final StringBuilder sb, final IMAPClient imap, final String folder
             ,final int msgNum) throws IOException {
         final int length = sb.length();
@@ -179,6 +189,11 @@ public final class IMAPImportMbox
         return haveMessage;
     }
 
+    private static boolean startsWith(final String input, final Pattern pat) {
+        final Matcher m = pat.matcher(input);
+        return m.lookingAt();
+    }
+
     /**
      * Is the message wanted?
      *
@@ -194,19 +209,4 @@ public final class IMAPImportMbox
              || listContains(contains, line); // contains string
     }
 
-    /**
-     * Is at least one entry in the list contained in the string?
-     * @param contains the list of strings to look for
-     * @param string the String to check against
-     * @return true if at least one entry in the contains list is contained in the string
-     */
-    private static boolean listContains(final List<String> contains, final String string) {
-        for(final String entry : contains) {
-            if (string.contains(entry)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/examples/mail/POP3ExportMbox.java b/src/main/java/org/apache/commons/net/examples/mail/POP3ExportMbox.java
index a5a1c59..db3bf93 100644
--- a/src/main/java/org/apache/commons/net/examples/mail/POP3ExportMbox.java
+++ b/src/main/java/org/apache/commons/net/examples/mail/POP3ExportMbox.java
@@ -168,6 +168,11 @@ public final class POP3ExportMbox
         }
     }
 
+    private static boolean startsWith(final String input, final Pattern pat) {
+        final Matcher m = pat.matcher(input);
+        return m.lookingAt();
+    }
+
     private static void writeFile(final POP3Client pop3, final OutputStreamWriter fw, final int i) throws IOException {
         try (final BufferedReader r = (BufferedReader) pop3.retrieveMessage(i)) {
             String line;
@@ -200,10 +205,5 @@ public final class POP3ExportMbox
             fw.write("\n");
         }
     }
-
-    private static boolean startsWith(final String input, final Pattern pat) {
-        final Matcher m = pat.matcher(input);
-        return m.lookingAt();
-    }
 }
 
diff --git a/src/main/java/org/apache/commons/net/examples/mail/POP3Mail.java b/src/main/java/org/apache/commons/net/examples/mail/POP3Mail.java
index bc921ac..5ea2c86 100644
--- a/src/main/java/org/apache/commons/net/examples/mail/POP3Mail.java
+++ b/src/main/java/org/apache/commons/net/examples/mail/POP3Mail.java
@@ -38,23 +38,6 @@ import org.apache.commons.net.pop3.POP3SClient;
 public final class POP3Mail
 {
 
-    public static void printMessageInfo(final BufferedReader reader, final int id) throws IOException  {
-        String from = "";
-        String subject = "";
-        String line;
-        while ((line = reader.readLine()) != null)
-        {
-            final String lower = line.toLowerCase(Locale.ENGLISH);
-            if (lower.startsWith("from: ")) {
-                from = line.substring(6).trim();
-            }  else if (lower.startsWith("subject: ")) {
-                subject = line.substring(9).trim();
-            }
-        }
-
-        System.out.println(Integer.toString(id) + " From: " + from + "  Subject: " + subject);
-    }
-
     public static void main(final String[] args)
     {
         if (args.length < 3)
@@ -172,5 +155,22 @@ public final class POP3Mail
             return;
         }
     }
+
+    public static void printMessageInfo(final BufferedReader reader, final int id) throws IOException  {
+        String from = "";
+        String subject = "";
+        String line;
+        while ((line = reader.readLine()) != null)
+        {
+            final String lower = line.toLowerCase(Locale.ENGLISH);
+            if (lower.startsWith("from: ")) {
+                from = line.substring(6).trim();
+            }  else if (lower.startsWith("subject: ")) {
+                subject = line.substring(9).trim();
+            }
+        }
+
+        System.out.println(Integer.toString(id) + " From: " + from + "  Subject: " + subject);
+    }
 }
 
diff --git a/src/main/java/org/apache/commons/net/examples/mail/Utils.java b/src/main/java/org/apache/commons/net/examples/mail/Utils.java
index 996c319..6408c10 100644
--- a/src/main/java/org/apache/commons/net/examples/mail/Utils.java
+++ b/src/main/java/org/apache/commons/net/examples/mail/Utils.java
@@ -28,10 +28,6 @@ import java.util.Locale;
  */
 class Utils {
 
-    private Utils() {
-        // not instantiable
-    }
-
     /**
      * If the initial password is:
      * '*' - replace it with a line read from the system console
@@ -67,4 +63,8 @@ class Utils {
         return password;
     }
 
+    private Utils() {
+        // not instantiable
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/net/examples/nntp/ExtendedNNTPOps.java b/src/main/java/org/apache/commons/net/examples/nntp/ExtendedNNTPOps.java
index f640ba4..4e2961e 100644
--- a/src/main/java/org/apache/commons/net/examples/nntp/ExtendedNNTPOps.java
+++ b/src/main/java/org/apache/commons/net/examples/nntp/ExtendedNNTPOps.java
@@ -32,14 +32,27 @@ import org.apache.commons.net.nntp.NewsgroupInfo;
 public class ExtendedNNTPOps {
 
 
+    public static void main(final String[] args) {
+        final ExtendedNNTPOps ops;
+
+        final int argc = args.length;
+        if (argc < 1) {
+            System.err.println("usage: ExtendedNNTPOps nntpserver [username password]");
+            System.exit(1);
+        }
+
+        ops = new ExtendedNNTPOps();
+        ops.demo(args[0], argc >=3 ? args[1] : null, argc >=3 ? args[2] : null);
+    }
+
     private final NNTPClient client;
 
+
     public ExtendedNNTPOps() {
         client = new NNTPClient();
         client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
     }
 
-
     private void demo(final String host, final String user, final String password) {
         try {
             client.connect(host);
@@ -81,17 +94,4 @@ public class ExtendedNNTPOps {
         }
     }
 
-    public static void main(final String[] args) {
-        final ExtendedNNTPOps ops;
-
-        final int argc = args.length;
-        if (argc < 1) {
-            System.err.println("usage: ExtendedNNTPOps nntpserver [username password]");
-            System.exit(1);
-        }
-
-        ops = new ExtendedNNTPOps();
-        ops.demo(args[0], argc >=3 ? args[1] : null, argc >=3 ? args[2] : null);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/examples/nntp/MessageThreading.java b/src/main/java/org/apache/commons/net/examples/nntp/MessageThreading.java
index a692051..547ede8 100644
--- a/src/main/java/org/apache/commons/net/examples/nntp/MessageThreading.java
+++ b/src/main/java/org/apache/commons/net/examples/nntp/MessageThreading.java
@@ -32,9 +32,6 @@ import org.apache.commons.net.nntp.Threader;
  * Sample program demonstrating the use of article iteration and threading.
  */
 public class MessageThreading {
-    public MessageThreading() {
-    }
-
     public static void main(final String[] args) throws SocketException, IOException {
 
         if (args.length != 2 && args.length != 4) {
@@ -83,4 +80,7 @@ public class MessageThreading {
         Article.printThread(root, 0);
     }
 
+    public MessageThreading() {
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/net/examples/ntp/NTPClient.java b/src/main/java/org/apache/commons/net/examples/ntp/NTPClient.java
index 7e18a06..1f067f7 100644
--- a/src/main/java/org/apache/commons/net/examples/ntp/NTPClient.java
+++ b/src/main/java/org/apache/commons/net/examples/ntp/NTPClient.java
@@ -49,6 +49,37 @@ public final class NTPClient
 
     private static final NumberFormat numberFormat = new java.text.DecimalFormat("0.00");
 
+    public static void main(final String[] args)
+    {
+        if (args.length == 0) {
+            System.err.println("Usage: NTPClient <hostname-or-address-list>");
+            System.exit(1);
+        }
+
+        final NTPUDPClient client = new NTPUDPClient();
+        // We want to timeout if a response takes longer than 10 seconds
+        client.setDefaultTimeout(10000);
+        try {
+            client.open();
+            for (final String arg : args)
+            {
+                System.out.println();
+                try {
+                    final InetAddress hostAddr = InetAddress.getByName(arg);
+                    System.out.println("> " + hostAddr.getHostName() + "/" + hostAddr.getHostAddress());
+                    final TimeInfo info = client.getTime(hostAddr);
+                    processResponse(info);
+                } catch (final IOException ioe) {
+                    ioe.printStackTrace();
+                }
+            }
+        } catch (final SocketException e) {
+            e.printStackTrace();
+        }
+
+        client.close();
+    }
+
     /**
      * Process <code>TimeInfo</code> object and print its details.
      * @param info <code>TimeInfo</code> object.
@@ -146,35 +177,4 @@ public final class NTPClient
                 + ", clock offset(ms)=" + offset); // offset in ms
     }
 
-    public static void main(final String[] args)
-    {
-        if (args.length == 0) {
-            System.err.println("Usage: NTPClient <hostname-or-address-list>");
-            System.exit(1);
-        }
-
-        final NTPUDPClient client = new NTPUDPClient();
-        // We want to timeout if a response takes longer than 10 seconds
-        client.setDefaultTimeout(10000);
-        try {
-            client.open();
-            for (final String arg : args)
-            {
-                System.out.println();
-                try {
-                    final InetAddress hostAddr = InetAddress.getByName(arg);
-                    System.out.println("> " + hostAddr.getHostName() + "/" + hostAddr.getHostAddress());
-                    final TimeInfo info = client.getTime(hostAddr);
-                    processResponse(info);
-                } catch (final IOException ioe) {
-                    ioe.printStackTrace();
-                }
-            }
-        } catch (final SocketException e) {
-            e.printStackTrace();
-        }
-
-        client.close();
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/examples/ntp/SimpleNTPServer.java b/src/main/java/org/apache/commons/net/examples/ntp/SimpleNTPServer.java
index 0b48dde..7b55d96 100644
--- a/src/main/java/org/apache/commons/net/examples/ntp/SimpleNTPServer.java
+++ b/src/main/java/org/apache/commons/net/examples/ntp/SimpleNTPServer.java
@@ -38,9 +38,28 @@ import org.apache.commons.net.ntp.TimeStamp;
  */
 public class SimpleNTPServer implements Runnable {
 
+    public static void main(final String[] args)
+    {
+        int port = NtpV3Packet.NTP_PORT;
+        if (args.length != 0)
+        {
+            try {
+                port = Integer.parseInt(args[0]);
+            } catch (final NumberFormatException nfe) {
+                nfe.printStackTrace();
+            }
+        }
+        final SimpleNTPServer timeServer = new SimpleNTPServer(port);
+        try {
+            timeServer.start();
+        } catch (final IOException e) {
+            e.printStackTrace();
+        }
+    }
     private int port;
     private volatile boolean running;
     private boolean started;
+
     private DatagramSocket socket;
 
     /**
@@ -66,31 +85,6 @@ public class SimpleNTPServer implements Runnable {
         this.port = port;
     }
 
-    public int getPort()
-    {
-        return port;
-    }
-
-    /**
-     * Returns state of whether time service is running.
-     *
-     * @return true if time service is running
-     */
-    public boolean isRunning()
-    {
-        return running;
-    }
-
-    /**
-     * Returns state of whether time service is running.
-     *
-     * @return true if time service is running
-     */
-    public boolean isStarted()
-    {
-        return started;
-    }
-
     /**
      * Connects to server socket and listen for client connections.
      *
@@ -109,46 +103,9 @@ public class SimpleNTPServer implements Runnable {
         }
     }
 
-    /**
-     * Starts time service and provide time to client connections.
-     *
-     * @throws java.io.IOException if an I/O error occurs when creating the socket.
-     */
-    public void start() throws IOException
-    {
-        if (socket == null)
-        {
-            connect();
-        }
-        if (!started)
-        {
-            started = true;
-            new Thread(this).start();
-        }
-    }
-
-    /**
-     * Main method to service client connections.
-     */
-    @Override
-    public void run()
+    public int getPort()
     {
-        running = true;
-        final byte buffer[] = new byte[48];
-        final DatagramPacket request = new DatagramPacket(buffer, buffer.length);
-        do {
-            try {
-                socket.receive(request);
-                final long rcvTime = System.currentTimeMillis();
-                handlePacket(request, rcvTime);
-            } catch (final IOException e) {
-                if (running)
-                {
-                    e.printStackTrace();
-                }
-                // otherwise socket thrown exception during shutdown
-            }
-        } while (running);
+        return port;
     }
 
     /**
@@ -196,6 +153,68 @@ public class SimpleNTPServer implements Runnable {
     }
 
     /**
+     * Returns state of whether time service is running.
+     *
+     * @return true if time service is running
+     */
+    public boolean isRunning()
+    {
+        return running;
+    }
+
+    /**
+     * Returns state of whether time service is running.
+     *
+     * @return true if time service is running
+     */
+    public boolean isStarted()
+    {
+        return started;
+    }
+
+    /**
+     * Main method to service client connections.
+     */
+    @Override
+    public void run()
+    {
+        running = true;
+        final byte buffer[] = new byte[48];
+        final DatagramPacket request = new DatagramPacket(buffer, buffer.length);
+        do {
+            try {
+                socket.receive(request);
+                final long rcvTime = System.currentTimeMillis();
+                handlePacket(request, rcvTime);
+            } catch (final IOException e) {
+                if (running)
+                {
+                    e.printStackTrace();
+                }
+                // otherwise socket thrown exception during shutdown
+            }
+        } while (running);
+    }
+
+    /**
+     * Starts time service and provide time to client connections.
+     *
+     * @throws java.io.IOException if an I/O error occurs when creating the socket.
+     */
+    public void start() throws IOException
+    {
+        if (socket == null)
+        {
+            connect();
+        }
+        if (!started)
+        {
+            started = true;
+            new Thread(this).start();
+        }
+    }
+
+    /**
      * Closes server socket and stop listening.
      */
     public void stop()
@@ -209,23 +228,4 @@ public class SimpleNTPServer implements Runnable {
         started = false;
     }
 
-    public static void main(final String[] args)
-    {
-        int port = NtpV3Packet.NTP_PORT;
-        if (args.length != 0)
-        {
-            try {
-                port = Integer.parseInt(args[0]);
-            } catch (final NumberFormatException nfe) {
-                nfe.printStackTrace();
-            }
-        }
-        final SimpleNTPServer timeServer = new SimpleNTPServer(port);
-        try {
-            timeServer.start();
-        } catch (final IOException e) {
-            e.printStackTrace();
-        }
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/examples/ntp/TimeClient.java b/src/main/java/org/apache/commons/net/examples/ntp/TimeClient.java
index 61619a1..967b0d7 100644
--- a/src/main/java/org/apache/commons/net/examples/ntp/TimeClient.java
+++ b/src/main/java/org/apache/commons/net/examples/ntp/TimeClient.java
@@ -38,30 +38,6 @@ import org.apache.commons.net.time.TimeUDPClient;
 public final class TimeClient
 {
 
-    public static void timeTCP(final String host) throws IOException
-    {
-        final TimeTCPClient client = new TimeTCPClient();
-    try {
-          // We want to timeout if a response takes longer than 60 seconds
-          client.setDefaultTimeout(60000);
-      client.connect(host);
-          System.out.println(client.getDate());
-    } finally {
-          client.disconnect();
-    }
-    }
-
-    public static void timeUDP(final String host) throws IOException
-    {
-        final TimeUDPClient client = new TimeUDPClient();
-
-        // We want to timeout if a response takes longer than 60 seconds
-        client.setDefaultTimeout(60000);
-        client.open();
-        System.out.println(client.getDate(InetAddress.getByName(host)));
-        client.close();
-    }
-
     public static void main(final String[] args)
     {
 
@@ -97,5 +73,29 @@ public final class TimeClient
 
     }
 
+    public static void timeTCP(final String host) throws IOException
+    {
+        final TimeTCPClient client = new TimeTCPClient();
+    try {
+          // We want to timeout if a response takes longer than 60 seconds
+          client.setDefaultTimeout(60000);
+      client.connect(host);
+          System.out.println(client.getDate());
+    } finally {
+          client.disconnect();
+    }
+    }
+
+    public static void timeUDP(final String host) throws IOException
+    {
+        final TimeUDPClient client = new TimeUDPClient();
+
+        // We want to timeout if a response takes longer than 60 seconds
+        client.setDefaultTimeout(60000);
+        client.open();
+        System.out.println(client.getDate(InetAddress.getByName(host)));
+        client.close();
+    }
+
 }
 
diff --git a/src/main/java/org/apache/commons/net/examples/unix/rdate.java b/src/main/java/org/apache/commons/net/examples/unix/rdate.java
index 3bcdf5e..60edfe3 100644
--- a/src/main/java/org/apache/commons/net/examples/unix/rdate.java
+++ b/src/main/java/org/apache/commons/net/examples/unix/rdate.java
@@ -37,29 +37,6 @@ import org.apache.commons.net.time.TimeUDPClient;
 public final class rdate
 {
 
-    public static void timeTCP(final String host) throws IOException
-    {
-        final TimeTCPClient client = new TimeTCPClient();
-
-        // We want to timeout if a response takes longer than 60 seconds
-        client.setDefaultTimeout(60000);
-        client.connect(host);
-        System.out.println(client.getDate().toString());
-        client.disconnect();
-    }
-
-    public static void timeUDP(final String host) throws IOException
-    {
-        final TimeUDPClient client = new TimeUDPClient();
-
-        // We want to timeout if a response takes longer than 60 seconds
-        client.setDefaultTimeout(60000);
-        client.open();
-        System.out.println(client.getDate(InetAddress.getByName(host)).toString());
-        client.close();
-    }
-
-
     public static void main(final String[] args)
     {
 
@@ -95,5 +72,28 @@ public final class rdate
 
     }
 
+    public static void timeTCP(final String host) throws IOException
+    {
+        final TimeTCPClient client = new TimeTCPClient();
+
+        // We want to timeout if a response takes longer than 60 seconds
+        client.setDefaultTimeout(60000);
+        client.connect(host);
+        System.out.println(client.getDate().toString());
+        client.disconnect();
+    }
+
+
+    public static void timeUDP(final String host) throws IOException
+    {
+        final TimeUDPClient client = new TimeUDPClient();
+
+        // We want to timeout if a response takes longer than 60 seconds
+        client.setDefaultTimeout(60000);
+        client.open();
+        System.out.println(client.getDate(InetAddress.getByName(host)).toString());
+        client.close();
+    }
+
 }
 
diff --git a/src/main/java/org/apache/commons/net/finger/FingerClient.java b/src/main/java/org/apache/commons/net/finger/FingerClient.java
index 3a8e580..255175e 100644
--- a/src/main/java/org/apache/commons/net/finger/FingerClient.java
+++ b/src/main/java/org/apache/commons/net/finger/FingerClient.java
@@ -72,48 +72,20 @@ public class FingerClient extends SocketClient
 
 
     /**
-     * Fingers a user at the connected host and returns the output
-     * as a String.  You must first connect to a finger server before
-     * calling this method, and you should disconnect afterward.
-     *
-     * @param longOutput Set to true if long output is requested, false if not.
-     * @param username  The name of the user to finger.
-     * @return The result of the finger query.
-     * @throws IOException If an I/O error occurs while reading the socket.
-     */
-    public String query(final boolean longOutput, final String username) throws IOException
-    {
-        int read;
-        final StringBuilder result = new StringBuilder(buffer.length);
-
-        try (final BufferedReader input = new BufferedReader(
-                new InputStreamReader(getInputStream(longOutput, username), getCharset()))) {
-            while (true) {
-                read = input.read(buffer, 0, buffer.length);
-                if (read <= 0) {
-                    break;
-                }
-                result.append(buffer, 0, read);
-            }
-        }
-
-        return result.toString();
-    }
-
-
-    /**
-     * Fingers the connected host and returns the output
-     * as a String.  You must first connect to a finger server before
-     * calling this method, and you should disconnect afterward.
-     * This is equivalent to calling <code> query(longOutput, "") </code>.
+     * Fingers the connected host and returns the input stream from
+     * the network connection of the finger query.  This is equivalent to
+     * calling getInputStream(longOutput, "").  You must first connect to a
+     * finger server before calling this method, and you should disconnect
+     * after finishing reading the stream.
      *
      * @param longOutput Set to true if long output is requested, false if not.
-     * @return The result of the finger query.
-     * @throws IOException If an I/O error occurs while reading the socket.
+     * @return The InputStream of the network connection of the finger query.
+     *         Can be read to obtain finger results.
+     * @throws IOException If an I/O error during the operation.
      */
-    public String query(final boolean longOutput) throws IOException
+    public InputStream getInputStream(final boolean longOutput) throws IOException
     {
-        return query(longOutput, "");
+        return getInputStream(longOutput, "");
     }
 
 
@@ -135,6 +107,7 @@ public class FingerClient extends SocketClient
         return getInputStream(longOutput, username, null);
     }
 
+
     /**
      * Fingers a user and returns the input stream from the network connection
      * of the finger query.  You must first connect to a finger server before
@@ -171,20 +144,47 @@ public class FingerClient extends SocketClient
     }
 
     /**
-     * Fingers the connected host and returns the input stream from
-     * the network connection of the finger query.  This is equivalent to
-     * calling getInputStream(longOutput, "").  You must first connect to a
-     * finger server before calling this method, and you should disconnect
-     * after finishing reading the stream.
+     * Fingers the connected host and returns the output
+     * as a String.  You must first connect to a finger server before
+     * calling this method, and you should disconnect afterward.
+     * This is equivalent to calling <code> query(longOutput, "") </code>.
      *
      * @param longOutput Set to true if long output is requested, false if not.
-     * @return The InputStream of the network connection of the finger query.
-     *         Can be read to obtain finger results.
-     * @throws IOException If an I/O error during the operation.
+     * @return The result of the finger query.
+     * @throws IOException If an I/O error occurs while reading the socket.
      */
-    public InputStream getInputStream(final boolean longOutput) throws IOException
+    public String query(final boolean longOutput) throws IOException
     {
-        return getInputStream(longOutput, "");
+        return query(longOutput, "");
+    }
+
+    /**
+     * Fingers a user at the connected host and returns the output
+     * as a String.  You must first connect to a finger server before
+     * calling this method, and you should disconnect afterward.
+     *
+     * @param longOutput Set to true if long output is requested, false if not.
+     * @param username  The name of the user to finger.
+     * @return The result of the finger query.
+     * @throws IOException If an I/O error occurs while reading the socket.
+     */
+    public String query(final boolean longOutput, final String username) throws IOException
+    {
+        int read;
+        final StringBuilder result = new StringBuilder(buffer.length);
+
+        try (final BufferedReader input = new BufferedReader(
+                new InputStreamReader(getInputStream(longOutput, username), getCharset()))) {
+            while (true) {
+                read = input.read(buffer, 0, buffer.length);
+                if (read <= 0) {
+                    break;
+                }
+                result.append(buffer, 0, read);
+            }
+        }
+
+        return result.toString();
     }
 
 }
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 389e3e3..90fbcb4 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTP.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTP.java
@@ -275,21 +275,6 @@ public class FTP extends SocketClient
         _commandSupport_ = new ProtocolCommandSupport(this);
     }
 
-    // The RFC-compliant multiline termination check
-    private boolean strictCheck(final String line, final String code) {
-        return !(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' ');
-    }
-
-    // The strict check is too strong a condition because of non-conforming ftp
-    // servers like ftp.funet.fi which sent 226 as the last line of a
-    // 426 multi-line reply in response to ls /.  We relax the condition to
-    // test that the line starts with a digit rather than starting with
-    // the code.
-    private boolean lenientCheck(final String line) {
-        return !(line.length() > REPLY_CODE_LEN&& line.charAt(REPLY_CODE_LEN) != '-' &&
-                Character.isDigit(line.charAt(0)));
-    }
-
     /**
      * Get the reply, but don't pass it to command listeners.
      * Used for keep-alive processing only.
@@ -301,83 +286,17 @@ public class FTP extends SocketClient
         getReply(false);
     }
 
-    private int getReply(final boolean reportReply) throws IOException
-    {
-        final int length;
-
-        _newReplyString = true;
-        _replyLines.clear();
-
-        String line = _controlInput_.readLine();
-
-        if (line == null) {
-            throw new FTPConnectionClosedException(
-                    "Connection closed without indication.");
-        }
-
-        // In case we run into an anomaly we don't want fatal index exceptions
-        // to be thrown.
-        length = line.length();
-        if (length < REPLY_CODE_LEN) {
-            throw new MalformedServerReplyException(
-                "Truncated server reply: " + line);
-        }
-
-        String code = null;
-        try
-        {
-            code = line.substring(0, REPLY_CODE_LEN);
-            _replyCode = Integer.parseInt(code);
-        }
-        catch (final NumberFormatException e)
-        {
-            throw new MalformedServerReplyException(
-                "Could not parse response code.\nServer Reply: " + line);
-        }
-
-        _replyLines.add(line);
-
-        // Check the server reply type
-        if (length > REPLY_CODE_LEN) {
-            final 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.");
-                    }
-
-                    _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));
-
-            } 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 +"'");
-                }
-            }
-        } else if (isStrictReplyParsing()) {
-            throw new MalformedServerReplyException("Truncated server reply: '" + line +"'");
-        }
-
-        if (reportReply) {
-            fireReplyReceived(_replyCode, getReplyString());
-        }
-
-        if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) {
-            throw new FTPConnectionClosedException("FTP response 421 received.  Server closed connection.");
-        }
-        return _replyCode;
+    /**
+     * Send a noop and get the reply without reporting to the command listener.
+     * Intended for use with keep-alive.
+     *
+     * @throws IOException on error
+     * @since 3.0
+     */
+    protected void __noop() throws IOException {
+        final String msg = buildMessage(FTPCmd.NOOP.getCommand(), null);
+        send(msg);
+        __getReplyNoReport(); // This may timeout
     }
 
     /**
@@ -390,7 +309,6 @@ public class FTP extends SocketClient
         _connectAction_(null);
     }
 
-
     /**
      * Initiates control connections and gets initial reply.
      * Initializes {@link #_controlInput_} and {@link #_controlOutput_}.
@@ -434,63 +352,51 @@ public class FTP extends SocketClient
         }
     }
 
-
     /**
-     * Saves the character encoding to be used by the FTP control connection.
-     * Some FTP servers require that commands be issued in a non-ASCII
-     * encoding like UTF-8 so that file names with multi-byte character
-     * representations (e.g, Big 8) can be specified.
-     * <p>
-     * Please note that this has to be set before the connection is established.
+     * A convenience method to send the FTP ABOR command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param encoding The new character encoding for the control connection.
-     */
-    public void setControlEncoding(final String encoding) {
-        _controlEncoding = encoding;
-    }
-
-
-    /**
-     * @return The character encoding used to communicate over the
-     * control connection.
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
      */
-    public String getControlEncoding() {
-        return _controlEncoding;
+    public int abor() throws IOException
+    {
+        return sendCommand(FTPCmd.ABOR);
     }
 
 
     /**
-     * Closes the control connection to the FTP server and sets to null
-     * some internal data so that the memory may be reclaimed by the
-     * garbage collector.  The reply text and code information from the
-     * last command is voided so that the memory it used may be reclaimed.
-     * Also sets {@link #_controlInput_} and {@link #_controlOutput_} to null.
+     * A convenience method to send the FTP ACCT command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @throws IOException If an error occurs while disconnecting.
+     * @param account  The account name to access.
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
      */
-    @Override
-    public void disconnect() throws IOException
+    public int acct(final String account) throws IOException
     {
-        super.disconnect();
-        _controlInput_ = null;
-        _controlOutput_ = null;
-        _newReplyString = false;
-        _replyString = null;
+        return sendCommand(FTPCmd.ACCT, account);
     }
 
 
     /**
-     * Sends an FTP command to the server, waits for a reply and returns the
-     * numerical response code.  After invocation, for more detailed
-     * information, the actual reply text can be accessed by calling
-     * {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.
+     * A convenience method to send the FTP ALLO command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param command  The text representation of the  FTP command to send.
-     * @param args The arguments to the FTP command.  If this parameter is
-     *             set to null, then the command is sent with no argument.
-     * @return The integer value of the FTP reply code returned by the server
-     *         in response to the command.
+     * @param bytes The number of bytes to allocate.
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -499,77 +405,19 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int sendCommand(final String command, final String args) throws IOException
+    public int allo(final int bytes) throws IOException
     {
-        if (_controlOutput_ == null) {
-            throw new IOException("Connection is not open");
-        }
-
-        final String message = buildMessage(command, args);
-
-        send(message);
-
-        fireCommandSent(command, message);
-
-        return getReply();
-    }
-
-    private String buildMessage(final String command, final String args) {
-        final StringBuilder __commandBuffer = new StringBuilder();
-
-        __commandBuffer.append(command);
-
-        if (args != null)
-        {
-            __commandBuffer.append(' ');
-            __commandBuffer.append(args);
-        }
-        __commandBuffer.append(SocketClient.NETASCII_EOL);
-        return __commandBuffer.toString();
-    }
-
-    private void send(final String message) throws IOException,
-            FTPConnectionClosedException, SocketException {
-        try{
-            _controlOutput_.write(message);
-            _controlOutput_.flush();
-        }
-        catch (final SocketException e)
-        {
-            if (!isConnected())
-            {
-                throw new FTPConnectionClosedException("Connection unexpectedly closed.");
-            }
-            throw e;
-        }
+        return sendCommand(FTPCmd.ALLO, Integer.toString(bytes));
     }
 
-    /**
-     * Send a noop and get the reply without reporting to the command listener.
-     * Intended for use with keep-alive.
-     *
-     * @throws IOException on error
-     * @since 3.0
-     */
-    protected void __noop() throws IOException {
-        final String msg = buildMessage(FTPCmd.NOOP.getCommand(), null);
-        send(msg);
-        __getReplyNoReport(); // This may timeout
-    }
 
     /**
-     * Sends an FTP command to the server, waits for a reply and returns the
-     * numerical response code.  After invocation, for more detailed
-     * information, the actual reply text can be accessed by calling
-     * {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.
+     * A convenience method to send the FTP ALLO command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param command  The FTPCommand constant corresponding to the FTP command
-     *                 to send.
-     * @param args The arguments to the FTP command.  If this parameter is
-     *             set to null, then the command is sent with no argument.
-     * @return The integer value of the FTP reply code returned by the server
-     *         in response to the command.
+     * @param bytes The number of bytes to allocate.
+     * @param recordSize  The size of a record.
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -577,25 +425,20 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @deprecated (3.3) Use {@link #sendCommand(FTPCmd, String)} instead
      */
-    @Deprecated
-    public int sendCommand(final int command, final String args) throws IOException
+    public int allo(final int bytes, final int recordSize) throws IOException
     {
-        return sendCommand(FTPCommand.getCommand(command), args);
+        return sendCommand(FTPCmd.ALLO, Integer.toString(bytes) + " R " +
+                           Integer.toString(recordSize));
     }
 
+
     /**
-     * Sends an FTP command to the server, waits for a reply and returns the
-     * numerical response code.  After invocation, for more detailed
-     * information, the actual reply text can be accessed by calling
-     * {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.
+     * A convenience method to send the FTP ALLO command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param command  The FTPCmd enum corresponding to the FTP command
-     *                 to send.
-     * @return The integer value of the FTP reply code returned by the server
-     *         in response to the command.
+     * @param bytes The number of bytes to allocate.
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -603,25 +446,20 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.3
      */
-    public int sendCommand(final FTPCmd command)  throws IOException{
-        return sendCommand(command, null);
+    public int allo(final long bytes) throws IOException
+    {
+        return sendCommand(FTPCmd.ALLO, Long.toString(bytes));
     }
 
+
     /**
-     * Sends an FTP command to the server, waits for a reply and returns the
-     * numerical response code.  After invocation, for more detailed
-     * information, the actual reply text can be accessed by calling
-     * {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.
+     * A convenience method to send the FTP ALLO command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param command  The FTPCmd enum corresponding to the FTP command
-     *                 to send.
-     * @param args The arguments to the FTP command.  If this parameter is
-     *             set to null, then the command is sent with no argument.
-     * @return The integer value of the FTP reply code returned by the server
-     *         in response to the command.
+     * @param bytes The number of bytes to allocate.
+     * @param recordSize  The size of a record.
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -629,22 +467,23 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.3
      */
-    public int sendCommand(final FTPCmd command, final String args)  throws IOException{
-        return sendCommand(command.getCommand(), args);
+    public int allo(final long bytes, final int recordSize) throws IOException
+    {
+        return sendCommand(FTPCmd.ALLO, Long.toString(bytes) + " R " +
+                           Integer.toString(recordSize));
     }
 
     /**
-     * Sends an FTP command with no arguments to the server, waits for a
-     * reply and returns the numerical response code.  After invocation, for
-     * more detailed information, the actual reply text can be accessed by
-     * calling {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.
+     * A convenience method to send the FTP APPE command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
-     * @param command  The text representation of the  FTP command to send.
-     * @return The integer value of the FTP reply code returned by the server
-     *         in response to the command.
+     * @param pathname  The pathname to use for the file when stored at
+     *                  the remote end of the transfer.
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -653,34 +492,305 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int sendCommand(final String command) throws IOException
+    public int appe(final String pathname) throws IOException
     {
-        return sendCommand(command, null);
+        return sendCommand(FTPCmd.APPE, pathname);
+    }
+
+    private String buildMessage(final String command, final String args) {
+        final StringBuilder __commandBuffer = new StringBuilder();
+
+        __commandBuffer.append(command);
+
+        if (args != null)
+        {
+            __commandBuffer.append(' ');
+            __commandBuffer.append(args);
+        }
+        __commandBuffer.append(SocketClient.NETASCII_EOL);
+        return __commandBuffer.toString();
+    }
+
+    /**
+     * A convenience method to send the FTP CDUP command to the server,
+     * receive the reply, and return the reply code.
+     *
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     */
+    public int cdup() throws IOException
+    {
+        return sendCommand(FTPCmd.CDUP);
+    }
+
+    /**
+     * A convenience method to send the FTP CWD command to the server,
+     * receive the reply, and return the reply code.
+     *
+     * @param directory The new working directory.
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     */
+    public int cwd(final String directory) throws IOException
+    {
+        return sendCommand(FTPCmd.CWD, directory);
+    }
+
+    /**
+     * A convenience method to send the FTP DELE command to the server,
+     * receive the reply, and return the reply code.
+     *
+     * @param pathname The pathname to delete.
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     */
+    public int dele(final String pathname) throws IOException
+    {
+        return sendCommand(FTPCmd.DELE, pathname);
+    }
+
+    /**
+     * Closes the control connection to the FTP server and sets to null
+     * some internal data so that the memory may be reclaimed by the
+     * garbage collector.  The reply text and code information from the
+     * last command is voided so that the memory it used may be reclaimed.
+     * Also sets {@link #_controlInput_} and {@link #_controlOutput_} to null.
+     *
+     * @throws IOException If an error occurs while disconnecting.
+     */
+    @Override
+    public void disconnect() throws IOException
+    {
+        super.disconnect();
+        _controlInput_ = null;
+        _controlOutput_ = null;
+        _newReplyString = false;
+        _replyString = null;
+    }
+
+    /**
+     * A convenience method to send the FTP EPRT command to the server,
+     * receive the reply, and return the reply code.
+     *
+     * Examples:
+     * <ul>
+     * <li>EPRT |1|132.235.1.2|6275|</li>
+     * <li>EPRT |2|1080::8:800:200C:417A|5282|</li>
+     * </ul>
+     *
+     * @see "http://www.faqs.org/rfcs/rfc2428.html"
+     *
+     * @param host  The host owning the port.
+     * @param port  The new port.
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     * @since 2.2
+     */
+    public int eprt(final InetAddress host, final int port) throws IOException
+    {
+        final int num;
+        final StringBuilder info = new StringBuilder();
+        String h;
+
+        // If IPv6, trim the zone index
+        h = host.getHostAddress();
+        num = h.indexOf('%');
+        if (num > 0) {
+            h = h.substring(0, num);
+        }
+
+        info.append("|");
+
+        if (host instanceof Inet4Address) {
+            info.append("1");
+        } else if (host instanceof Inet6Address) {
+            info.append("2");
+        }
+        info.append("|");
+        info.append(h);
+        info.append("|");
+        info.append(port);
+        info.append("|");
+
+        return sendCommand(FTPCmd.EPRT, info.toString());
+    }
+
+
+    /**
+     * A convenience method to send the FTP EPSV command to the server,
+     * receive the reply, and return the reply code.  Remember, it's up
+     * to you to interpret the reply string containing the host/port
+     * information.
+     *
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     * @since 2.2
+     */
+    public int epsv() throws IOException
+    {
+        return sendCommand(FTPCmd.EPSV);
+    }
+
+
+    /**
+     * A convenience method to send the FTP FEAT command to the server, receive the reply,
+     * and return the reply code.
+     * @return The reply code received by the server
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     * @since 2.2
+     */
+    public int feat() throws IOException
+    {
+        return sendCommand(FTPCmd.FEAT);
     }
 
+    /**
+     * Provide command support to super-class
+     */
+    @Override
+    protected ProtocolCommandSupport getCommandSupport() {
+        return _commandSupport_;
+    }
+
+
+    /**
+     * @return The character encoding used to communicate over the
+     * control connection.
+     */
+    public String getControlEncoding() {
+        return _controlEncoding;
+    }
+
+    /**
+     * Fetches a reply from the FTP server and returns the integer reply
+     * code.  After calling this method, the actual reply text can be accessed
+     * from either  calling {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.  Only use this
+     * method if you are implementing your own FTP client or if you need to
+     * fetch a secondary response from the FTP server.
+     *
+     * @return The integer value of the reply code of the fetched FTP reply.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while receiving the
+     *                         server reply.
+     */
+    public int getReply() throws IOException
+    {
+        return getReply(true);
+    }
+
+    private int getReply(final boolean reportReply) throws IOException
+    {
+        final int length;
+
+        _newReplyString = true;
+        _replyLines.clear();
+
+        String line = _controlInput_.readLine();
+
+        if (line == null) {
+            throw new FTPConnectionClosedException(
+                    "Connection closed without indication.");
+        }
+
+        // In case we run into an anomaly we don't want fatal index exceptions
+        // to be thrown.
+        length = line.length();
+        if (length < REPLY_CODE_LEN) {
+            throw new MalformedServerReplyException(
+                "Truncated server reply: " + line);
+        }
+
+        String code = null;
+        try
+        {
+            code = line.substring(0, REPLY_CODE_LEN);
+            _replyCode = Integer.parseInt(code);
+        }
+        catch (final NumberFormatException e)
+        {
+            throw new MalformedServerReplyException(
+                "Could not parse response code.\nServer Reply: " + line);
+        }
+
+        _replyLines.add(line);
+
+        // Check the server reply type
+        if (length > REPLY_CODE_LEN) {
+            final 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.");
+                    }
+
+                    _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));
+
+            } 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 +"'");
+                }
+            }
+        } else if (isStrictReplyParsing()) {
+            throw new MalformedServerReplyException("Truncated server reply: '" + line +"'");
+        }
+
+        if (reportReply) {
+            fireReplyReceived(_replyCode, getReplyString());
+        }
 
-    /**
-     * Sends an FTP command with no arguments to the server, waits for a
-     * reply and returns the numerical response code.  After invocation, for
-     * more detailed information, the actual reply text can be accessed by
-     * calling {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.
-     *
-     * @param command  The FTPCommand constant corresponding to the FTP command
-     *                 to send.
-     * @return The integer value of the FTP reply code returned by the server
-     *         in response to the command.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int sendCommand(final int command) throws IOException
-    {
-        return sendCommand(command, null);
+        if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) {
+            throw new FTPConnectionClosedException("FTP response 421 received.  Server closed connection.");
+        }
+        return _replyCode;
     }
 
 
@@ -698,54 +808,6 @@ public class FTP extends SocketClient
     }
 
     /**
-     * Fetches a reply from the FTP server and returns the integer reply
-     * code.  After calling this method, the actual reply text can be accessed
-     * from either  calling {@link #getReplyString  getReplyString } or
-     * {@link #getReplyStrings  getReplyStrings }.  Only use this
-     * method if you are implementing your own FTP client or if you need to
-     * fetch a secondary response from the FTP server.
-     *
-     * @return The integer value of the reply code of the fetched FTP reply.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while receiving the
-     *                         server reply.
-     */
-    public int getReply() throws IOException
-    {
-        return getReply(true);
-    }
-
-
-    /**
-     * Returns the lines of text from the last FTP server response as an array
-     * of strings, one entry per line.  The end of line markers of each are
-     * stripped from each line.
-     *
-     * @return The lines of text from the last FTP response as an array.
-     */
-    public String[] getReplyStrings()
-    {
-        return _replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY);
-    }
-
-    /**
-     * Returns the nth line of text from the last FTP server response as a string. The end of line markers of each are
-     * stripped from the line.
-     *
-     * @param index The index of the line to return, 0-based.
-     *
-     * @return The lines of text from the last FTP response as an array.
-     */
-    String getReplyString(final int index)
-    {
-        return _replyLines.get(index);
-    }
-
-    /**
      * Returns the entire text of the last FTP server response exactly
      * as it was received, including all end of line markers in NETASCII
      * format.
@@ -772,66 +834,34 @@ public class FTP extends SocketClient
         return _replyString = buffer.toString();
     }
 
-
     /**
-     * A convenience method to send the FTP USER command to the server,
-     * receive the reply, and return the reply code.
+     * Returns the nth line of text from the last FTP server response as a string. The end of line markers of each are
+     * stripped from the line.
      *
-     * @param username  The username to login under.
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
+     * @param index The index of the line to return, 0-based.
+     *
+     * @return The lines of text from the last FTP response as an array.
      */
-    public int user(final String username) throws IOException
+    String getReplyString(final int index)
     {
-        return sendCommand(FTPCmd.USER, username);
+        return _replyLines.get(index);
     }
 
-    /**
-     * A convenience method to send the FTP PASS command to the server,
-     * receive the reply, and return the reply code.
-     * @param password The plain text password of the username being logged into.
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int pass(final String password) throws IOException
-    {
-        return sendCommand(FTPCmd.PASS, password);
-    }
 
     /**
-     * A convenience method to send the FTP ACCT command to the server,
-     * receive the reply, and return the reply code.
+     * Returns the lines of text from the last FTP server response as an array
+     * of strings, one entry per line.  The end of line markers of each are
+     * stripped from each line.
      *
-     * @param account  The account name to access.
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
+     * @return The lines of text from the last FTP response as an array.
      */
-    public int acct(final String account) throws IOException
+    public String[] getReplyStrings()
     {
-        return sendCommand(FTPCmd.ACCT, account);
+        return _replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY);
     }
 
-
     /**
-     * A convenience method to send the FTP ABOR command to the server,
+     * A convenience method to send the FTP HELP command to the server,
      * receive the reply, and return the reply code.
      *
      * @return The reply code received from the server.
@@ -843,16 +873,16 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int abor() throws IOException
+    public int help() throws IOException
     {
-        return sendCommand(FTPCmd.ABOR);
+        return sendCommand(FTPCmd.HELP);
     }
 
     /**
-     * A convenience method to send the FTP CWD command to the server,
+     * A convenience method to send the FTP HELP command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param directory The new working directory.
+     * @param command  The command name on which to request help.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -862,70 +892,51 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int cwd(final String directory) throws IOException
+    public int help(final String command) throws IOException
     {
-        return sendCommand(FTPCmd.CWD, directory);
+        return sendCommand(FTPCmd.HELP, command);
     }
 
     /**
-     * A convenience method to send the FTP CDUP command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
+     * Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2.
+     * @return True if strict, false if lenient
+     * @since 2.0
      */
-    public int cdup() throws IOException
-    {
-        return sendCommand(FTPCmd.CDUP);
+    public boolean isStrictMultilineParsing() {
+        return strictMultilineParsing;
     }
 
     /**
-     * A convenience method to send the FTP QUIT command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
+     * 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 int quit() throws IOException
-    {
-        return sendCommand(FTPCmd.QUIT);
+    public boolean isStrictReplyParsing() {
+        return strictReplyParsing;
     }
 
-    /**
-     * A convenience method to send the FTP REIN command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int rein() throws IOException
-    {
-        return sendCommand(FTPCmd.REIN);
+    // The strict check is too strong a condition because of non-conforming ftp
+    // servers like ftp.funet.fi which sent 226 as the last line of a
+    // 426 multi-line reply in response to ls /.  We relax the condition to
+    // test that the line starts with a digit rather than starting with
+    // the code.
+    private boolean lenientCheck(final String line) {
+        return !(line.length() > REPLY_CODE_LEN&& line.charAt(REPLY_CODE_LEN) != '-' &&
+                Character.isDigit(line.charAt(0)));
     }
 
     /**
-     * A convenience method to send the FTP SMNT command to the server,
-     * receive the reply, and return the reply code.
+     * A convenience method to send the FTP LIST command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
-     * @param dir  The directory name.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -935,56 +946,53 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int smnt(final String dir) throws IOException
+    public int list() throws IOException
     {
-        return sendCommand(FTPCmd.SMNT, dir);
+        return sendCommand(FTPCmd.LIST);
     }
 
     /**
-     * A convenience method to send the FTP PORT command to the server,
-     * receive the reply, and return the reply code.
+     * A convenience method to send the FTP LIST command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
-     * @param host  The host owning the port.
-     * @param port  The new port.
+     * @param pathname  The pathname to list,
+     * may be {@code null} in which case the command is sent with no parameters
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
      *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int port(final InetAddress host, final int port) throws IOException
-    {
-        int num;
-        final StringBuilder info = new StringBuilder(24);
-
-        info.append(host.getHostAddress().replace('.', ','));
-        num = port >>> 8;
-        info.append(',');
-        info.append(num);
-        info.append(',');
-        num = port & 0xff;
-        info.append(num);
-
-        return sendCommand(FTPCmd.PORT, info.toString());
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     */
+    public int list(final String pathname) throws IOException
+    {
+        return sendCommand(FTPCmd.LIST, pathname);
     }
 
     /**
-     * A convenience method to send the FTP EPRT command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * Examples:
-     * <ul>
-     * <li>EPRT |1|132.235.1.2|6275|</li>
-     * <li>EPRT |2|1080::8:800:200C:417A|5282|</li>
-     * </ul>
+     * Sends the MDTM command for the given file.
      *
-     * @see "http://www.faqs.org/rfcs/rfc2428.html"
+     * @param file name of file
+     * @return the status
+     * @throws IOException on error
+     * @since 2.0
+     **/
+    public int mdtm(final String file) throws IOException
+    {
+        return sendCommand(FTPCmd.MDTM, file);
+    }
+
+     /**
+     * A convenience method to send the FTP MFMT command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param host  The host owning the port.
-     * @param port  The new port.
+     * @param pathname The pathname for which mtime is to be changed
+     * @param timeval Timestamp in <code>YYYYMMDDhhmmss</code> format
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -994,42 +1002,18 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      * @since 2.2
-     */
-    public int eprt(final InetAddress host, final int port) throws IOException
+     * @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a>
+     **/
+    public int mfmt(final String pathname, final String timeval) throws IOException
     {
-        final int num;
-        final StringBuilder info = new StringBuilder();
-        String h;
-
-        // If IPv6, trim the zone index
-        h = host.getHostAddress();
-        num = h.indexOf('%');
-        if (num > 0) {
-            h = h.substring(0, num);
-        }
-
-        info.append("|");
-
-        if (host instanceof Inet4Address) {
-            info.append("1");
-        } else if (host instanceof Inet6Address) {
-            info.append("2");
-        }
-        info.append("|");
-        info.append(h);
-        info.append("|");
-        info.append(port);
-        info.append("|");
-
-        return sendCommand(FTPCmd.EPRT, info.toString());
+        return sendCommand(FTPCmd.MFMT, timeval + " " + pathname);
     }
 
     /**
-     * A convenience method to send the FTP PASV command to the server,
-     * receive the reply, and return the reply code.  Remember, it's up
-     * to you to interpret the reply string containing the host/port
-     * information.
+     * A convenience method to send the FTP MKD command to the server,
+     * receive the reply, and return the reply code.
      *
+     * @param pathname The pathname of the new directory to create.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1039,16 +1023,18 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int pasv() throws IOException
+    public int mkd(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.PASV);
+        return sendCommand(FTPCmd.MKD, pathname);
     }
 
-     /**
-     * A convenience method to send the FTP EPSV command to the server,
-     * receive the reply, and return the reply code.  Remember, it's up
-     * to you to interpret the reply string containing the host/port
-     * information.
+
+    /**
+     * A convenience method to send the FTP MLSD command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
@@ -1058,22 +1044,23 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 2.2
+     * @since 3.0
      */
-    public int epsv() throws IOException
+    public int mlsd() throws IOException
     {
-        return sendCommand(FTPCmd.EPSV);
+        return sendCommand(FTPCmd.MLSD);
     }
 
     /**
-     * A convenience method to send the FTP TYPE command for text files
-     * to the server, receive the reply, and return the reply code.
-     * @param fileType  The type of the file (one of the <code>FILE_TYPE</code>
-     *              constants).
-     * @param formatOrByteSize  The format of the file (one of the
-     *              <code>_FORMAT</code> constants.  In the case of
-     *              <code>LOCAL_FILE_TYPE</code>, the byte size.
-     * @return The reply code received from the server.
+     * A convenience method to send the FTP MLSD command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
+     *
+     * @param path the path to report on
+     * @return The reply code received from the server,
+     * may be {@code null} in which case the command is sent with no parameters
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1081,29 +1068,20 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
+     * @since 3.0
      */
-    public int type(final int fileType, final int formatOrByteSize) throws IOException
+    public int mlsd(final String path) throws IOException
     {
-        final StringBuilder arg = new StringBuilder();
-
-        arg.append(modes.charAt(fileType));
-        arg.append(' ');
-        if (fileType == LOCAL_FILE_TYPE) {
-            arg.append(formatOrByteSize);
-        } else {
-            arg.append(modes.charAt(formatOrByteSize));
-        }
-
-        return sendCommand(FTPCmd.TYPE, arg.toString());
+        return sendCommand(FTPCmd.MLSD, path);
     }
 
-
     /**
-     * A convenience method to send the FTP TYPE command to the server,
-     * receive the reply, and return the reply code.
+     * A convenience method to send the FTP MLST command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
-     * @param fileType  The type of the file (one of the <code>FILE_TYPE</code>
-     *              constants).
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1112,20 +1090,23 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
+     * @since 3.0
      */
-    public int type(final int fileType) throws IOException
+    public int mlst() throws IOException
     {
-        return sendCommand(FTPCmd.TYPE,
-                           modes.substring(fileType, fileType + 1));
+        return sendCommand(FTPCmd.MLST);
     }
 
     /**
-     * A convenience method to send the FTP STRU command to the server,
-     * receive the reply, and return the reply code.
+     * A convenience method to send the FTP MLST command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
-     * @param structure  The structure of the file (one of the
-     *         <code>_STRUCTURE</code> constants).
-     * @return The reply code received from the server.
+     * @param path the path to report on
+     * @return The reply code received from the server,
+     * may be {@code null} in which case the command is sent with no parameters
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1133,11 +1114,11 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
+     * @since 3.0
      */
-    public int stru(final int structure) throws IOException
+    public int mlst(final String path) throws IOException
     {
-        return sendCommand(FTPCmd.STRU,
-                           modes.substring(structure, structure + 1));
+        return sendCommand(FTPCmd.MLST, path);
     }
 
     /**
@@ -1162,13 +1143,12 @@ public class FTP extends SocketClient
     }
 
     /**
-     * A convenience method to send the FTP RETR command to the server,
+     * A convenience method to send the FTP NLST command to the server,
      * receive the reply, and return the reply code.  Remember, it is up
      * to you to manage the data connection.  If you don't need this low
      * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
      * , which will handle all low level details for you.
      *
-     * @param pathname  The pathname of the file to retrieve.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1178,20 +1158,20 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int retr(final String pathname) throws IOException
+    public int nlst() throws IOException
     {
-        return sendCommand(FTPCmd.RETR, pathname);
+        return sendCommand(FTPCmd.NLST);
     }
 
     /**
-     * A convenience method to send the FTP STOR command to the server,
+     * A convenience method to send the FTP NLST command to the server,
      * receive the reply, and return the reply code.  Remember, it is up
      * to you to manage the data connection.  If you don't need this low
      * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
      * , which will handle all low level details for you.
      *
-     * @param pathname  The pathname to use for the file when stored at
-     *                  the remote end of the transfer.
+     * @param pathname  The pathname to list,
+     * may be {@code null} in which case the command is sent with no parameters
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1201,17 +1181,14 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int stor(final String pathname) throws IOException
+    public int nlst(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.STOR, pathname);
+        return sendCommand(FTPCmd.NLST, pathname);
     }
 
     /**
-     * A convenience method to send the FTP STOU command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * A convenience method to send the FTP NOOP command to the server,
+     * receive the reply, and return the reply code.
      *
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
@@ -1222,20 +1199,15 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int stou() throws IOException
+    public int noop() throws IOException
     {
-        return sendCommand(FTPCmd.STOU);
+        return sendCommand(FTPCmd.NOOP);
     }
 
     /**
-     * A convenience method to send the FTP STOU command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
-     * @param pathname  The base pathname to use for the file when stored at
-     *                  the remote end of the transfer.  Some FTP servers
-     *                  require this.
+     * A convenience method to send the FTP PASS command to the server,
+     * receive the reply, and return the reply code.
+     * @param password The plain text password of the username being logged into.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1245,20 +1217,17 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int stou(final String pathname) throws IOException
+    public int pass(final String password) throws IOException
     {
-        return sendCommand(FTPCmd.STOU, pathname);
+        return sendCommand(FTPCmd.PASS, password);
     }
 
     /**
-     * A convenience method to send the FTP APPE command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * A convenience method to send the FTP PASV command to the server,
+     * receive the reply, and return the reply code.  Remember, it's up
+     * to you to interpret the reply string containing the host/port
+     * information.
      *
-     * @param pathname  The pathname to use for the file when stored at
-     *                  the remote end of the transfer.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1268,16 +1237,17 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int appe(final String pathname) throws IOException
+    public int pasv() throws IOException
     {
-        return sendCommand(FTPCmd.APPE, pathname);
+        return sendCommand(FTPCmd.PASV);
     }
 
     /**
-     * A convenience method to send the FTP ALLO command to the server,
+     * A convenience method to send the FTP PORT command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param bytes The number of bytes to allocate.
+     * @param host  The host owning the port.
+     * @param port  The new port.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1287,49 +1257,44 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int allo(final int bytes) throws IOException
+    public int port(final InetAddress host, final int port) throws IOException
     {
-        return sendCommand(FTPCmd.ALLO, Integer.toString(bytes));
+        int num;
+        final StringBuilder info = new StringBuilder(24);
+
+        info.append(host.getHostAddress().replace('.', ','));
+        num = port >>> 8;
+        info.append(',');
+        info.append(num);
+        info.append(',');
+        num = port & 0xff;
+        info.append(num);
+
+        return sendCommand(FTPCmd.PORT, info.toString());
     }
 
     /**
-     * A convenience method to send the FTP ALLO command to the server,
+     * A convenience method to send the FTP PWD command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param bytes The number of bytes to allocate.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int allo(final long bytes) throws IOException
-    {
-        return sendCommand(FTPCmd.ALLO, Long.toString(bytes));
-    }
-
-    /**
-     * A convenience method to send the FTP FEAT command to the server, receive the reply,
-     * and return the reply code.
-     * @return The reply code received by the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 2.2
      */
-    public int feat() throws IOException
+    public int pwd() throws IOException
     {
-        return sendCommand(FTPCmd.FEAT);
+        return sendCommand(FTPCmd.PWD);
     }
 
     /**
-     * A convenience method to send the FTP ALLO command to the server,
+     * A convenience method to send the FTP QUIT command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param bytes The number of bytes to allocate.
-     * @param recordSize  The size of a record.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1339,18 +1304,15 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int allo(final int bytes, final int recordSize) throws IOException
+    public int quit() throws IOException
     {
-        return sendCommand(FTPCmd.ALLO, Integer.toString(bytes) + " R " +
-                           Integer.toString(recordSize));
+        return sendCommand(FTPCmd.QUIT);
     }
 
     /**
-     * A convenience method to send the FTP ALLO command to the server,
+     * A convenience method to send the FTP REIN command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param bytes The number of bytes to allocate.
-     * @param recordSize  The size of a record.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1360,12 +1322,12 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int allo(final long bytes, final int recordSize) throws IOException
+    public int rein() throws IOException
     {
-        return sendCommand(FTPCmd.ALLO, Long.toString(bytes) + " R " +
-                           Integer.toString(recordSize));
+        return sendCommand(FTPCmd.REIN);
     }
 
+
     /**
      * A convenience method to send the FTP REST command to the server,
      * receive the reply, and return the reply code.
@@ -1387,25 +1349,33 @@ public class FTP extends SocketClient
 
 
     /**
-     * Sends the MDTM command for the given file.
+     * A convenience method to send the FTP RETR command to the server,
+     * receive the reply, and return the reply code.  Remember, it is up
+     * to you to manage the data connection.  If you don't need this low
+     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
+     * , which will handle all low level details for you.
      *
-     * @param file name of file
-     * @return the status
-     * @throws IOException on error
-     * @since 2.0
-     **/
-    public int mdtm(final String file) throws IOException
+     * @param pathname  The pathname of the file to retrieve.
+     * @return The reply code received from the server.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     */
+    public int retr(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.MDTM, file);
+        return sendCommand(FTPCmd.RETR, pathname);
     }
 
 
     /**
-     * A convenience method to send the FTP MFMT command to the server,
+     * A convenience method to send the FTP RMD command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param pathname The pathname for which mtime is to be changed
-     * @param timeval Timestamp in <code>YYYYMMDDhhmmss</code> format
+     * @param pathname The pathname of the directory to remove.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1414,15 +1384,12 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 2.2
-     * @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a>
-     **/
-    public int mfmt(final String pathname, final String timeval) throws IOException
+     */
+    public int rmd(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.MFMT, timeval + " " + pathname);
+        return sendCommand(FTPCmd.RMD, pathname);
     }
 
-
     /**
      * A convenience method to send the FTP RNFR command to the server,
      * receive the reply, and return the reply code.
@@ -1461,12 +1428,33 @@ public class FTP extends SocketClient
         return sendCommand(FTPCmd.RNTO, pathname);
     }
 
+    private void send(final String message) throws IOException,
+            FTPConnectionClosedException, SocketException {
+        try{
+            _controlOutput_.write(message);
+            _controlOutput_.flush();
+        }
+        catch (final SocketException e)
+        {
+            if (!isConnected())
+            {
+                throw new FTPConnectionClosedException("Connection unexpectedly closed.");
+            }
+            throw e;
+        }
+    }
+
     /**
-     * A convenience method to send the FTP DELE command to the server,
-     * receive the reply, and return the reply code.
+     * Sends an FTP command to the server, waits for a reply and returns the
+     * numerical response code.  After invocation, for more detailed
+     * information, the actual reply text can be accessed by calling
+     * {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.
      *
-     * @param pathname The pathname to delete.
-     * @return The reply code received from the server.
+     * @param command  The FTPCmd enum corresponding to the FTP command
+     *                 to send.
+     * @return The integer value of the FTP reply code returned by the server
+     *         in response to the command.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1474,18 +1462,75 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
+     * @since 3.3
      */
-    public int dele(final String pathname) throws IOException
+    public int sendCommand(final FTPCmd command)  throws IOException{
+        return sendCommand(command, null);
+    }
+
+    /**
+     * Sends an FTP command to the server, waits for a reply and returns the
+     * numerical response code.  After invocation, for more detailed
+     * information, the actual reply text can be accessed by calling
+     * {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.
+     *
+     * @param command  The FTPCmd enum corresponding to the FTP command
+     *                 to send.
+     * @param args The arguments to the FTP command.  If this parameter is
+     *             set to null, then the command is sent with no argument.
+     * @return The integer value of the FTP reply code returned by the server
+     *         in response to the command.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     * @since 3.3
+     */
+    public int sendCommand(final FTPCmd command, final String args)  throws IOException{
+        return sendCommand(command.getCommand(), args);
+    }
+
+    /**
+     * Sends an FTP command with no arguments to the server, waits for a
+     * reply and returns the numerical response code.  After invocation, for
+     * more detailed information, the actual reply text can be accessed by
+     * calling {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.
+     *
+     * @param command  The FTPCommand constant corresponding to the FTP command
+     *                 to send.
+     * @return The integer value of the FTP reply code returned by the server
+     *         in response to the command.
+     * @throws FTPConnectionClosedException
+     *      If the FTP server prematurely closes the connection as a result
+     *      of the client being idle or some other reason causing the server
+     *      to send FTP reply code 421.  This exception may be caught either
+     *      as an IOException or independently as itself.
+     * @throws IOException  If an I/O error occurs while either sending the
+     *      command or receiving the server reply.
+     */
+    public int sendCommand(final int command) throws IOException
     {
-        return sendCommand(FTPCmd.DELE, pathname);
+        return sendCommand(command, null);
     }
 
     /**
-     * A convenience method to send the FTP RMD command to the server,
-     * receive the reply, and return the reply code.
+     * Sends an FTP command to the server, waits for a reply and returns the
+     * numerical response code.  After invocation, for more detailed
+     * information, the actual reply text can be accessed by calling
+     * {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.
      *
-     * @param pathname The pathname of the directory to remove.
-     * @return The reply code received from the server.
+     * @param command  The FTPCommand constant corresponding to the FTP command
+     *                 to send.
+     * @param args The arguments to the FTP command.  If this parameter is
+     *             set to null, then the command is sent with no argument.
+     * @return The integer value of the FTP reply code returned by the server
+     *         in response to the command.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1493,18 +1538,24 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
+     * @deprecated (3.3) Use {@link #sendCommand(FTPCmd, String)} instead
      */
-    public int rmd(final String pathname) throws IOException
+    @Deprecated
+    public int sendCommand(final int command, final String args) throws IOException
     {
-        return sendCommand(FTPCmd.RMD, pathname);
+        return sendCommand(FTPCommand.getCommand(command), args);
     }
 
     /**
-     * A convenience method to send the FTP MKD command to the server,
-     * receive the reply, and return the reply code.
+     * Sends an FTP command with no arguments to the server, waits for a
+     * reply and returns the numerical response code.  After invocation, for
+     * more detailed information, the actual reply text can be accessed by
+     * calling {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.
      *
-     * @param pathname The pathname of the new directory to create.
-     * @return The reply code received from the server.
+     * @param command  The text representation of the  FTP command to send.
+     * @return The integer value of the FTP reply code returned by the server
+     *         in response to the command.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1513,16 +1564,23 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int mkd(final String pathname) throws IOException
+    public int sendCommand(final String command) throws IOException
     {
-        return sendCommand(FTPCmd.MKD, pathname);
+        return sendCommand(command, null);
     }
 
     /**
-     * A convenience method to send the FTP PWD command to the server,
-     * receive the reply, and return the reply code.
+     * Sends an FTP command to the server, waits for a reply and returns the
+     * numerical response code.  After invocation, for more detailed
+     * information, the actual reply text can be accessed by calling
+     * {@link #getReplyString  getReplyString } or
+     * {@link #getReplyStrings  getReplyStrings }.
      *
-     * @return The reply code received from the server.
+     * @param command  The text representation of the  FTP command to send.
+     * @param args The arguments to the FTP command.  If this parameter is
+     *             set to null, then the command is sent with no argument.
+     * @return The integer value of the FTP reply code returned by the server
+     *         in response to the command.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1531,18 +1589,65 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int pwd() throws IOException
+    public int sendCommand(final String command, final String args) throws IOException
     {
-        return sendCommand(FTPCmd.PWD);
+        if (_controlOutput_ == null) {
+            throw new IOException("Connection is not open");
+        }
+
+        final String message = buildMessage(command, args);
+
+        send(message);
+
+        fireCommandSent(command, message);
+
+        return getReply();
     }
 
     /**
-     * A convenience method to send the FTP LIST command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * Saves the character encoding to be used by the FTP control connection.
+     * Some FTP servers require that commands be issued in a non-ASCII
+     * encoding like UTF-8 so that file names with multi-byte character
+     * representations (e.g, Big 8) can be specified.
+     * <p>
+     * Please note that this has to be set before the connection is established.
+     *
+     * @param encoding The new character encoding for the control connection.
+     */
+    public void setControlEncoding(final String encoding) {
+        _controlEncoding = encoding;
+    }
+
+    /**
+     * Set strict multiline parsing.
+     * @param strictMultilineParsing the setting
+     * @since 2.0
+     */
+    public void setStrictMultilineParsing(final boolean strictMultilineParsing) {
+        this.strictMultilineParsing = strictMultilineParsing;
+    }
+
+    /**
+     * 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(final boolean strictReplyParsing) {
+        this.strictReplyParsing = strictReplyParsing;
+    }
+
+    /**
+     * A convenience method to send the FTP SITE command to the server,
+     * receive the reply, and return the reply code.
      *
+     * @param parameters  The site parameters to send.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1552,20 +1657,16 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int list() throws IOException
+    public int site(final String parameters) throws IOException
     {
-        return sendCommand(FTPCmd.LIST);
+        return sendCommand(FTPCmd.SITE, parameters);
     }
 
     /**
-     * A convenience method to send the FTP LIST command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * A convenience method to send the FTP SIZE command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param pathname  The pathname to list,
-     * may be {@code null} in which case the command is sent with no parameters
+     * @param parameters  The site parameters to send.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1574,19 +1675,18 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
+     * @since 3.7
      */
-    public int list(final String pathname) throws IOException
+    public int size(final String parameters) throws IOException
     {
-        return sendCommand(FTPCmd.LIST, pathname);
+        return sendCommand(FTPCmd.SIZE, parameters);
     }
 
     /**
-     * A convenience method to send the FTP MLSD command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * A convenience method to send the FTP SMNT command to the server,
+     * receive the reply, and return the reply code.
      *
+     * @param dir  The directory name.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1595,23 +1695,17 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.0
      */
-    public int mlsd() throws IOException
+    public int smnt(final String dir) throws IOException
     {
-        return sendCommand(FTPCmd.MLSD);
+        return sendCommand(FTPCmd.SMNT, dir);
     }
 
     /**
-     * A convenience method to send the FTP MLSD command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * A convenience method to send the FTP STAT command to the server,
+     * receive the reply, and return the reply code.
      *
-     * @param path the path to report on
-     * @return The reply code received from the server,
-     * may be {@code null} in which case the command is sent with no parameters
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1619,20 +1713,17 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.0
      */
-    public int mlsd(final String path) throws IOException
+    public int stat() throws IOException
     {
-        return sendCommand(FTPCmd.MLSD, path);
+        return sendCommand(FTPCmd.STAT);
     }
 
     /**
-     * A convenience method to send the FTP MLST command to the server,
-     * receive the reply, and return the reply code.  Remember, it is up
-     * to you to manage the data connection.  If you don't need this low
-     * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
-     * , which will handle all low level details for you.
+     * A convenience method to send the FTP STAT command to the server,
+     * receive the reply, and return the reply code.
      *
+     * @param pathname  A pathname to list.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1641,23 +1732,22 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.0
      */
-    public int mlst() throws IOException
+    public int stat(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.MLST);
+        return sendCommand(FTPCmd.STAT, pathname);
     }
 
     /**
-     * A convenience method to send the FTP MLST command to the server,
+     * A convenience method to send the FTP STOR command to the server,
      * receive the reply, and return the reply code.  Remember, it is up
      * to you to manage the data connection.  If you don't need this low
      * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
      * , which will handle all low level details for you.
      *
-     * @param path the path to report on
-     * @return The reply code received from the server,
-     * may be {@code null} in which case the command is sent with no parameters
+     * @param pathname  The pathname to use for the file when stored at
+     *                  the remote end of the transfer.
+     * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
      *      of the client being idle or some other reason causing the server
@@ -1665,15 +1755,14 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.0
      */
-    public int mlst(final String path) throws IOException
+    public int stor(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.MLST, path);
+        return sendCommand(FTPCmd.STOR, pathname);
     }
 
     /**
-     * A convenience method to send the FTP NLST command to the server,
+     * A convenience method to send the FTP STOU command to the server,
      * receive the reply, and return the reply code.  Remember, it is up
      * to you to manage the data connection.  If you don't need this low
      * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
@@ -1688,20 +1777,20 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int nlst() throws IOException
+    public int stou() throws IOException
     {
-        return sendCommand(FTPCmd.NLST);
+        return sendCommand(FTPCmd.STOU);
     }
 
     /**
-     * A convenience method to send the FTP NLST command to the server,
+     * A convenience method to send the FTP STOU command to the server,
      * receive the reply, and return the reply code.  Remember, it is up
      * to you to manage the data connection.  If you don't need this low
      * level of access, use {@link org.apache.commons.net.ftp.FTPClient}
      * , which will handle all low level details for you.
-     *
-     * @param pathname  The pathname to list,
-     * may be {@code null} in which case the command is sent with no parameters
+     * @param pathname  The base pathname to use for the file when stored at
+     *                  the remote end of the transfer.  Some FTP servers
+     *                  require this.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1711,35 +1800,22 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int nlst(final String pathname) throws IOException
+    public int stou(final String pathname) throws IOException
     {
-        return sendCommand(FTPCmd.NLST, pathname);
+        return sendCommand(FTPCmd.STOU, pathname);
     }
 
-    /**
-     * A convenience method to send the FTP SITE command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @param parameters  The site parameters to send.
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int site(final String parameters) throws IOException
-    {
-        return sendCommand(FTPCmd.SITE, parameters);
+    // The RFC-compliant multiline termination check
+    private boolean strictCheck(final String line, final String code) {
+        return !(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' ');
     }
 
     /**
-     * A convenience method to send the FTP SIZE command to the server,
+     * A convenience method to send the FTP STRU command to the server,
      * receive the reply, and return the reply code.
      *
-     * @param parameters  The site parameters to send.
+     * @param structure  The structure of the file (one of the
+     *         <code>_STRUCTURE</code> constants).
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1748,11 +1824,11 @@ public class FTP extends SocketClient
      *      as an IOException or independently as itself.
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
-     * @since 3.7
      */
-    public int size(final String parameters) throws IOException
+    public int stru(final int structure) throws IOException
     {
-        return sendCommand(FTPCmd.SIZE, parameters);
+        return sendCommand(FTPCmd.STRU,
+                           modes.substring(structure, structure + 1));
     }
 
     /**
@@ -1774,9 +1850,11 @@ public class FTP extends SocketClient
     }
 
     /**
-     * A convenience method to send the FTP STAT command to the server,
+     * A convenience method to send the FTP TYPE command to the server,
      * receive the reply, and return the reply code.
      *
+     * @param fileType  The type of the file (one of the <code>FILE_TYPE</code>
+     *              constants).
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1786,16 +1864,20 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int stat() throws IOException
+    public int type(final int fileType) throws IOException
     {
-        return sendCommand(FTPCmd.STAT);
+        return sendCommand(FTPCmd.TYPE,
+                           modes.substring(fileType, fileType + 1));
     }
 
     /**
-     * A convenience method to send the FTP STAT command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @param pathname  A pathname to list.
+     * A convenience method to send the FTP TYPE command for text files
+     * to the server, receive the reply, and return the reply code.
+     * @param fileType  The type of the file (one of the <code>FILE_TYPE</code>
+     *              constants).
+     * @param formatOrByteSize  The format of the file (one of the
+     *              <code>_FORMAT</code> constants.  In the case of
+     *              <code>LOCAL_FILE_TYPE</code>, the byte size.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1805,52 +1887,26 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int stat(final String pathname) throws IOException
+    public int type(final int fileType, final int formatOrByteSize) throws IOException
     {
-        return sendCommand(FTPCmd.STAT, pathname);
-    }
+        final StringBuilder arg = new StringBuilder();
 
-    /**
-     * A convenience method to send the FTP HELP command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int help() throws IOException
-    {
-        return sendCommand(FTPCmd.HELP);
-    }
+        arg.append(modes.charAt(fileType));
+        arg.append(' ');
+        if (fileType == LOCAL_FILE_TYPE) {
+            arg.append(formatOrByteSize);
+        } else {
+            arg.append(modes.charAt(formatOrByteSize));
+        }
 
-    /**
-     * A convenience method to send the FTP HELP command to the server,
-     * receive the reply, and return the reply code.
-     *
-     * @param command  The command name on which to request help.
-     * @return The reply code received from the server.
-     * @throws FTPConnectionClosedException
-     *      If the FTP server prematurely closes the connection as a result
-     *      of the client being idle or some other reason causing the server
-     *      to send FTP reply code 421.  This exception may be caught either
-     *      as an IOException or independently as itself.
-     * @throws IOException  If an I/O error occurs while either sending the
-     *      command or receiving the server reply.
-     */
-    public int help(final String command) throws IOException
-    {
-        return sendCommand(FTPCmd.HELP, command);
+        return sendCommand(FTPCmd.TYPE, arg.toString());
     }
 
     /**
-     * A convenience method to send the FTP NOOP command to the server,
+     * A convenience method to send the FTP USER command to the server,
      * receive the reply, and return the reply code.
      *
+     * @param username  The username to login under.
      * @return The reply code received from the server.
      * @throws FTPConnectionClosedException
      *      If the FTP server prematurely closes the connection as a result
@@ -1860,64 +1916,8 @@ public class FTP extends SocketClient
      * @throws IOException  If an I/O error occurs while either sending the
      *      command or receiving the server reply.
      */
-    public int noop() throws IOException
+    public int user(final String username) throws IOException
     {
-        return sendCommand(FTPCmd.NOOP);
-    }
-
-    /**
-     * Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2.
-     * @return True if strict, false if lenient
-     * @since 2.0
-     */
-    public boolean isStrictMultilineParsing() {
-        return strictMultilineParsing;
-    }
-
-    /**
-     * Set strict multiline parsing.
-     * @param strictMultilineParsing the setting
-     * @since 2.0
-     */
-    public void setStrictMultilineParsing(final boolean strictMultilineParsing) {
-        this.strictMultilineParsing = strictMultilineParsing;
-    }
-
-    /**
-     * 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(final boolean strictReplyParsing) {
-        this.strictReplyParsing = strictReplyParsing;
-    }
-
-    /**
-     * Provide command support to super-class
-     */
-    @Override
-    protected ProtocolCommandSupport getCommandSupport() {
-        return _commandSupport_;
+        return sendCommand(FTPCmd.USER, username);
     }
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java b/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java
index eaf97a4..8790167 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPClientConfig.java
@@ -219,17 +219,142 @@ public class FTPClientConfig
     // Full string is "MACOS Peter's Server"; the substring below should be enough
     public static final String SYST_MACOS_PETER  = "MACOS PETER"; // NET-436
 
+    private static final Map<String, Object> LANGUAGE_CODE_MAP = new TreeMap<>();
+    static {
+
+        // if there are other commonly used month name encodings which
+        // correspond to particular locales, please add them here.
+
+
+
+        // many locales code short names for months as all three letters
+        // these we handle simply.
+        LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH);
+        LANGUAGE_CODE_MAP.put("de",Locale.GERMAN);
+        LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN);
+        LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish
+        LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese
+        LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish
+        LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish
+        LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian
+        LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch
+        LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian
+        LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian
+        LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian
+        LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak
+        LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian
+
+
+        // some don't
+        LANGUAGE_CODE_MAP.put("fr",
+                "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c");  //french
+
+    }
+    /**
+     * Returns a DateFormatSymbols object configured with short month names
+     * as in the supplied string
+     * @param shortmonths This  should be as described in
+     *  {@link  #setShortMonthNames(String)  shortMonthNames}
+     * @return a DateFormatSymbols object configured with short month names
+     * as in the supplied string
+     */
+    public static DateFormatSymbols getDateFormatSymbols(final String shortmonths)
+    {
+        final String[] months = splitShortMonthString(shortmonths);
+        final DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
+        dfs.setShortMonths(months);
+        return dfs;
+    }
+    /**
+     * Returns a Collection of all the language codes currently supported
+     * by this class. See {@link  #setServerLanguageCode(String)  serverLanguageCode}
+     * for a functional descrption of language codes within this system.
+     *
+     * @return a Collection of all the language codes currently supported
+     * by this class
+     */
+    public static Collection<String> getSupportedLanguageCodes() {
+        return LANGUAGE_CODE_MAP.keySet();
+    }
+    /**
+     * Looks up the supplied language code in the internally maintained table of
+     * language codes.  Returns a DateFormatSymbols object configured with
+     * short month names corresponding to the code.  If there is no corresponding
+     * entry in the table, the object returned will be that for
+     * <code>Locale.US</code>
+     * @param languageCode See {@link  #setServerLanguageCode(String)  serverLanguageCode}
+     * @return a DateFormatSymbols object configured with short month names
+     * corresponding to the supplied code, or with month names for
+     * <code>Locale.US</code> if there is no corresponding entry in the internal
+     * table.
+     */
+    public static DateFormatSymbols lookupDateFormatSymbols(final String languageCode)
+    {
+        final Object lang = LANGUAGE_CODE_MAP.get(languageCode);
+        if (lang != null) {
+            if (lang instanceof Locale) {
+                return new DateFormatSymbols((Locale) lang);
+            } else if (lang instanceof String){
+                return getDateFormatSymbols((String) lang);
+            }
+        }
+        return new DateFormatSymbols(Locale.US);
+    }
+    private static String[] splitShortMonthString(final String shortmonths) {
+        final StringTokenizer st = new StringTokenizer(shortmonths, "|");
+        final int monthcnt = st.countTokens();
+        if (12 != monthcnt) {
+            throw new IllegalArgumentException(
+                    "expecting a pipe-delimited string containing 12 tokens");
+        }
+        final String[] months = new String[13];
+        int pos = 0;
+        while(st.hasMoreTokens()) {
+            months[pos++] = st.nextToken();
+        }
+        months[pos]="";
+        return months;
+    }
     private final String serverSystemKey;
     private String defaultDateFormatStr;
+
+
     private String recentDateFormatStr;
+
     private boolean lenientFutureDates = true; // NET-407
+
     private String serverLanguageCode;
+
     private String shortMonthNames;
+
     private String serverTimeZoneId;
+
     private boolean saveUnparseableEntries;
 
+    /**
+     * Convenience constructor mainly for use in testing.
+     * Constructs a UNIX configuration.
+     */
+    public FTPClientConfig() {
+        this(SYST_UNIX);
+    }
 
     /**
+     * Copy constructor
+     * @param config source
+     * @since 3.6
+     */
+    public FTPClientConfig(final FTPClientConfig config) {
+        this.serverSystemKey = config.serverSystemKey;
+        this.defaultDateFormatStr = config.defaultDateFormatStr;
+        this.lenientFutureDates = config.lenientFutureDates;
+        this.recentDateFormatStr = config.recentDateFormatStr;
+        this.saveUnparseableEntries = config.saveUnparseableEntries;
+        this.serverLanguageCode = config.serverLanguageCode;
+        this.serverTimeZoneId = config.serverTimeZoneId;
+        this.shortMonthNames = config.shortMonthNames;
+    }
+    /**
      * The main constructor for an FTPClientConfig object
      * @param systemKey key representing system type of the  server being
      * connected to. See {@link #getServerSystemKey() serverSystemKey}
@@ -241,12 +366,16 @@ public class FTPClientConfig
         this.serverSystemKey = systemKey;
     }
 
-    /**
-     * Convenience constructor mainly for use in testing.
-     * Constructs a UNIX configuration.
-     */
-    public FTPClientConfig() {
-        this(SYST_UNIX);
+    // Copy constructor, intended for use by FTPClient only
+    FTPClientConfig(final String systemKey, final FTPClientConfig config) {
+        this.serverSystemKey = systemKey;
+        this.defaultDateFormatStr = config.defaultDateFormatStr;
+        this.lenientFutureDates = config.lenientFutureDates;
+        this.recentDateFormatStr = config.recentDateFormatStr;
+        this.saveUnparseableEntries = config.saveUnparseableEntries;
+        this.serverLanguageCode = config.serverLanguageCode;
+        this.serverTimeZoneId = config.serverTimeZoneId;
+        this.shortMonthNames = config.shortMonthNames;
     }
 
     /**
@@ -339,67 +468,34 @@ public class FTPClientConfig
         this.serverTimeZoneId = serverTimeZoneId;
     }
 
-    // Copy constructor, intended for use by FTPClient only
-    FTPClientConfig(final String systemKey, final FTPClientConfig config) {
-        this.serverSystemKey = systemKey;
-        this.defaultDateFormatStr = config.defaultDateFormatStr;
-        this.lenientFutureDates = config.lenientFutureDates;
-        this.recentDateFormatStr = config.recentDateFormatStr;
-        this.saveUnparseableEntries = config.saveUnparseableEntries;
-        this.serverLanguageCode = config.serverLanguageCode;
-        this.serverTimeZoneId = config.serverTimeZoneId;
-        this.shortMonthNames = config.shortMonthNames;
-    }
-
     /**
-     * Copy constructor
-     * @param config source
-     * @since 3.6
+     * getter for the {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
+     * property.
+     * @return Returns the defaultDateFormatStr property.
      */
-    public FTPClientConfig(final FTPClientConfig config) {
-        this.serverSystemKey = config.serverSystemKey;
-        this.defaultDateFormatStr = config.defaultDateFormatStr;
-        this.lenientFutureDates = config.lenientFutureDates;
-        this.recentDateFormatStr = config.recentDateFormatStr;
-        this.saveUnparseableEntries = config.saveUnparseableEntries;
-        this.serverLanguageCode = config.serverLanguageCode;
-        this.serverTimeZoneId = config.serverTimeZoneId;
-        this.shortMonthNames = config.shortMonthNames;
+    public String getDefaultDateFormatStr() {
+        return defaultDateFormatStr;
     }
 
-    private static final Map<String, Object> LANGUAGE_CODE_MAP = new TreeMap<>();
-    static {
-
-        // if there are other commonly used month name encodings which
-        // correspond to particular locales, please add them here.
-
-
-
-        // many locales code short names for months as all three letters
-        // these we handle simply.
-        LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH);
-        LANGUAGE_CODE_MAP.put("de",Locale.GERMAN);
-        LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN);
-        LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish
-        LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese
-        LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish
-        LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish
-        LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian
-        LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch
-        LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian
-        LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian
-        LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian
-        LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak
-        LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian
-
-
-        // some don't
-        LANGUAGE_CODE_MAP.put("fr",
-                "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c");  //french
+    /**
+     * getter for the {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property.
+     * @return Returns the recentDateFormatStr property.
+     */
 
+    public String getRecentDateFormatStr() {
+        return recentDateFormatStr;
     }
 
     /**
+     * <p>
+     * getter for the {@link  #setServerLanguageCode(String)  serverLanguageCode} property.
+     * </p>
+     * @return Returns the serverLanguageCode property.
+     */
+    public String getServerLanguageCode() {
+        return serverLanguageCode;
+    }
+    /**
      * Getter for the serverSystemKey property.  This property
      * specifies the general type of server to which the client connects.
      * Should be either one of the <code>FTPClientConfig.SYST_*</code> codes
@@ -413,24 +509,6 @@ public class FTPClientConfig
     }
 
     /**
-     * getter for the {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
-     * property.
-     * @return Returns the defaultDateFormatStr property.
-     */
-    public String getDefaultDateFormatStr() {
-        return defaultDateFormatStr;
-    }
-
-    /**
-     * getter for the {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property.
-     * @return Returns the recentDateFormatStr property.
-     */
-
-    public String getRecentDateFormatStr() {
-        return recentDateFormatStr;
-    }
-
-    /**
      * getter for the {@link  #setServerTimeZoneId(String)  serverTimeZoneId} property.
      * @return Returns the serverTimeZoneId property.
      */
@@ -448,15 +526,15 @@ public class FTPClientConfig
     public String getShortMonthNames() {
         return shortMonthNames;
     }
-
     /**
+     * @return true if list parsing should return FTPFile entries even for unparseable response lines
      * <p>
-     * getter for the {@link  #setServerLanguageCode(String)  serverLanguageCode} property.
-     * </p>
-     * @return Returns the serverLanguageCode property.
+     * If true, the FTPFile for any unparseable entries will contain only the unparsed entry
+     * {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false}
+     * @since 3.4
      */
-    public String getServerLanguageCode() {
-        return serverLanguageCode;
+    public boolean getUnparseableEntries() {
+        return this.saveUnparseableEntries;
     }
 
     /**
@@ -469,6 +547,7 @@ public class FTPClientConfig
     public boolean isLenientFutureDates() {
         return lenientFutureDates;
     }
+
     /**
      * <p>
      * setter for the defaultDateFormatStr property.  This property
@@ -489,27 +568,6 @@ public class FTPClientConfig
 
     /**
      * <p>
-     * setter for the recentDateFormatStr property.  This property
-     * specifies a secondary date format that will be used by a parser
-     * configured by this configuration to parse file timestamps, typically
-     * those less than a year old.  If this is  not specified, such a parser
-     * will not attempt to parse using an alternate format.
-     * </p>
-     * <p>
-     * This is used primarily in unix-based systems.
-     * </p>
-     * <p>
-     * This should be in the format described for
-     * <code>java.text.SimpleDateFormat</code>.
-     * </p>
-     * @param recentDateFormatStr The recentDateFormatStr to set.
-     */
-    public void setRecentDateFormatStr(final String recentDateFormatStr) {
-        this.recentDateFormatStr = recentDateFormatStr;
-    }
-
-    /**
-     * <p>
      * setter for the lenientFutureDates property.  This boolean property
      * (default: true) only has meaning when a
      * {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property
@@ -531,43 +589,26 @@ public class FTPClientConfig
     public void setLenientFutureDates(final boolean lenientFutureDates) {
         this.lenientFutureDates = lenientFutureDates;
     }
+
     /**
      * <p>
-     * setter for the serverTimeZoneId property.  This property
-     * allows a time zone to be specified corresponding to that known to be
-     * used by an FTP server in file listings.  This might be particularly
-     * useful to clients such as Ant that try to use these timestamps for
-     * dependency checking.
-     * </p><p>
-     * This should be one of the identifiers used by
-     * <code>java.util.TimeZone</code> to refer to time zones, for example,
-     * <code>America/Chicago</code> or <code>Asia/Rangoon</code>.
+     * setter for the recentDateFormatStr property.  This property
+     * specifies a secondary date format that will be used by a parser
+     * configured by this configuration to parse file timestamps, typically
+     * those less than a year old.  If this is  not specified, such a parser
+     * will not attempt to parse using an alternate format.
      * </p>
-     * @param serverTimeZoneId The serverTimeZoneId to set.
-     */
-    public void setServerTimeZoneId(final String serverTimeZoneId) {
-        this.serverTimeZoneId = serverTimeZoneId;
-    }
-
-    /**
      * <p>
-     * setter for the shortMonthNames property.
-     * This property allows the user to specify a set of month names
-     * used by the server that is different from those that may be
-     * specified using the {@link  #setServerLanguageCode(String)  serverLanguageCode}
-     * property.
-     * </p><p>
-     * This should be a string containing twelve strings each composed of
-     * three characters, delimited by pipe (|) characters.  Currently,
-     * only 8-bit ASCII characters are known to be supported.  For example,
-     * a set of month names used by a hypothetical Icelandic FTP server might
-     * conceivably be specified as
-     * <code>"jan|feb|mar|apr|ma&#xED;|j&#xFA;n|j&#xFA;l|&#xE1;g&#xFA;|sep|okt|n&#xF3;v|des"</code>.
+     * This is used primarily in unix-based systems.
      * </p>
-     * @param shortMonthNames The value to set to the shortMonthNames property.
+     * <p>
+     * This should be in the format described for
+     * <code>java.text.SimpleDateFormat</code>.
+     * </p>
+     * @param recentDateFormatStr The recentDateFormatStr to set.
      */
-    public void setShortMonthNames(final String shortMonthNames) {
-        this.shortMonthNames = shortMonthNames;
+    public void setRecentDateFormatStr(final String recentDateFormatStr) {
+        this.recentDateFormatStr = recentDateFormatStr;
     }
 
     /**
@@ -615,72 +656,42 @@ public class FTPClientConfig
     }
 
     /**
-     * Looks up the supplied language code in the internally maintained table of
-     * language codes.  Returns a DateFormatSymbols object configured with
-     * short month names corresponding to the code.  If there is no corresponding
-     * entry in the table, the object returned will be that for
-     * <code>Locale.US</code>
-     * @param languageCode See {@link  #setServerLanguageCode(String)  serverLanguageCode}
-     * @return a DateFormatSymbols object configured with short month names
-     * corresponding to the supplied code, or with month names for
-     * <code>Locale.US</code> if there is no corresponding entry in the internal
-     * table.
-     */
-    public static DateFormatSymbols lookupDateFormatSymbols(final String languageCode)
-    {
-        final Object lang = LANGUAGE_CODE_MAP.get(languageCode);
-        if (lang != null) {
-            if (lang instanceof Locale) {
-                return new DateFormatSymbols((Locale) lang);
-            } else if (lang instanceof String){
-                return getDateFormatSymbols((String) lang);
-            }
-        }
-        return new DateFormatSymbols(Locale.US);
-    }
-
-    /**
-     * Returns a DateFormatSymbols object configured with short month names
-     * as in the supplied string
-     * @param shortmonths This  should be as described in
-     *  {@link  #setShortMonthNames(String)  shortMonthNames}
-     * @return a DateFormatSymbols object configured with short month names
-     * as in the supplied string
+     * <p>
+     * setter for the serverTimeZoneId property.  This property
+     * allows a time zone to be specified corresponding to that known to be
+     * used by an FTP server in file listings.  This might be particularly
+     * useful to clients such as Ant that try to use these timestamps for
+     * dependency checking.
+     * </p><p>
+     * This should be one of the identifiers used by
+     * <code>java.util.TimeZone</code> to refer to time zones, for example,
+     * <code>America/Chicago</code> or <code>Asia/Rangoon</code>.
+     * </p>
+     * @param serverTimeZoneId The serverTimeZoneId to set.
      */
-    public static DateFormatSymbols getDateFormatSymbols(final String shortmonths)
-    {
-        final String[] months = splitShortMonthString(shortmonths);
-        final DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
-        dfs.setShortMonths(months);
-        return dfs;
-    }
-
-    private static String[] splitShortMonthString(final String shortmonths) {
-        final StringTokenizer st = new StringTokenizer(shortmonths, "|");
-        final int monthcnt = st.countTokens();
-        if (12 != monthcnt) {
-            throw new IllegalArgumentException(
-                    "expecting a pipe-delimited string containing 12 tokens");
-        }
-        final String[] months = new String[13];
-        int pos = 0;
-        while(st.hasMoreTokens()) {
-            months[pos++] = st.nextToken();
-        }
-        months[pos]="";
-        return months;
+    public void setServerTimeZoneId(final String serverTimeZoneId) {
+        this.serverTimeZoneId = serverTimeZoneId;
     }
 
     /**
-     * Returns a Collection of all the language codes currently supported
-     * by this class. See {@link  #setServerLanguageCode(String)  serverLanguageCode}
-     * for a functional descrption of language codes within this system.
-     *
-     * @return a Collection of all the language codes currently supported
-     * by this class
+     * <p>
+     * setter for the shortMonthNames property.
+     * This property allows the user to specify a set of month names
+     * used by the server that is different from those that may be
+     * specified using the {@link  #setServerLanguageCode(String)  serverLanguageCode}
+     * property.
+     * </p><p>
+     * This should be a string containing twelve strings each composed of
+     * three characters, delimited by pipe (|) characters.  Currently,
+     * only 8-bit ASCII characters are known to be supported.  For example,
+     * a set of month names used by a hypothetical Icelandic FTP server might
+     * conceivably be specified as
+     * <code>"jan|feb|mar|apr|ma&#xED;|j&#xFA;n|j&#xFA;l|&#xE1;g&#xFA;|sep|okt|n&#xF3;v|des"</code>.
+     * </p>
+     * @param shortMonthNames The value to set to the shortMonthNames property.
      */
-    public static Collection<String> getSupportedLanguageCodes() {
-        return LANGUAGE_CODE_MAP.keySet();
+    public void setShortMonthNames(final String shortMonthNames) {
+        this.shortMonthNames = shortMonthNames;
     }
 
     /**
@@ -695,15 +706,4 @@ public class FTPClientConfig
         this.saveUnparseableEntries = saveUnparseable;
     }
 
-    /**
-     * @return true if list parsing should return FTPFile entries even for unparseable response lines
-     * <p>
-     * If true, the FTPFile for any unparseable entries will contain only the unparsed entry
-     * {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false}
-     * @since 3.4
-     */
-    public boolean getUnparseableEntries() {
-        return this.saveUnparseableEntries;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPCommand.java b/src/main/java/org/apache/commons/net/ftp/FTPCommand.java
index 0b69b92..528f679 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPCommand.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPCommand.java
@@ -134,10 +134,6 @@ public final class FTPCommand
     /** @since 2.2 */
     public static final int SET_MOD_TIME = MFMT;
 
-    // Cannot be instantiated
-    private FTPCommand()
-    {}
-
     private static final String[] _commands = {
                                           "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT",
                                           "PASV", "TYPE", "STRU", "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO",
@@ -145,17 +141,17 @@ public final class FTPCommand
                                           "NLST", "SITE", "SYST", "STAT", "HELP", "NOOP", "MDTM", "FEAT", "MFMT",
                                           "EPSV", "EPRT", "MLSD", "MLST" };
 
-
-
     // default access needed for Unit test
-    static void checkArray(){
-        final int expectedLength = LAST+1;
+    static void checkArray() {
+        final int expectedLength = LAST + 1;
         if (_commands.length != expectedLength) {
-            throw new RuntimeException("Incorrect _commands array. Should have length "
-                    +expectedLength+" found "+_commands.length);
+            throw new RuntimeException(
+                "Incorrect _commands array. Should have length " + expectedLength + " found " + _commands.length);
         }
     }
 
+
+
     /**
      * Retrieve the FTP protocol command string corresponding to a specified
      * command code.
@@ -168,4 +164,8 @@ public final class FTPCommand
     {
         return _commands[command];
     }
+
+    // Cannot be instantiated
+    private FTPCommand()
+    {}
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java b/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java
index 8ff1afb..9fa7987 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParser.java
@@ -92,21 +92,6 @@ public interface FTPFileEntryParser
     FTPFile parseFTPEntry(String listEntry);
 
     /**
-     * Reads the next entry using the supplied BufferedReader object up to
-     * whatever delemits one entry from the next.  Implementors must define
-     * this for the particular ftp system being parsed.  In many but not all
-     * cases, this can be defined simply by calling BufferedReader.readLine().
-     *
-     * @param reader The BufferedReader object from which entries are to be
-     * read.
-     *
-     * @return A string representing the next ftp entry or null if none found.
-     * @throws IOException thrown on any IO Error reading from the reader.
-     */
-    String readNextEntry(BufferedReader reader) throws IOException;
-
-
-    /**
      * This method is a hook for those implementors (such as
      * VMSVersioningFTPEntryParser, and possibly others) which need to
      * perform some action upon the FTPFileList after it has been created
@@ -121,4 +106,19 @@ public interface FTPFileEntryParser
     List<String> preParse(List<String> original);
 
 
+    /**
+     * Reads the next entry using the supplied BufferedReader object up to
+     * whatever delemits one entry from the next.  Implementors must define
+     * this for the particular ftp system being parsed.  In many but not all
+     * cases, this can be defined simply by calling BufferedReader.readLine().
+     *
+     * @param reader The BufferedReader object from which entries are to be
+     * read.
+     *
+     * @return A string representing the next ftp entry or null if none found.
+     * @throws IOException thrown on any IO Error reading from the reader.
+     */
+    String readNextEntry(BufferedReader reader) throws IOException;
+
+
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java b/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java
index 4240b95..f76722a 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPFileEntryParserImpl.java
@@ -37,22 +37,6 @@ public abstract class FTPFileEntryParserImpl
     }
 
     /**
-     * Reads the next entry using the supplied BufferedReader object up to
-     * whatever delimits one entry from the next.  This default implementation
-     * simply calls BufferedReader.readLine().
-     *
-     * @param reader The BufferedReader object from which entries are to be
-     * read.
-     *
-     * @return A string representing the next ftp entry or null if none found.
-     * @throws java.io.IOException thrown on any IO Error reading from the reader.
-     */
-    @Override
-    public String readNextEntry(final BufferedReader reader) throws IOException
-    {
-        return reader.readLine();
-    }
-    /**
      * This method is a hook for those implementors (such as
      * VMSVersioningFTPEntryParser, and possibly others) which need to
      * perform some action upon the FTPFileList after it has been created
@@ -68,4 +52,20 @@ public abstract class FTPFileEntryParserImpl
     public List<String> preParse(final List<String> original) {
          return original;
      }
+    /**
+     * Reads the next entry using the supplied BufferedReader object up to
+     * whatever delimits one entry from the next.  This default implementation
+     * simply calls BufferedReader.readLine().
+     *
+     * @param reader The BufferedReader object from which entries are to be
+     * read.
+     *
+     * @return A string representing the next ftp entry or null if none found.
+     * @throws java.io.IOException thrown on any IO Error reading from the reader.
+     */
+    @Override
+    public String readNextEntry(final BufferedReader reader) throws IOException
+    {
+        return reader.readLine();
+    }
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java b/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java
index b51d046..8e1ab42 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java
@@ -40,68 +40,68 @@ import org.apache.commons.net.util.Base64;
  * @since 2.2
  */
 public class FTPHTTPClient extends FTPClient {
+    private static final byte[] CRLF={'\r', '\n'};
     private final String proxyHost;
     private final int proxyPort;
     private final String proxyUsername;
     private final String proxyPassword;
-    private final Charset charset;
 
-    private static final byte[] CRLF={'\r', '\n'};
+    private final Charset charset;
     private final Base64 base64 = new Base64();
 
     private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV)
 
     /**
-     * Create an instance with the specified encoding
+     * Create an instance using the UTF-8 encoding, with no proxy credentials.
      *
      * @param proxyHost the hostname to use
      * @param proxyPort the port to use
-     * @param proxyUser the user name for the proxy
-     * @param proxyPass the password for the proxy
-     * @param encoding the encoding to use
      */
-    public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass,
-            final Charset encoding) {
-        this.proxyHost = proxyHost;
-        this.proxyPort = proxyPort;
-        this.proxyUsername = proxyUser;
-        this.proxyPassword = proxyPass;
-        this.tunnelHost = null;
-        this.charset = encoding;
+    public FTPHTTPClient(final String proxyHost, final int proxyPort) {
+        this(proxyHost, proxyPort, null, null);
     }
 
     /**
-     * Create an instance using the UTF-8 encoding
+     * Create an instance using the specified encoding, with no proxy credentials.
      *
      * @param proxyHost the hostname to use
      * @param proxyPort the port to use
-     * @param proxyUser the user name for the proxy
-     * @param proxyPass the password for the proxy
+     * @param encoding the encoding to use
      */
-    public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass) {
-        this(proxyHost, proxyPort, proxyUser, proxyPass, StandardCharsets.UTF_8);
+    public FTPHTTPClient(final String proxyHost, final int proxyPort, final Charset encoding) {
+        this(proxyHost, proxyPort, null, null, encoding);
     }
 
     /**
-     * Create an instance using the UTF-8 encoding, with no proxy credentials.
+     * Create an instance using the UTF-8 encoding
      *
      * @param proxyHost the hostname to use
      * @param proxyPort the port to use
+     * @param proxyUser the user name for the proxy
+     * @param proxyPass the password for the proxy
      */
-    public FTPHTTPClient(final String proxyHost, final int proxyPort) {
-        this(proxyHost, proxyPort, null, null);
+    public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass) {
+        this(proxyHost, proxyPort, proxyUser, proxyPass, StandardCharsets.UTF_8);
     }
 
 
     /**
-     * Create an instance using the specified encoding, with no proxy credentials.
+     * Create an instance with the specified encoding
      *
      * @param proxyHost the hostname to use
      * @param proxyPort the port to use
+     * @param proxyUser the user name for the proxy
+     * @param proxyPass the password for the proxy
      * @param encoding the encoding to use
      */
-    public FTPHTTPClient(final String proxyHost, final int proxyPort, final Charset encoding) {
-        this(proxyHost, proxyPort, null, null, encoding);
+    public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass,
+            final Charset encoding) {
+        this.proxyHost = proxyHost;
+        this.proxyPort = proxyPort;
+        this.proxyUsername = proxyUser;
+        this.proxyPassword = proxyPass;
+        this.tunnelHost = null;
+        this.charset = encoding;
     }
 
 
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java b/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java
index 5ffe0c7..44abe3c 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPListParseEngine.java
@@ -75,18 +75,18 @@ import org.apache.commons.net.util.Charsets;
  * uses this class transparently.
  */
 public class FTPListParseEngine {
+    /**
+     * An empty immutable {@code FTPFile} array.
+     */
+    private static final FTPFile[] EMPTY_FTP_FILE_ARRAY = new FTPFile[0];
     private List<String> entries = new LinkedList<>();
-    private ListIterator<String> internalIterator = entries.listIterator();
 
+    private ListIterator<String> internalIterator = entries.listIterator();
     private final FTPFileEntryParser parser;
+
     // Should invalid files (parse failures) be allowed?
     private final boolean saveUnparseableEntries;
 
-    /**
-     * An empty immutable {@code FTPFile} array.
-     */
-    private static final FTPFile[] EMPTY_FTP_FILE_ARRAY = new FTPFile[0];
-
     public FTPListParseEngine(final FTPFileEntryParser parser) {
         this(parser, null);
     }
@@ -105,44 +105,55 @@ public class FTPListParseEngine {
     }
 
     /**
-     * Reads (and closes) the initial reading and preparsing of the list returned by the server. After this method has
-     * completed, this object will contain a list of unparsed entries (Strings) each referring to a unique file on the
-     * server.
-     *
-     * @param inputStream input stream provided by the server socket.
-     * @param charsetName the encoding to be used for reading the stream
+     * Returns an array of FTPFile objects containing the whole list of
+     * files returned by the server as read by this object's parser.
      *
-     * @throws IOException thrown on any failure to read from the sever.
+     * @return an array of FTPFile objects containing the whole list of
+     *         files returned by the server as read by this object's parser.
+     * None of the entries will be null
+     * @throws IOException - not ever thrown, may be removed in a later release
      */
-    public void readServerList(final InputStream inputStream, final String charsetName) throws IOException {
-        this.entries = new LinkedList<>();
-        read(inputStream, charsetName);
-        this.parser.preParse(this.entries);
-        resetIterator();
+    public FTPFile[] getFiles()
+    throws IOException // TODO remove; not actually thrown
+    {
+        return getFiles(FTPFileFilters.NON_NULL);
     }
 
     /**
-     * Internal method for reading (and closing) the input into the <code>entries</code> list. After this method has
-     * completed, <code>entries</code> will contain a collection of entries (as defined by
-     * <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain various non-entry preliminary lines from
-     * the server output, duplicates, and other data that will not be part of the final listing.
+     * Returns an array of FTPFile objects containing the whole list of
+     * files returned by the server as read by this object's parser.
+     * The files are filtered before being added to the array.
      *
-     * @param inputStream The socket stream on which the input will be read.
-     * @param charsetName The encoding to use.
+     * @param filter FTPFileFilter, must not be <code>null</code>.
      *
-     * @throws IOException thrown on any failure to read the stream
+     * @return an array of FTPFile objects containing the whole list of
+     *         files returned by the server as read by this object's parser.
+     * <p><b>
+     * NOTE:</b> This array may contain null members if any of the
+     * individual file listings failed to parse.  The caller should
+     * check each entry for null before referencing it, or use the
+     * a filter such as {@link FTPFileFilters#NON_NULL} which does not
+     * allow null entries.
+     * @since 2.2
+     * @throws IOException - not ever thrown, may be removed in a later release
      */
-    private void read(final InputStream inputStream, final String charsetName) throws IOException {
-        try (final BufferedReader reader = new BufferedReader(
-            new InputStreamReader(inputStream, Charsets.toCharset(charsetName)))) {
-
-            String line = this.parser.readNextEntry(reader);
-
-            while (line != null) {
-                this.entries.add(line);
-                line = this.parser.readNextEntry(reader);
+    public FTPFile[] getFiles(final FTPFileFilter filter)
+    throws IOException // TODO remove; not actually thrown
+    {
+        final List<FTPFile> tmpResults = new ArrayList<>();
+        final Iterator<String> iter = this.entries.iterator();
+        while (iter.hasNext()) {
+            final String entry = iter.next();
+            FTPFile temp = this.parser.parseFTPEntry(entry);
+            if (temp == null && saveUnparseableEntries) {
+                temp = new FTPFile(entry);
+            }
+            if (filter.accept(temp)) {
+                tmpResults.add(temp);
             }
         }
+        return tmpResults.toArray(EMPTY_FTP_FILE_ARRAY);
+
     }
 
     /**
@@ -226,58 +237,6 @@ public class FTPListParseEngine {
     }
 
     /**
-     * Returns an array of FTPFile objects containing the whole list of
-     * files returned by the server as read by this object's parser.
-     *
-     * @return an array of FTPFile objects containing the whole list of
-     *         files returned by the server as read by this object's parser.
-     * None of the entries will be null
-     * @throws IOException - not ever thrown, may be removed in a later release
-     */
-    public FTPFile[] getFiles()
-    throws IOException // TODO remove; not actually thrown
-    {
-        return getFiles(FTPFileFilters.NON_NULL);
-    }
-
-    /**
-     * Returns an array of FTPFile objects containing the whole list of
-     * files returned by the server as read by this object's parser.
-     * The files are filtered before being added to the array.
-     *
-     * @param filter FTPFileFilter, must not be <code>null</code>.
-     *
-     * @return an array of FTPFile objects containing the whole list of
-     *         files returned by the server as read by this object's parser.
-     * <p><b>
-     * NOTE:</b> This array may contain null members if any of the
-     * individual file listings failed to parse.  The caller should
-     * check each entry for null before referencing it, or use the
-     * a filter such as {@link FTPFileFilters#NON_NULL} which does not
-     * allow null entries.
-     * @since 2.2
-     * @throws IOException - not ever thrown, may be removed in a later release
-     */
-    public FTPFile[] getFiles(final FTPFileFilter filter)
-    throws IOException // TODO remove; not actually thrown
-    {
-        final List<FTPFile> tmpResults = new ArrayList<>();
-        final Iterator<String> iter = this.entries.iterator();
-        while (iter.hasNext()) {
-            final String entry = iter.next();
-            FTPFile temp = this.parser.parseFTPEntry(entry);
-            if (temp == null && saveUnparseableEntries) {
-                temp = new FTPFile(entry);
-            }
-            if (filter.accept(temp)) {
-                tmpResults.add(temp);
-            }
-        }
-        return tmpResults.toArray(EMPTY_FTP_FILE_ARRAY);
-
-    }
-
-    /**
      * convenience method to allow clients to know whether this object's
      * internal iterator's current position is at the end of the list.
      *
@@ -300,13 +259,28 @@ public class FTPListParseEngine {
     }
 
     /**
-     * resets this object's internal iterator to the beginning of the list.
+     * Internal method for reading (and closing) the input into the <code>entries</code> list. After this method has
+     * completed, <code>entries</code> will contain a collection of entries (as defined by
+     * <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain various non-entry preliminary lines from
+     * the server output, duplicates, and other data that will not be part of the final listing.
+     *
+     * @param inputStream The socket stream on which the input will be read.
+     * @param charsetName The encoding to use.
+     *
+     * @throws IOException thrown on any failure to read the stream
      */
-    public void resetIterator() {
-        this.internalIterator = this.entries.listIterator();
-    }
+    private void read(final InputStream inputStream, final String charsetName) throws IOException {
+        try (final BufferedReader reader = new BufferedReader(
+            new InputStreamReader(inputStream, Charsets.toCharset(charsetName)))) {
 
-    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
+            String line = this.parser.readNextEntry(reader);
+
+            while (line != null) {
+                this.entries.add(line);
+                line = this.parser.readNextEntry(reader);
+            }
+        }
+    }
 
     /**
      * Do not use.
@@ -319,4 +293,30 @@ public class FTPListParseEngine {
         readServerList(inputStream, null);
     }
 
+    /**
+     * Reads (and closes) the initial reading and preparsing of the list returned by the server. After this method has
+     * completed, this object will contain a list of unparsed entries (Strings) each referring to a unique file on the
+     * server.
+     *
+     * @param inputStream input stream provided by the server socket.
+     * @param charsetName the encoding to be used for reading the stream
+     *
+     * @throws IOException thrown on any failure to read from the sever.
+     */
+    public void readServerList(final InputStream inputStream, final String charsetName) throws IOException {
+        this.entries = new LinkedList<>();
+        read(inputStream, charsetName);
+        this.parser.preParse(this.entries);
+        resetIterator();
+    }
+
+    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
+
+    /**
+     * resets this object's internal iterator to the beginning of the list.
+     */
+    public void resetIterator() {
+        this.internalIterator = this.entries.listIterator();
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPReply.java b/src/main/java/org/apache/commons/net/ftp/FTPReply.java
index 30db15e..70aff16 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPReply.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPReply.java
@@ -99,25 +99,34 @@ public final class FTPReply
     /** @since 2.2 */
     public static final int EXTENDED_PORT_FAILURE = 522;
 
-    // Cannot be instantiated
-    private FTPReply()
-    {}
+    /**
+     * Determine if a reply code is a negative permanent response.  All
+     * codes beginning with a 5 are negative permanent responses.
+     * The FTP server will send a negative permanent response on the
+     * failure of a command that cannot be reattempted with success.
+     *
+     * @param reply  The reply code to test.
+     * @return True if a reply code is a negative permanent response, false
+     *         if not.
+     */
+    public static boolean isNegativePermanent(final int reply)
+    {
+        return reply >= 500 && reply < 600;
+    }
 
     /**
-     * Determine if a reply code is a positive preliminary response.  All
-     * codes beginning with a 1 are positive preliminary responses.
-     * Postitive preliminary responses are used to indicate tentative success.
-     * No further commands can be issued to the FTP server after a positive
-     * preliminary response until a follow up response is received from the
-     * server.
+     * Determine if a reply code is a negative transient response.  All
+     * codes beginning with a 4 are negative transient responses.
+     * The FTP server will send a negative transient response on the
+     * failure of a command that can be reattempted with success.
      *
      * @param reply  The reply code to test.
-     * @return True if a reply code is a positive preliminary response, false
+     * @return True if a reply code is a negative transient response, false
      *         if not.
      */
-    public static boolean isPositivePreliminary(final int reply)
+    public static boolean isNegativeTransient(final int reply)
     {
-        return reply >= 100 && reply < 200;
+        return reply >= 400 && reply < 500;
     }
 
     /**
@@ -154,33 +163,20 @@ public final class FTPReply
     }
 
     /**
-     * Determine if a reply code is a negative transient response.  All
-     * codes beginning with a 4 are negative transient responses.
-     * The FTP server will send a negative transient response on the
-     * failure of a command that can be reattempted with success.
-     *
-     * @param reply  The reply code to test.
-     * @return True if a reply code is a negative transient response, false
-     *         if not.
-     */
-    public static boolean isNegativeTransient(final int reply)
-    {
-        return reply >= 400 && reply < 500;
-    }
-
-    /**
-     * Determine if a reply code is a negative permanent response.  All
-     * codes beginning with a 5 are negative permanent responses.
-     * The FTP server will send a negative permanent response on the
-     * failure of a command that cannot be reattempted with success.
+     * Determine if a reply code is a positive preliminary response.  All
+     * codes beginning with a 1 are positive preliminary responses.
+     * Postitive preliminary responses are used to indicate tentative success.
+     * No further commands can be issued to the FTP server after a positive
+     * preliminary response until a follow up response is received from the
+     * server.
      *
      * @param reply  The reply code to test.
-     * @return True if a reply code is a negative permanent response, false
+     * @return True if a reply code is a positive preliminary response, false
      *         if not.
      */
-    public static boolean isNegativePermanent(final int reply)
+    public static boolean isPositivePreliminary(final int reply)
     {
-        return reply >= 500 && reply < 600;
+        return reply >= 100 && reply < 200;
     }
 
     /**
@@ -197,5 +193,9 @@ public final class FTPReply
         return reply >= 600 && reply < 700;
     }
 
+    // Cannot be instantiated
+    private FTPReply()
+    {}
+
 
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPSClient.java b/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
index 9f50584..d5e863c 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
@@ -83,6 +83,18 @@ public class FTPSClient extends FTPClient {
     /**  The CCC (Clear Command Channel) command. */
     private static final String CMD_CCC = "CCC";
 
+    /** @deprecated - not used - may be removed in a future release */
+    @Deprecated
+    public static String KEYSTORE_ALGORITHM;
+    /** @deprecated - not used - may be removed in a future release */
+    @Deprecated
+    public static String TRUSTSTORE_ALGORITHM;
+    /** @deprecated - not used - may be removed in a future release */
+    @Deprecated
+    public static String PROVIDER;
+    /** @deprecated - not used - may be removed in a future release */
+    @Deprecated
+    public static String STORE_TYPE;
     /** The security mode. (True - Implicit Mode / False - Explicit Mode) */
     private final boolean isImplicit;
     /** The secure socket protocol to be used, e.g. SSL/TLS. */
@@ -97,12 +109,16 @@ public class FTPSClient extends FTPClient {
     private boolean isCreation = true;
     /** The use client mode flag. */
     private boolean isClientMode = true;
+
     /** The need client auth flag. */
     private boolean isNeedClientAuth;
+
     /** The want client auth flag. */
     private boolean isWantClientAuth;
+
     /** The cipher suites */
     private String[] suites;
+
     /** The protocol versions */
     private String[] protocols;
 
@@ -138,30 +154,6 @@ public class FTPSClient extends FTPClient {
         this(DEFAULT_PROTOCOL, isImplicit);
     }
 
-    /**
-     * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}.
-     *
-     * @param protocol the protocol to use
-     */
-    public FTPSClient(final String protocol) {
-        this(protocol, false);
-    }
-
-    /**
-     * Constructor for FTPSClient allowing specification of protocol
-     * and security mode. If isImplicit is true, the port is set to
-     * {@link #DEFAULT_FTPS_PORT} i.e. 990.
-     * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
-     * @param protocol the protocol
-     * @param isImplicit The security mode(Implicit/Explicit).
-     */
-    public FTPSClient(final String protocol, final boolean isImplicit) {
-        this.protocol = protocol;
-        this.isImplicit = isImplicit;
-        if (isImplicit) {
-            setDefaultPort(DEFAULT_FTPS_PORT);
-        }
-    }
 
     /**
      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
@@ -186,23 +178,30 @@ public class FTPSClient extends FTPClient {
 
 
     /**
-     * Set AUTH command use value.
-     * This processing is done before connected processing.
-     * @param auth AUTH command use value.
+     * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}.
+     *
+     * @param protocol the protocol to use
      */
-    public void setAuthValue(final String auth) {
-        this.auth = auth;
+    public FTPSClient(final String protocol) {
+        this(protocol, false);
     }
 
     /**
-     * Return AUTH command use value.
-     * @return AUTH command use value.
+     * Constructor for FTPSClient allowing specification of protocol
+     * and security mode. If isImplicit is true, the port is set to
+     * {@link #DEFAULT_FTPS_PORT} i.e. 990.
+     * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
+     * @param protocol the protocol
+     * @param isImplicit The security mode(Implicit/Explicit).
      */
-    public String getAuthValue() {
-        return this.auth;
+    public FTPSClient(final String protocol, final boolean isImplicit) {
+        this.protocol = protocol;
+        this.isImplicit = isImplicit;
+        if (isImplicit) {
+            setDefaultPort(DEFAULT_FTPS_PORT);
+        }
     }
 
-
     /**
      * Because there are so many connect() methods,
      * the _connectAction_() method is provided as a means of performing
@@ -227,227 +226,256 @@ public class FTPSClient extends FTPClient {
     }
 
     /**
-     * AUTH command.
-     * @throws SSLException If it server reply code not equal "234" and "334".
-     * @throws IOException If an I/O error occurs while either sending
-     * the command.
-     */
-    protected void execAUTH() throws SSLException, IOException {
-        final int replyCode = sendCommand(CMD_AUTH, auth);
-        if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
-            // replyCode = 334
-            // I carry out an ADAT command.
-        } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
-            throw new SSLException(getReplyString());
-        }
-    }
-
-    /**
-     * Performs a lazy init of the SSL context
-     * @throws IOException
+     * Returns a socket of the data connection.
+     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
+     * @param command The int representation of the FTP command to send.
+     * @param arg The arguments to the FTP command.
+     * If this parameter is set to null, then the command is sent with
+     * no arguments.
+     * @return corresponding to the established data connection.
+     * Null is returned if an FTP protocol error is reported at any point
+     * during the establishment and initialization of the connection.
+     * @throws IOException If there is any problem with the connection.
+     * @see FTPClient#_openDataConnection_(int, String)
+     * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead
      */
-    private void initSslContext() throws IOException {
-        if (context == null) {
-            context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
-        }
+    @Override
+    // Strictly speaking this is not needed, but it works round a Clirr bug
+    // So rather than invoke the parent code, we do it here
+    @Deprecated
+    protected Socket _openDataConnection_(final int command, final String arg)
+            throws IOException {
+        return _openDataConnection_(FTPCommand.getCommand(command), arg);
     }
 
     /**
-     * SSL/TLS negotiation. Acquires an SSL socket of a control
-     * connection and carries out handshake processing.
-     * @throws IOException If server negotiation fails
-     */
-    protected void sslNegotiation() throws IOException {
-        plainSocket = _socket_;
-        initSslContext();
-        final SSLSocket socket = createSSLSocket(_socket_);
-        socket.setEnableSessionCreation(isCreation);
-        socket.setUseClientMode(isClientMode);
-
-        // client mode
-        if (isClientMode) {
-            if (tlsEndpointChecking) {
-                SSLSocketUtils.enableEndpointNameVerification(socket);
+         * Returns a socket of the data connection.
+         * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
+         * @param command The textual representation of the FTP command to send.
+         * @param arg The arguments to the FTP command.
+         * If this parameter is set to null, then the command is sent with
+         * no arguments.
+         * @return corresponding to the established data connection.
+         * Null is returned if an FTP protocol error is reported at any point
+         * during the establishment and initialization of the connection.
+         * @throws IOException If there is any problem with the connection.
+         * @see FTPClient#_openDataConnection_(int, String)
+         * @since 3.2
+         */
+        @Override
+        protected Socket _openDataConnection_(final String command, final String arg)
+                throws IOException {
+            final Socket socket = super._openDataConnection_(command, arg);
+            _prepareDataSocket_(socket);
+            if (socket instanceof SSLSocket) {
+                final SSLSocket sslSocket = (SSLSocket)socket;
+
+                sslSocket.setUseClientMode(isClientMode);
+                sslSocket.setEnableSessionCreation(isCreation);
+
+                // server mode
+                if (!isClientMode) {
+                    sslSocket.setNeedClientAuth(isNeedClientAuth);
+                    sslSocket.setWantClientAuth(isWantClientAuth);
+                }
+                if (suites != null) {
+                    sslSocket.setEnabledCipherSuites(suites);
+                }
+                if (protocols != null) {
+                    sslSocket.setEnabledProtocols(protocols);
+                }
+                sslSocket.startHandshake();
             }
-        } else { // server mode
-            socket.setNeedClientAuth(isNeedClientAuth);
-            socket.setWantClientAuth(isWantClientAuth);
-        }
 
-        if (protocols != null) {
-            socket.setEnabledProtocols(protocols);
-        }
-        if (suites != null) {
-            socket.setEnabledCipherSuites(suites);
+            return socket;
         }
-        socket.startHandshake();
 
-        // TODO the following setup appears to duplicate that in the super class methods
-        _socket_ = socket;
-        _controlInput_ = new BufferedReader(new InputStreamReader(
-                socket .getInputStream(), getControlEncoding()));
-        _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
-                socket.getOutputStream(), getControlEncoding()));
+    /**
+    * Performs any custom initialization for a newly created SSLSocket (before
+    * the SSL handshake happens).
+    * Called by {@link #_openDataConnection_(int, String)} immediately
+    * after creating the socket.
+    * The default implementation is a no-op
+     * @param socket the socket to set up
+    * @throws IOException on error
+    * @since 3.1
+    */
+    protected void _prepareDataSocket_(final Socket socket)
+            throws IOException {
+    }
 
-        if (isClientMode) {
-            if (hostnameVerifier != null &&
-                !hostnameVerifier.verify(_hostname_, socket.getSession())) {
-                throw new SSLHandshakeException("Hostname doesn't match certificate");
+    /**
+     * Check the value that can be set in PROT Command value.
+     * @param prot Data Channel Protection Level.
+     * @return True - A set point is right / False - A set point is not right
+     */
+    private boolean checkPROTValue(final String prot) {
+        for (final String element : PROT_COMMAND_VALUE)
+        {
+            if (element.equals(prot)) {
+                return true;
             }
         }
+        return false;
     }
 
     /**
-     * Get the {@link KeyManager} instance.
-     * @return The {@link KeyManager} instance
+     * Create SSL socket from plain socket.
+     *
+     * @param socket
+     * @return SSL Socket
+     * @throws IOException
      */
-    private KeyManager getKeyManager() {
-        return keyManager;
+    private SSLSocket createSSLSocket(final Socket socket) throws IOException {
+        if (socket != null) {
+            final SSLSocketFactory f = context.getSocketFactory();
+            return (SSLSocket) f.createSocket(socket, _hostname_, socket.getPort(), false);
+        }
+        return null;
     }
 
     /**
-    * Set a {@link KeyManager} to use
-    *
-    * @param keyManager The KeyManager implementation to set.
-    * @see org.apache.commons.net.util.KeyManagerUtils
-    */
-    public void setKeyManager(final KeyManager keyManager) {
-        this.keyManager = keyManager;
+     * Closes the connection to the FTP server and restores
+     * connection parameters to the default values.
+     * <p>
+     * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
+     * to reset the factories that may have been changed during the session,
+     * e.g. by {@link #execPROT(String)}
+     * @throws IOException If an error occurs while disconnecting.
+     * @since 3.0
+     */
+    @Override
+    public void disconnect() throws IOException
+    {
+        super.disconnect();
+        if (plainSocket != null) {
+            plainSocket.close();
+        }
+        setSocketFactory(null);
+        setServerSocketFactory(null);
     }
 
     /**
-     * Controls whether a new SSL session may be established by this socket.
-     * @param isCreation The established socket flag.
+     * Send the ADAT command with the specified authentication data.
+     * @param data The data to send with the command.
+     * @return server reply.
+     * @throws IOException If an I/O error occurs while sending
+     * the command.
+     * @since 3.0
      */
-    public void setEnabledSessionCreation(final boolean isCreation) {
-        this.isCreation = isCreation;
+    public int execADAT(final byte[] data) throws IOException
+    {
+        if (data != null)
+        {
+            return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
+        }
+        return sendCommand(CMD_ADAT);
     }
 
     /**
-     * Returns true if new SSL sessions may be established by this socket.
-     * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an
-     * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled,
-     * this returns False.
-     * @return true - Indicates that sessions may be created;
-     * this is the default.
-     * false - indicates that an existing session must be resumed.
+     * AUTH command.
+     * @throws SSLException If it server reply code not equal "234" and "334".
+     * @throws IOException If an I/O error occurs while either sending
+     * the command.
      */
-    public boolean getEnableSessionCreation() {
-        if (_socket_ instanceof SSLSocket) {
-            return ((SSLSocket)_socket_).getEnableSessionCreation();
+    protected void execAUTH() throws SSLException, IOException {
+        final int replyCode = sendCommand(CMD_AUTH, auth);
+        if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
+            // replyCode = 334
+            // I carry out an ADAT command.
+        } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
+            throw new SSLException(getReplyString());
         }
-        return false;
     }
 
     /**
-     * Configures the socket to require client authentication.
-     * @param isNeedClientAuth The need client auth flag.
+     * Send the AUTH command with the specified mechanism.
+     * @param mechanism The mechanism name to send with the command.
+     * @return server reply.
+     * @throws IOException If an I/O error occurs while sending
+     * the command.
+     * @since 3.0
      */
-    public void setNeedClientAuth(final boolean isNeedClientAuth) {
-        this.isNeedClientAuth = isNeedClientAuth;
+    public int execAUTH(final String mechanism) throws IOException
+    {
+        return sendCommand(CMD_AUTH, mechanism);
     }
 
     /**
-     * Returns true if the socket will require client authentication.
-     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
-     * @return true - If the server mode socket should request
-     * that the client authenticate itself.
-     */
-    public boolean getNeedClientAuth() {
-        if (_socket_ instanceof SSLSocket) {
-            return ((SSLSocket)_socket_).getNeedClientAuth();
-        }
-        return false;
-    }
-
-    /**
-     * Configures the socket to request client authentication,
-     * but only if such a request is appropriate to the cipher
-     * suite negotiated.
-     * @param isWantClientAuth The want client auth flag.
-     */
-    public void setWantClientAuth(final boolean isWantClientAuth) {
-        this.isWantClientAuth = isWantClientAuth;
-    }
-
-    /**
-     * Returns true if the socket will request client authentication.
-     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
-     * @return true - If the server mode socket should request
-     * that the client authenticate itself.
-     */
-    public boolean getWantClientAuth() {
-        if (_socket_ instanceof SSLSocket) {
-            return ((SSLSocket)_socket_).getWantClientAuth();
-        }
-        return false;
-    }
-
-    /**
-     * Configures the socket to use client (or server) mode in its first
-     * handshake.
-     * @param isClientMode The use client mode flag.
+     * Send the CCC command to the server.
+     * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
+     * to a plain {@link Socket} instances
+     * @return server reply.
+     * @throws IOException If an I/O error occurs while sending
+     * the command.
+     * @since 3.0
      */
-    public void setUseClientMode(final boolean isClientMode) {
-        this.isClientMode = isClientMode;
+    public int execCCC() throws IOException
+    {
+        final int repCode = sendCommand(CMD_CCC);
+// This will be performed by sendCommand(String, String)
+//        if (FTPReply.isPositiveCompletion(repCode)) {
+//            _socket_.close();
+//            _socket_ = plainSocket;
+//            _controlInput_ = new BufferedReader(
+//                new InputStreamReader(
+//                    _socket_.getInputStream(), getControlEncoding()));
+//            _controlOutput_ = new BufferedWriter(
+//                new OutputStreamWriter(
+//                    _socket_.getOutputStream(), getControlEncoding()));
+//        }
+        return repCode;
     }
 
     /**
-     * Returns true if the socket is set to use client mode
-     * in its first handshake.
-     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
-     * @return true - If the socket should start its first handshake
-     * in "client" mode.
+     * Send the CONF command with the specified data.
+     * @param data The data to send with the command.
+     * @return server reply.
+     * @throws IOException If an I/O error occurs while sending
+     * the command.
+     * @since 3.0
      */
-    public boolean getUseClientMode() {
-        if (_socket_ instanceof SSLSocket) {
-            return ((SSLSocket)_socket_).getUseClientMode();
+    public int execCONF(final byte[] data) throws IOException
+    {
+        if (data != null)
+        {
+            return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
         }
-        return false;
-    }
-
-    /**
-     * Controls which particular cipher suites are enabled for use on this
-     * connection. Called before server negotiation.
-     * @param cipherSuites The cipher suites.
-     */
-    public void setEnabledCipherSuites(final String[] cipherSuites) {
-        suites = cipherSuites.clone();
+        return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
     }
 
     /**
-     * Returns the names of the cipher suites which could be enabled
-     * for use on this connection.
-     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
-     * @return An array of cipher suite names, or <code>null</code>
+     * Send the ENC command with the specified data.
+     * @param data The data to send with the command.
+     * @return server reply.
+     * @throws IOException If an I/O error occurs while sending
+     * the command.
+     * @since 3.0
      */
-    public String[] getEnabledCipherSuites() {
-        if (_socket_ instanceof SSLSocket) {
-            return ((SSLSocket)_socket_).getEnabledCipherSuites();
+    public int execENC(final byte[] data) throws IOException
+    {
+        if (data != null)
+        {
+            return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
         }
-        return null;
-    }
-
-    /**
-     * Controls which particular protocol versions are enabled for use on this
-     * connection. I perform setting before a server negotiation.
-     * @param protocolVersions The protocol versions.
-     */
-    public void setEnabledProtocols(final String[] protocolVersions) {
-        protocols = protocolVersions.clone();
+        return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
     }
 
     /**
-     * Returns the names of the protocol versions which are currently
-     * enabled for use on this connection.
-     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
-     * @return An array of protocols, or <code>null</code>
+     * Send the MIC command with the specified data.
+     * @param data The data to send with the command.
+     * @return server reply.
+     * @throws IOException If an I/O error occurs while sending
+     * the command.
+     * @since 3.0
      */
-    public String[] getEnabledProtocols() {
-        if (_socket_ instanceof SSLSocket) {
-            return ((SSLSocket)_socket_).getEnabledProtocols();
+    public int execMIC(final byte[] data) throws IOException
+    {
+        if (data != null)
+        {
+            return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
         }
-        return null;
+        return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
     }
 
     /**
@@ -469,31 +497,6 @@ public class FTPSClient extends FTPClient {
     }
 
     /**
-     * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
-     * Issues the command and parses the response to return the negotiated value.
-     *
-     * @param pbsz Protection Buffer Size.
-     * @throws SSLException If the server reply code does not equal "200".
-     * @throws IOException If an I/O error occurs while sending
-     * the command.
-     * @return the negotiated value.
-     * @see #execPBSZ(long)
-     * @since 3.0
-     */
-    public long parsePBSZ(final long pbsz) throws SSLException, IOException {
-        execPBSZ(pbsz);
-        long minvalue = pbsz;
-        final String remainder = extractPrefixedData("PBSZ=", getReplyString());
-        if (remainder != null) {
-            final long replysz = Long.parseLong(remainder);
-            if (replysz < minvalue) {
-                minvalue = replysz;
-            }
-        }
-        return minvalue;
-    }
-
-    /**
      * PROT command.
      * <ul>
      * <li>C - Clear</li>
@@ -531,129 +534,100 @@ public class FTPSClient extends FTPClient {
     }
 
     /**
-     * Check the value that can be set in PROT Command value.
-     * @param prot Data Channel Protection Level.
-     * @return True - A set point is right / False - A set point is not right
+     * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
+     * @param prefix the prefix to find
+     * @param reply where to find the prefix
+     * @return the remainder of the string after the prefix, or null if the prefix was not present.
      */
-    private boolean checkPROTValue(final String prot) {
-        for (final String element : PROT_COMMAND_VALUE)
-        {
-            if (element.equals(prot)) {
-                return true;
-            }
+    private String extractPrefixedData(final String prefix, final String reply) {
+        final int idx = reply.indexOf(prefix);
+        if (idx == -1) {
+            return null;
         }
-        return false;
+        // N.B. Cannot use trim before substring as leading space would affect the offset.
+        return reply.substring(idx+prefix.length()).trim();
     }
 
     /**
-     * Send an FTP command.
-     * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket}
-     * instance to be assigned to a plain {@link Socket}
-     * @param command The FTP command.
-     * @return server reply.
-     * @throws IOException If an I/O error occurs while sending the command.
-     * @throws SSLException if a CCC command fails
-     * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
+     * Return AUTH command use value.
+     * @return AUTH command use value.
      */
-    // Would like to remove this method, but that will break any existing clients that are using CCC
-    @Override
-    public int sendCommand(final String command, final String args) throws IOException {
-        final int repCode = super.sendCommand(command, args);
-        /* If CCC is issued, restore socket i/o streams to unsecured versions */
-        if (CMD_CCC.equals(command)) {
-            if (FTPReply.COMMAND_OK == repCode) {
-                _socket_.close();
-                _socket_ = plainSocket;
-                _controlInput_ = new BufferedReader(
-                    new InputStreamReader(
-                        _socket_ .getInputStream(), getControlEncoding()));
-                _controlOutput_ = new BufferedWriter(
-                    new OutputStreamWriter(
-                        _socket_.getOutputStream(), getControlEncoding()));
-            } else {
-                throw new SSLException(getReplyString());
-            }
-        }
-        return repCode;
+    public String getAuthValue() {
+        return this.auth;
     }
 
     /**
-     * Returns a socket of the data connection.
-     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
-     * @param command The int representation of the FTP command to send.
-     * @param arg The arguments to the FTP command.
-     * If this parameter is set to null, then the command is sent with
-     * no arguments.
-     * @return corresponding to the established data connection.
-     * Null is returned if an FTP protocol error is reported at any point
-     * during the establishment and initialization of the connection.
-     * @throws IOException If there is any problem with the connection.
-     * @see FTPClient#_openDataConnection_(int, String)
-     * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead
+     * Returns the names of the cipher suites which could be enabled
+     * for use on this connection.
+     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
+     * @return An array of cipher suite names, or <code>null</code>
      */
-    @Override
-    // Strictly speaking this is not needed, but it works round a Clirr bug
-    // So rather than invoke the parent code, we do it here
-    @Deprecated
-    protected Socket _openDataConnection_(final int command, final String arg)
-            throws IOException {
-        return _openDataConnection_(FTPCommand.getCommand(command), arg);
+    public String[] getEnabledCipherSuites() {
+        if (_socket_ instanceof SSLSocket) {
+            return ((SSLSocket)_socket_).getEnabledCipherSuites();
+        }
+        return null;
     }
 
-   /**
-     * Returns a socket of the data connection.
-     * Wrapped as an {@link SSLSocket}, which carries out handshake processing.
-     * @param command The textual representation of the FTP command to send.
-     * @param arg The arguments to the FTP command.
-     * If this parameter is set to null, then the command is sent with
-     * no arguments.
-     * @return corresponding to the established data connection.
-     * Null is returned if an FTP protocol error is reported at any point
-     * during the establishment and initialization of the connection.
-     * @throws IOException If there is any problem with the connection.
-     * @see FTPClient#_openDataConnection_(int, String)
-     * @since 3.2
+    /**
+     * Returns the names of the protocol versions which are currently
+     * enabled for use on this connection.
+     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
+     * @return An array of protocols, or <code>null</code>
      */
-    @Override
-    protected Socket _openDataConnection_(final String command, final String arg)
-            throws IOException {
-        final Socket socket = super._openDataConnection_(command, arg);
-        _prepareDataSocket_(socket);
-        if (socket instanceof SSLSocket) {
-            final SSLSocket sslSocket = (SSLSocket)socket;
-
-            sslSocket.setUseClientMode(isClientMode);
-            sslSocket.setEnableSessionCreation(isCreation);
-
-            // server mode
-            if (!isClientMode) {
-                sslSocket.setNeedClientAuth(isNeedClientAuth);
-                sslSocket.setWantClientAuth(isWantClientAuth);
-            }
-            if (suites != null) {
-                sslSocket.setEnabledCipherSuites(suites);
-            }
-            if (protocols != null) {
-                sslSocket.setEnabledProtocols(protocols);
-            }
-            sslSocket.startHandshake();
+    public String[] getEnabledProtocols() {
+        if (_socket_ instanceof SSLSocket) {
+            return ((SSLSocket)_socket_).getEnabledProtocols();
         }
+        return null;
+    }
 
-        return socket;
+    /**
+     * Returns true if new SSL sessions may be established by this socket.
+     * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an
+     * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled,
+     * this returns False.
+     * @return true - Indicates that sessions may be created;
+     * this is the default.
+     * false - indicates that an existing session must be resumed.
+     */
+    public boolean getEnableSessionCreation() {
+        if (_socket_ instanceof SSLSocket) {
+            return ((SSLSocket)_socket_).getEnableSessionCreation();
+        }
+        return false;
     }
 
     /**
-    * Performs any custom initialization for a newly created SSLSocket (before
-    * the SSL handshake happens).
-    * Called by {@link #_openDataConnection_(int, String)} immediately
-    * after creating the socket.
-    * The default implementation is a no-op
-     * @param socket the socket to set up
-    * @throws IOException on error
-    * @since 3.1
-    */
-    protected void _prepareDataSocket_(final Socket socket)
-            throws IOException {
+     * Get the currently configured {@link HostnameVerifier}.
+     * The verifier is only used on client mode connections.
+     * @return A HostnameVerifier instance.
+     * @since 3.4
+     */
+    public HostnameVerifier getHostnameVerifier()
+    {
+        return hostnameVerifier;
+    }
+
+   /**
+ * Get the {@link KeyManager} instance.
+ * @return The {@link KeyManager} instance
+ */
+private KeyManager getKeyManager() {
+    return keyManager;
+}
+
+    /**
+     * Returns true if the socket will require client authentication.
+     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
+     * @return true - If the server mode socket should request
+     * that the client authenticate itself.
+     */
+    public boolean getNeedClientAuth() {
+        if (_socket_ instanceof SSLSocket) {
+            return ((SSLSocket)_socket_).getNeedClientAuth();
+        }
+        return false;
     }
 
     /**
@@ -666,36 +640,40 @@ public class FTPSClient extends FTPClient {
     }
 
     /**
-     * Override the default {@link TrustManager} to use; if set to {@code null},
-     * the default TrustManager from the JVM will be used.
-     *
-     * @param trustManager The TrustManager implementation to set, may be {@code null}
-     * @see org.apache.commons.net.util.TrustManagerUtils
+     * Returns true if the socket is set to use client mode
+     * in its first handshake.
+     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
+     * @return true - If the socket should start its first handshake
+     * in "client" mode.
      */
-    public void setTrustManager(final TrustManager trustManager) {
-        this.trustManager = trustManager;
+    public boolean getUseClientMode() {
+        if (_socket_ instanceof SSLSocket) {
+            return ((SSLSocket)_socket_).getUseClientMode();
+        }
+        return false;
     }
 
     /**
-     * Get the currently configured {@link HostnameVerifier}.
-     * The verifier is only used on client mode connections.
-     * @return A HostnameVerifier instance.
-     * @since 3.4
+     * Returns true if the socket will request client authentication.
+     * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
+     * @return true - If the server mode socket should request
+     * that the client authenticate itself.
      */
-    public HostnameVerifier getHostnameVerifier()
-    {
-        return hostnameVerifier;
+    public boolean getWantClientAuth() {
+        if (_socket_ instanceof SSLSocket) {
+            return ((SSLSocket)_socket_).getWantClientAuth();
+        }
+        return false;
     }
 
     /**
-     * Override the default {@link HostnameVerifier} to use.
-     * The verifier is only used on client mode connections.
-     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
-     * @since 3.4
+     * Performs a lazy init of the SSL context
+     * @throws IOException
      */
-    public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier)
-    {
-        hostnameVerifier = newHostnameVerifier;
+    private void initSslContext() throws IOException {
+        if (context == null) {
+            context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
+        }
     }
 
     /**
@@ -713,208 +691,230 @@ public class FTPSClient extends FTPClient {
     }
 
     /**
-     * Automatic endpoint identification checking using the HTTPS algorithm
-     * is supported on Java 1.7+. The default behavior is for this to be disabled.
-     *
-     * This check is only performed on client mode connections.
-     *
-     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
-     * @since 3.4
-     */
-    public void setEndpointCheckingEnabled(final boolean enable)
-    {
-        tlsEndpointChecking = enable;
-    }
-
-    /**
-     * Closes the connection to the FTP server and restores
-     * connection parameters to the default values.
-     * <p>
-     * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
-     * to reset the factories that may have been changed during the session,
-     * e.g. by {@link #execPROT(String)}
-     * @throws IOException If an error occurs while disconnecting.
+     * Parses the given ADAT response line and base64-decodes the data.
+     * @param reply The ADAT reply to parse.
+     * @return the data in the reply, base64-decoded.
      * @since 3.0
      */
-    @Override
-    public void disconnect() throws IOException
+    public byte[] parseADATReply(final String reply)
     {
-        super.disconnect();
-        if (plainSocket != null) {
-            plainSocket.close();
+        if (reply == null) {
+            return null;
         }
-        setSocketFactory(null);
-        setServerSocketFactory(null);
+        return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
     }
 
     /**
-     * Send the AUTH command with the specified mechanism.
-     * @param mechanism The mechanism name to send with the command.
-     * @return server reply.
+     * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
+     * Issues the command and parses the response to return the negotiated value.
+     *
+     * @param pbsz Protection Buffer Size.
+     * @throws SSLException If the server reply code does not equal "200".
      * @throws IOException If an I/O error occurs while sending
      * the command.
+     * @return the negotiated value.
+     * @see #execPBSZ(long)
      * @since 3.0
      */
-    public int execAUTH(final String mechanism) throws IOException
-    {
-        return sendCommand(CMD_AUTH, mechanism);
+    public long parsePBSZ(final long pbsz) throws SSLException, IOException {
+        execPBSZ(pbsz);
+        long minvalue = pbsz;
+        final String remainder = extractPrefixedData("PBSZ=", getReplyString());
+        if (remainder != null) {
+            final long replysz = Long.parseLong(remainder);
+            if (replysz < minvalue) {
+                minvalue = replysz;
+            }
+        }
+        return minvalue;
     }
 
     /**
-     * Send the ADAT command with the specified authentication data.
-     * @param data The data to send with the command.
+     * Send an FTP command.
+     * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket}
+     * instance to be assigned to a plain {@link Socket}
+     * @param command The FTP command.
      * @return server reply.
-     * @throws IOException If an I/O error occurs while sending
-     * the command.
-     * @since 3.0
+     * @throws IOException If an I/O error occurs while sending the command.
+     * @throws SSLException if a CCC command fails
+     * @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
      */
-    public int execADAT(final byte[] data) throws IOException
-    {
-        if (data != null)
-        {
-            return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
+    // Would like to remove this method, but that will break any existing clients that are using CCC
+    @Override
+    public int sendCommand(final String command, final String args) throws IOException {
+        final int repCode = super.sendCommand(command, args);
+        /* If CCC is issued, restore socket i/o streams to unsecured versions */
+        if (CMD_CCC.equals(command)) {
+            if (FTPReply.COMMAND_OK == repCode) {
+                _socket_.close();
+                _socket_ = plainSocket;
+                _controlInput_ = new BufferedReader(
+                    new InputStreamReader(
+                        _socket_ .getInputStream(), getControlEncoding()));
+                _controlOutput_ = new BufferedWriter(
+                    new OutputStreamWriter(
+                        _socket_.getOutputStream(), getControlEncoding()));
+            } else {
+                throw new SSLException(getReplyString());
+            }
         }
-        return sendCommand(CMD_ADAT);
+        return repCode;
     }
 
     /**
-     * Send the CCC command to the server.
-     * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance  to be assigned
-     * to a plain {@link Socket} instances
-     * @return server reply.
-     * @throws IOException If an I/O error occurs while sending
-     * the command.
-     * @since 3.0
+     * Set AUTH command use value.
+     * This processing is done before connected processing.
+     * @param auth AUTH command use value.
      */
-    public int execCCC() throws IOException
-    {
-        final int repCode = sendCommand(CMD_CCC);
-// This will be performed by sendCommand(String, String)
-//        if (FTPReply.isPositiveCompletion(repCode)) {
-//            _socket_.close();
-//            _socket_ = plainSocket;
-//            _controlInput_ = new BufferedReader(
-//                new InputStreamReader(
-//                    _socket_.getInputStream(), getControlEncoding()));
-//            _controlOutput_ = new BufferedWriter(
-//                new OutputStreamWriter(
-//                    _socket_.getOutputStream(), getControlEncoding()));
-//        }
-        return repCode;
+    public void setAuthValue(final String auth) {
+        this.auth = auth;
     }
 
     /**
-     * Send the MIC command with the specified data.
-     * @param data The data to send with the command.
-     * @return server reply.
-     * @throws IOException If an I/O error occurs while sending
-     * the command.
-     * @since 3.0
+     * Controls which particular cipher suites are enabled for use on this
+     * connection. Called before server negotiation.
+     * @param cipherSuites The cipher suites.
      */
-    public int execMIC(final byte[] data) throws IOException
-    {
-        if (data != null)
-        {
-            return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
-        }
-        return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
+    public void setEnabledCipherSuites(final String[] cipherSuites) {
+        suites = cipherSuites.clone();
     }
 
     /**
-     * Send the CONF command with the specified data.
-     * @param data The data to send with the command.
-     * @return server reply.
-     * @throws IOException If an I/O error occurs while sending
-     * the command.
-     * @since 3.0
+     * Controls which particular protocol versions are enabled for use on this
+     * connection. I perform setting before a server negotiation.
+     * @param protocolVersions The protocol versions.
      */
-    public int execCONF(final byte[] data) throws IOException
-    {
-        if (data != null)
-        {
-            return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
-        }
-        return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
+    public void setEnabledProtocols(final String[] protocolVersions) {
+        protocols = protocolVersions.clone();
     }
 
     /**
-     * Send the ENC command with the specified data.
-     * @param data The data to send with the command.
-     * @return server reply.
-     * @throws IOException If an I/O error occurs while sending
-     * the command.
-     * @since 3.0
+     * Controls whether a new SSL session may be established by this socket.
+     * @param isCreation The established socket flag.
      */
-    public int execENC(final byte[] data) throws IOException
+    public void setEnabledSessionCreation(final boolean isCreation) {
+        this.isCreation = isCreation;
+    }
+
+    /**
+     * Automatic endpoint identification checking using the HTTPS algorithm
+     * is supported on Java 1.7+. The default behavior is for this to be disabled.
+     *
+     * This check is only performed on client mode connections.
+     *
+     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
+     * @since 3.4
+     */
+    public void setEndpointCheckingEnabled(final boolean enable)
     {
-        if (data != null)
-        {
-            return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
-        }
-        return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
+        tlsEndpointChecking = enable;
     }
 
     /**
-     * Parses the given ADAT response line and base64-decodes the data.
-     * @param reply The ADAT reply to parse.
-     * @return the data in the reply, base64-decoded.
-     * @since 3.0
+     * Override the default {@link HostnameVerifier} to use.
+     * The verifier is only used on client mode connections.
+     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
+     * @since 3.4
      */
-    public byte[] parseADATReply(final String reply)
+    public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier)
     {
-        if (reply == null) {
-            return null;
-        }
-        return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
+        hostnameVerifier = newHostnameVerifier;
     }
 
     /**
-     * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
-     * @param prefix the prefix to find
-     * @param reply where to find the prefix
-     * @return the remainder of the string after the prefix, or null if the prefix was not present.
+    * Set a {@link KeyManager} to use
+    *
+    * @param keyManager The KeyManager implementation to set.
+    * @see org.apache.commons.net.util.KeyManagerUtils
+    */
+    public void setKeyManager(final KeyManager keyManager) {
+        this.keyManager = keyManager;
+    }
+
+    /**
+     * Configures the socket to require client authentication.
+     * @param isNeedClientAuth The need client auth flag.
      */
-    private String extractPrefixedData(final String prefix, final String reply) {
-        final int idx = reply.indexOf(prefix);
-        if (idx == -1) {
-            return null;
-        }
-        // N.B. Cannot use trim before substring as leading space would affect the offset.
-        return reply.substring(idx+prefix.length()).trim();
+    public void setNeedClientAuth(final boolean isNeedClientAuth) {
+        this.isNeedClientAuth = isNeedClientAuth;
     }
 
+    // DEPRECATED - for API compatibility only - DO NOT USE
+
     /**
-     * Create SSL socket from plain socket.
+     * Override the default {@link TrustManager} to use; if set to {@code null},
+     * the default TrustManager from the JVM will be used.
      *
-     * @param socket
-     * @return SSL Socket
-     * @throws IOException
+     * @param trustManager The TrustManager implementation to set, may be {@code null}
+     * @see org.apache.commons.net.util.TrustManagerUtils
      */
-    private SSLSocket createSSLSocket(final Socket socket) throws IOException {
-        if (socket != null) {
-            final SSLSocketFactory f = context.getSocketFactory();
-            return (SSLSocket) f.createSocket(socket, _hostname_, socket.getPort(), false);
-        }
-        return null;
+    public void setTrustManager(final TrustManager trustManager) {
+        this.trustManager = trustManager;
     }
 
-    // DEPRECATED - for API compatibility only - DO NOT USE
+    /**
+     * Configures the socket to use client (or server) mode in its first
+     * handshake.
+     * @param isClientMode The use client mode flag.
+     */
+    public void setUseClientMode(final boolean isClientMode) {
+        this.isClientMode = isClientMode;
+    }
 
-    /** @deprecated - not used - may be removed in a future release */
-    @Deprecated
-    public static String KEYSTORE_ALGORITHM;
+    /**
+     * Configures the socket to request client authentication,
+     * but only if such a request is appropriate to the cipher
+     * suite negotiated.
+     * @param isWantClientAuth The want client auth flag.
+     */
+    public void setWantClientAuth(final boolean isWantClientAuth) {
+        this.isWantClientAuth = isWantClientAuth;
+    }
 
-    /** @deprecated - not used - may be removed in a future release */
-    @Deprecated
-    public static String TRUSTSTORE_ALGORITHM;
+    /**
+     * SSL/TLS negotiation. Acquires an SSL socket of a control
+     * connection and carries out handshake processing.
+     * @throws IOException If server negotiation fails
+     */
+    protected void sslNegotiation() throws IOException {
+        plainSocket = _socket_;
+        initSslContext();
+        final SSLSocket socket = createSSLSocket(_socket_);
+        socket.setEnableSessionCreation(isCreation);
+        socket.setUseClientMode(isClientMode);
 
-    /** @deprecated - not used - may be removed in a future release */
-    @Deprecated
-    public static String PROVIDER;
+        // client mode
+        if (isClientMode) {
+            if (tlsEndpointChecking) {
+                SSLSocketUtils.enableEndpointNameVerification(socket);
+            }
+        } else { // server mode
+            socket.setNeedClientAuth(isNeedClientAuth);
+            socket.setWantClientAuth(isWantClientAuth);
+        }
 
-    /** @deprecated - not used - may be removed in a future release */
-    @Deprecated
-    public static String STORE_TYPE;
+        if (protocols != null) {
+            socket.setEnabledProtocols(protocols);
+        }
+        if (suites != null) {
+            socket.setEnabledCipherSuites(suites);
+        }
+        socket.startHandshake();
+
+        // TODO the following setup appears to duplicate that in the super class methods
+        _socket_ = socket;
+        _controlInput_ = new BufferedReader(new InputStreamReader(
+                socket .getInputStream(), getControlEncoding()));
+        _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
+                socket.getOutputStream(), getControlEncoding()));
+
+        if (isClientMode) {
+            if (hostnameVerifier != null &&
+                !hostnameVerifier.verify(_hostname_, socket.getSession())) {
+                throw new SSLHandshakeException("Hostname doesn't match certificate");
+            }
+        }
+    }
 
 }
 /* kate: indent-width 4; replace-tabs on; */
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java b/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java
index 3beaaa1..211d747 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPSSocketFactory.java
@@ -39,36 +39,6 @@ public class FTPSSocketFactory extends SocketFactory {
         this.context = context;
     }
 
-    // Override the default implementation
-    @Override
-    public Socket createSocket() throws IOException{
-        return this.context.getSocketFactory().createSocket();
-    }
-
-    @Override
-    public Socket createSocket(final String address, final int port) throws UnknownHostException, IOException {
-        return this.context.getSocketFactory().createSocket(address, port);
-    }
-
-    @Override
-    public Socket createSocket(final InetAddress address, final int port) throws IOException {
-        return this.context.getSocketFactory().createSocket(address, port);
-    }
-
-    @Override
-    public Socket createSocket(final String address, final int port, final InetAddress localAddress, final int localPort)
-            throws UnknownHostException, IOException {
-        return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
-    }
-
-    @Override
-    public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress,
-            final int localPort) throws IOException {
-        return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
-    }
-
-    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
-
     /** @param port the port
      * @return  the socket
      * @throws IOException on error
@@ -100,6 +70,36 @@ public class FTPSSocketFactory extends SocketFactory {
         return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress));
     }
 
+    // Override the default implementation
+    @Override
+    public Socket createSocket() throws IOException{
+        return this.context.getSocketFactory().createSocket();
+    }
+
+    @Override
+    public Socket createSocket(final InetAddress address, final int port) throws IOException {
+        return this.context.getSocketFactory().createSocket(address, port);
+    }
+
+    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
+
+    @Override
+    public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress,
+            final int localPort) throws IOException {
+        return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
+    }
+
+    @Override
+    public Socket createSocket(final String address, final int port) throws UnknownHostException, IOException {
+        return this.context.getSocketFactory().createSocket(address, port);
+    }
+
+    @Override
+    public Socket createSocket(final String address, final int port, final InetAddress localAddress, final int localPort)
+            throws UnknownHostException, IOException {
+        return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort);
+    }
+
     /** @param socket the socket
      * @return the socket
      * @throws IOException  on error
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java b/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java
index 6e30a77..ac88607 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java
@@ -71,22 +71,6 @@ implements Configurable
     }
 
     /**
-     * This method is called by the concrete parsers to delegate
-     * timestamp parsing to the timestamp parser.
-     *
-     * @param timestampStr the timestamp string pulled from the
-     * file listing by the regular expression parser, to be submitted
-     * to the <code>timestampParser</code> for extracting the timestamp.
-     * @return a <code>java.util.Calendar</code> containing results of the
-     * timestamp parse.
-     * @throws ParseException on parse error
-     */
-    public Calendar parseTimestamp(final String timestampStr) throws ParseException {
-        return this.timestampParser.parseTimestamp(timestampStr);
-    }
-
-
-    /**
      * Implementation of the {@link  Configurable  Configurable}
      * interface. Configures this parser by delegating to the
      * underlying Configurable FTPTimestampParser implementation, '
@@ -117,6 +101,7 @@ implements Configurable
         }
     }
 
+
     /**
      * Each concrete subclass must define this member to create
      * a default configuration to be used when that subclass is
@@ -125,4 +110,19 @@ implements Configurable
      * @return the default configuration for the subclass.
      */
     protected abstract FTPClientConfig getDefaultConfiguration();
+
+    /**
+     * This method is called by the concrete parsers to delegate
+     * timestamp parsing to the timestamp parser.
+     *
+     * @param timestampStr the timestamp string pulled from the
+     * file listing by the regular expression parser, to be submitted
+     * to the <code>timestampParser</code> for extracting the timestamp.
+     * @return a <code>java.util.Calendar</code> containing results of the
+     * timestamp parse.
+     * @throws ParseException on parse error
+     */
+    public Calendar parseTimestamp(final String timestampStr) throws ParseException {
+        return this.timestampParser.parseTimestamp(timestampStr);
+    }
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java b/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java
index de72eb2..7981293 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java
@@ -46,6 +46,35 @@ public class DefaultFTPFileEntryParserFactory
     private static final Pattern JAVA_QUALIFIED_NAME_PATTERN = Pattern.compile(JAVA_QUALIFIED_NAME);
 
     /**
+     * <p>Implementation extracts a key from the supplied
+     * {@link  FTPClientConfig FTPClientConfig}
+     * parameter and creates an object implementing the
+     * interface FTPFileEntryParser and uses the supplied configuration
+     * to configure it.
+     * </p><p>
+     * Note that this method will generally not be called in scenarios
+     * that call for autodetection of parser type but rather, for situations
+     * where the user knows that the server uses a non-default configuration
+     * and knows what that configuration is.
+     * </p>
+     * @param config  A {@link  FTPClientConfig FTPClientConfig}
+     * used to configure the parser created
+     *
+     * @return the @link  FTPFileEntryParser FTPFileEntryParser} so created.
+     * @throws ParserInitializationException
+     *                   Thrown on any exception in instantiation
+     * @throws NullPointerException if {@code config} is {@code null}
+     * @since 1.4
+     */
+    @Override
+    public FTPFileEntryParser createFileEntryParser(final FTPClientConfig config)
+    throws ParserInitializationException
+    {
+        final String key = config.getServerSystemKey();
+        return createFileEntryParser(key, config);
+    }
+
+    /**
      * This default implementation of the FTPFileEntryParserFactory
      * interface works according to the following logic:
      * First it attempts to interpret the supplied key as a fully
@@ -175,44 +204,10 @@ public class DefaultFTPFileEntryParserFactory
         return parser;
     }
 
-    /**
-     * <p>Implementation extracts a key from the supplied
-     * {@link  FTPClientConfig FTPClientConfig}
-     * parameter and creates an object implementing the
-     * interface FTPFileEntryParser and uses the supplied configuration
-     * to configure it.
-     * </p><p>
-     * Note that this method will generally not be called in scenarios
-     * that call for autodetection of parser type but rather, for situations
-     * where the user knows that the server uses a non-default configuration
-     * and knows what that configuration is.
-     * </p>
-     * @param config  A {@link  FTPClientConfig FTPClientConfig}
-     * used to configure the parser created
-     *
-     * @return the @link  FTPFileEntryParser FTPFileEntryParser} so created.
-     * @throws ParserInitializationException
-     *                   Thrown on any exception in instantiation
-     * @throws NullPointerException if {@code config} is {@code null}
-     * @since 1.4
-     */
-    @Override
-    public FTPFileEntryParser createFileEntryParser(final FTPClientConfig config)
-    throws ParserInitializationException
-    {
-        final String key = config.getServerSystemKey();
-        return createFileEntryParser(key, config);
-    }
-
-
-    public FTPFileEntryParser createUnixFTPEntryParser()
-    {
-        return new UnixFTPEntryParser();
-    }
 
-    public FTPFileEntryParser createVMSVersioningFTPEntryParser()
+    public FTPFileEntryParser createMVSEntryParser()
     {
-        return new VMSVersioningFTPEntryParser();
+        return new MVSFTPEntryParser();
     }
 
     public FTPFileEntryParser createNetwareFTPEntryParser() {
@@ -248,12 +243,12 @@ public class DefaultFTPFileEntryParserFactory
                });
     }
 
-     public FTPFileEntryParser createOS2FTPEntryParser()
+    public FTPFileEntryParser createOS2FTPEntryParser()
     {
         return new OS2FTPEntryParser();
     }
 
-    public FTPFileEntryParser createOS400FTPEntryParser()
+     public FTPFileEntryParser createOS400FTPEntryParser()
     {
         return createOS400FTPEntryParser(null);
     }
@@ -282,9 +277,14 @@ public class DefaultFTPFileEntryParserFactory
             });
     }
 
-    public FTPFileEntryParser createMVSEntryParser()
+    public FTPFileEntryParser createUnixFTPEntryParser()
     {
-        return new MVSFTPEntryParser();
+        return new UnixFTPEntryParser();
+    }
+
+    public FTPFileEntryParser createVMSVersioningFTPEntryParser()
+    {
+        return new VMSVersioningFTPEntryParser();
     }
 
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java b/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
index 59b711a..c99386a 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java
@@ -26,21 +26,6 @@ import org.apache.commons.net.ftp.FTPFileEntryParser;
 public interface FTPFileEntryParserFactory
 {
     /**
-     * Implementation should be a method that decodes the
-     * supplied key and creates an object implementing the
-     * interface FTPFileEntryParser.
-     *
-     * @param key    A string that somehow identifies an
-     *               FTPFileEntryParser to be created.
-     *
-     * @return the FTPFileEntryParser created.
-     * @throws ParserInitializationException
-     *                   Thrown on any exception in instantiation
-     */
-    FTPFileEntryParser createFileEntryParser(String key)
-        throws ParserInitializationException;
-
-    /**
      *<p>
      * Implementation should be a method that extracts
      * a key from the supplied {@link  FTPClientConfig FTPClientConfig}
@@ -65,4 +50,19 @@ public interface FTPFileEntryParserFactory
     FTPFileEntryParser createFileEntryParser(FTPClientConfig config)
         throws ParserInitializationException;
 
+    /**
+     * Implementation should be a method that decodes the
+     * supplied key and creates an object implementing the
+     * interface FTPFileEntryParser.
+     *
+     * @param key    A string that somehow identifies an
+     *               FTPFileEntryParser to be created.
+     *
+     * @return the FTPFileEntryParser created.
+     * @throws ParserInitializationException
+     *                   Thrown on any exception in instantiation
+     */
+    FTPFileEntryParser createFileEntryParser(String key)
+        throws ParserInitializationException;
+
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java b/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
index 99761e9..648fd5a 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
@@ -41,18 +41,6 @@ public class FTPTimestampParserImpl implements
 {
 
 
-    /** The date format for all dates, except possibly recent dates. Assumed to include the year. */
-    private SimpleDateFormat defaultDateFormat;
-    /* The index in CALENDAR_UNITS of the smallest time unit in defaultDateFormat */
-    private int defaultDateSmallestUnitIndex;
-
-    /** The format used for recent dates (which don't have the year). May be null. */
-    private SimpleDateFormat recentDateFormat;
-    /* The index in CALENDAR_UNITS of the smallest time unit in recentDateFormat */
-    private int recentDateSmallestUnitIndex;
-
-    private boolean lenientFutureDates;
-
     /*
      * List of units in order of increasing significance.
      * This allows the code to clear all units in the Calendar until it
@@ -73,7 +61,6 @@ public class FTPTimestampParserImpl implements
         Calendar.DAY_OF_MONTH,
         Calendar.MONTH,
         Calendar.YEAR};
-
     /*
      * Return the index to the array representing the least significant
      * unit found in the date format.
@@ -118,7 +105,6 @@ public class FTPTimestampParserImpl implements
         }
         return 0;
     }
-
     /*
      * Sets the Calendar precision (used by FTPFile#toFormattedDate) by clearing
      * the immediately preceeding unit (if any).
@@ -139,6 +125,20 @@ public class FTPTimestampParserImpl implements
         }
     }
 
+    /** The date format for all dates, except possibly recent dates. Assumed to include the year. */
+    private SimpleDateFormat defaultDateFormat;
+
+    /* The index in CALENDAR_UNITS of the smallest time unit in defaultDateFormat */
+    private int defaultDateSmallestUnitIndex;
+
+    /** The format used for recent dates (which don't have the year). May be null. */
+    private SimpleDateFormat recentDateFormat;
+
+    /* The index in CALENDAR_UNITS of the smallest time unit in recentDateFormat */
+    private int recentDateSmallestUnitIndex;
+
+    private boolean lenientFutureDates;
+
     /**
      * The only constructor for this class.
      */
@@ -148,6 +148,103 @@ public class FTPTimestampParserImpl implements
     }
 
     /**
+     * Implementation of the {@link  Configurable  Configurable}
+     * interface. Configures this <code>FTPTimestampParser</code> according
+     * to the following logic:
+     * <p>
+     * Set up the {@link  FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat}
+     * and optionally the {@link  FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat}
+     * to values supplied in the config based on month names configured as follows:
+     * </p>
+     * <ul>
+     * <li>If a {@link  FTPClientConfig#setShortMonthNames(String) shortMonthString}
+     * has been supplied in the <code>config</code>, use that to parse  parse timestamps.</li>
+     * <li>Otherwise, if a {@link  FTPClientConfig#setServerLanguageCode(String) serverLanguageCode}
+     * has been supplied in the <code>config</code>, use the month names represented
+     * by that {@link  FTPClientConfig#lookupDateFormatSymbols(String) language}
+     * to parse timestamps.</li>
+     * <li>otherwise use default English month names</li>
+     * </ul><p>
+     * Finally if a {@link  org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId}
+     * has been supplied via the config, set that into all date formats that have
+     * been configured.
+     * </p>
+     */
+    @Override
+    public void configure(final FTPClientConfig config) {
+        DateFormatSymbols dfs = null;
+
+        final String languageCode = config.getServerLanguageCode();
+        final String shortmonths = config.getShortMonthNames();
+        if (shortmonths != null) {
+            dfs = FTPClientConfig.getDateFormatSymbols(shortmonths);
+        } else if (languageCode != null) {
+            dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode);
+        } else {
+            dfs = FTPClientConfig.lookupDateFormatSymbols("en");
+        }
+
+
+        final String recentFormatString = config.getRecentDateFormatStr();
+        setRecentDateFormat(recentFormatString, dfs);
+
+        final String defaultFormatString = config.getDefaultDateFormatStr();
+        if (defaultFormatString == null) {
+            throw new IllegalArgumentException("defaultFormatString cannot be null");
+        }
+        setDefaultDateFormat(defaultFormatString, dfs);
+
+        setServerTimeZone(config.getServerTimeZoneId());
+
+        this.lenientFutureDates = config.isLenientFutureDates();
+    }
+
+    /**
+     * @return Returns the defaultDateFormat.
+     */
+    public SimpleDateFormat getDefaultDateFormat() {
+        return defaultDateFormat;
+    }
+
+    /**
+     * @return Returns the defaultDateFormat pattern string.
+     */
+    public String getDefaultDateFormatString() {
+        return defaultDateFormat.toPattern();
+    }
+    /**
+     * @return Returns the recentDateFormat.
+     */
+    public SimpleDateFormat getRecentDateFormat() {
+        return recentDateFormat;
+    }
+    /**
+     * @return Returns the recentDateFormat.
+     */
+    public String getRecentDateFormatString() {
+        return recentDateFormat.toPattern();
+    }
+    /**
+     * @return Returns the serverTimeZone used by this parser.
+     */
+    public TimeZone getServerTimeZone() {
+        return this.defaultDateFormat.getTimeZone();
+    }
+    /**
+     * @return returns an array of 12 strings representing the short
+     * month names used by this parse.
+     */
+    public String[] getShortMonths() {
+        return defaultDateFormat.getDateFormatSymbols().getShortMonths();
+    }
+    /**
+     * @return Returns the lenientFutureDates.
+     */
+    boolean isLenientFutureDates() {
+        return lenientFutureDates;
+    }
+
+    /**
      * Implements the one {@link  FTPTimestampParser#parseTimestamp(String)  method}
      * in the {@link  FTPTimestampParser  FTPTimestampParser} interface
      * according to this algorithm:
@@ -170,6 +267,7 @@ public class FTPTimestampParserImpl implements
         return parseTimestamp(timestampStr, now);
     }
 
+
     /**
      * If the recentDateFormat member has been defined, try to parse the
      * supplied string with that.  If that parse fails, or if the recentDateFormat
@@ -248,19 +346,6 @@ public class FTPTimestampParserImpl implements
         setPrecision(defaultDateSmallestUnitIndex, working);
         return working;
     }
-
-    /**
-     * @return Returns the defaultDateFormat.
-     */
-    public SimpleDateFormat getDefaultDateFormat() {
-        return defaultDateFormat;
-    }
-    /**
-     * @return Returns the defaultDateFormat pattern string.
-     */
-    public String getDefaultDateFormatString() {
-        return defaultDateFormat.toPattern();
-    }
     /**
      * @param format The defaultDateFormat to be set.
      * @param dfs the symbols to use (may be null)
@@ -278,17 +363,12 @@ public class FTPTimestampParserImpl implements
         }
         this.defaultDateSmallestUnitIndex = getEntry(this.defaultDateFormat);
     }
+
     /**
-     * @return Returns the recentDateFormat.
-     */
-    public SimpleDateFormat getRecentDateFormat() {
-        return recentDateFormat;
-    }
-    /**
-     * @return Returns the recentDateFormat.
+     * @param lenientFutureDates The lenientFutureDates to set.
      */
-    public String getRecentDateFormatString() {
-        return recentDateFormat.toPattern();
+    void setLenientFutureDates(final boolean lenientFutureDates) {
+        this.lenientFutureDates = lenientFutureDates;
     }
     /**
      * @param format The recentDateFormat to set.
@@ -307,22 +387,6 @@ public class FTPTimestampParserImpl implements
         }
         this.recentDateSmallestUnitIndex = getEntry(this.recentDateFormat);
     }
-
-    /**
-     * @return returns an array of 12 strings representing the short
-     * month names used by this parse.
-     */
-    public String[] getShortMonths() {
-        return defaultDateFormat.getDateFormatSymbols().getShortMonths();
-    }
-
-
-    /**
-     * @return Returns the serverTimeZone used by this parser.
-     */
-    public TimeZone getServerTimeZone() {
-        return this.defaultDateFormat.getTimeZone();
-    }
     /**
      * sets a TimeZone represented by the supplied ID string into all
      * of the parsers used by this server.
@@ -339,68 +403,4 @@ public class FTPTimestampParserImpl implements
             this.recentDateFormat.setTimeZone(serverTimeZone);
         }
     }
-
-    /**
-     * Implementation of the {@link  Configurable  Configurable}
-     * interface. Configures this <code>FTPTimestampParser</code> according
-     * to the following logic:
-     * <p>
-     * Set up the {@link  FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat}
-     * and optionally the {@link  FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat}
-     * to values supplied in the config based on month names configured as follows:
-     * </p>
-     * <ul>
-     * <li>If a {@link  FTPClientConfig#setShortMonthNames(String) shortMonthString}
-     * has been supplied in the <code>config</code>, use that to parse  parse timestamps.</li>
-     * <li>Otherwise, if a {@link  FTPClientConfig#setServerLanguageCode(String) serverLanguageCode}
-     * has been supplied in the <code>config</code>, use the month names represented
-     * by that {@link  FTPClientConfig#lookupDateFormatSymbols(String) language}
-     * to parse timestamps.</li>
-     * <li>otherwise use default English month names</li>
-     * </ul><p>
-     * Finally if a {@link  org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId}
-     * has been supplied via the config, set that into all date formats that have
-     * been configured.
-     * </p>
-     */
-    @Override
-    public void configure(final FTPClientConfig config) {
-        DateFormatSymbols dfs = null;
-
-        final String languageCode = config.getServerLanguageCode();
-        final String shortmonths = config.getShortMonthNames();
-        if (shortmonths != null) {
-            dfs = FTPClientConfig.getDateFormatSymbols(shortmonths);
-        } else if (languageCode != null) {
-            dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode);
-        } else {
-            dfs = FTPClientConfig.lookupDateFormatSymbols("en");
-        }
-
-
-        final String recentFormatString = config.getRecentDateFormatStr();
-        setRecentDateFormat(recentFormatString, dfs);
-
-        final String defaultFormatString = config.getDefaultDateFormatStr();
-        if (defaultFormatString == null) {
-            throw new IllegalArgumentException("defaultFormatString cannot be null");
-        }
-        setDefaultDateFormat(defaultFormatString, dfs);
-
-        setServerTimeZone(config.getServerTimeZoneId());
-
-        this.lenientFutureDates = config.isLenientFutureDates();
-    }
-    /**
-     * @return Returns the lenientFutureDates.
-     */
-    boolean isLenientFutureDates() {
-        return lenientFutureDates;
-    }
-    /**
-     * @param lenientFutureDates The lenientFutureDates to set.
-     */
-    void setLenientFutureDates(final boolean lenientFutureDates) {
-        this.lenientFutureDates = lenientFutureDates;
-    }
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/MLSxEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/MLSxEntryParser.java
index 841d327..d0c6cd5 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/MLSxEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/MLSxEntryParser.java
@@ -88,96 +88,12 @@ public class MLSxEntryParser extends FTPFileEntryParserImpl
 /* 7 */  {FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION, FTPFile.EXECUTE_PERMISSION},
     };
 
-    /**
-     * Create the parser for MSLT and MSLD listing entries
-     * This class is immutable, so one can use {@link #getInstance()} instead.
-     */
-    public MLSxEntryParser()
-    {
+    public static  MLSxEntryParser getInstance() {
+        return INSTANCE;
     }
 
-    @Override
-    public FTPFile parseFTPEntry(final String entry) {
-        if (entry.startsWith(" ")) {// leading space means no facts are present
-            if (entry.length() > 1) { // is there a path name?
-                final FTPFile file = new FTPFile();
-                file.setRawListing(entry);
-                file.setName(entry.substring(1));
-                return file;
-            }
-            return null; // Invalid - no pathname
-
-        }
-        final String parts[] = entry.split(" ",2); // Path may contain space
-        if (parts.length != 2 || parts[1].isEmpty()) {
-            return null; // no space found or no file name
-        }
-        final String factList = parts[0];
-        if (!factList.endsWith(";")) {
-            return null;
-        }
-        final FTPFile file = new FTPFile();
-        file.setRawListing(entry);
-        file.setName(parts[1]);
-        final String[] facts = factList.split(";");
-        final boolean hasUnixMode = parts[0].toLowerCase(Locale.ENGLISH).contains("unix.mode=");
-        for(final String fact : facts) {
-            final String []factparts = fact.split("=", -1); // Don't drop empty values
-// Sample missing permission
-// drwx------   2 mirror   mirror       4096 Mar 13  2010 subversion
-// modify=20100313224553;perm=;type=dir;unique=811U282598;UNIX.group=500;UNIX.mode=0700;UNIX.owner=500; subversion
-            if (factparts.length != 2) {
-                return null; // invalid - there was no "=" sign
-            }
-            final String factname = factparts[0].toLowerCase(Locale.ENGLISH);
-            final String factvalue = factparts[1];
-            if (factvalue.isEmpty()) {
-                continue; // nothing to see here
-            }
-            final String valueLowerCase = factvalue.toLowerCase(Locale.ENGLISH);
-            if ("size".equals(factname) || "sizd".equals(factname)) {
-                file.setSize(Long.parseLong(factvalue));
-            }
-            else if ("modify".equals(factname)) {
-                final Calendar parsed = parseGMTdateTime(factvalue);
-                if (parsed == null) {
-                    return null;
-                }
-                file.setTimestamp(parsed);
-            }
-            else if ("type".equals(factname)) {
-                    final Integer intType = TYPE_TO_INT.get(valueLowerCase);
-                    if (intType == null) {
-                        file.setType(FTPFile.UNKNOWN_TYPE);
-                    } else {
-                        file.setType(intType.intValue());
-                    }
-            }
-            else if (factname.startsWith("unix.")) {
-                final String unixfact = factname.substring("unix.".length()).toLowerCase(Locale.ENGLISH);
-                if ("group".equals(unixfact)){
-                    file.setGroup(factvalue);
-                } else if ("owner".equals(unixfact)){
-                    file.setUser(factvalue);
-                } else if ("mode".equals(unixfact)){ // e.g. 0[1]755
-                    final int off = factvalue.length()-3; // only parse last 3 digits
-                    for(int i=0; i < 3; i++){
-                        final int ch = factvalue.charAt(off+i)-'0';
-                        if (ch >= 0 && ch <= 7) { // Check it's valid octal
-                            for(final int p : UNIX_PERMS[ch]) {
-                                file.setPermission(UNIX_GROUPS[i], p, true);
-                            }
-                        } else {
-                            // TODO should this cause failure, or can it be reported somehow?
-                        }
-                    } // digits
-                } // mode
-            } // unix.
-            else if (!hasUnixMode && "perm".equals(factname)) { // skip if we have the UNIX.mode
-                doUnixPerms(file, valueLowerCase);
-            } // process "perm"
-        } // each fact
-        return file;
+    public static FTPFile parseEntry(final String entry) {
+        return INSTANCE.parseFTPEntry(entry);
     }
 
     /**
@@ -225,6 +141,14 @@ public class MLSxEntryParser extends FTPFileEntryParserImpl
         return parseGMTdateTime(timestamp).toInstant();
     }
 
+    /**
+     * Create the parser for MSLT and MSLD listing entries
+     * This class is immutable, so one can use {@link #getInstance()} instead.
+     */
+    public MLSxEntryParser()
+    {
+    }
+
     //              perm-fact    = "Perm" "=" *pvals
     //              pvals        = "a" / "c" / "d" / "e" / "f" /
     //                             "l" / "m" / "p" / "r" / "w"
@@ -269,11 +193,87 @@ public class MLSxEntryParser extends FTPFileEntryParserImpl
         } // each char
     }
 
-    public static FTPFile parseEntry(final String entry) {
-        return INSTANCE.parseFTPEntry(entry);
-    }
+    @Override
+    public FTPFile parseFTPEntry(final String entry) {
+        if (entry.startsWith(" ")) {// leading space means no facts are present
+            if (entry.length() > 1) { // is there a path name?
+                final FTPFile file = new FTPFile();
+                file.setRawListing(entry);
+                file.setName(entry.substring(1));
+                return file;
+            }
+            return null; // Invalid - no pathname
 
-    public static  MLSxEntryParser getInstance() {
-        return INSTANCE;
+        }
+        final String parts[] = entry.split(" ",2); // Path may contain space
+        if (parts.length != 2 || parts[1].isEmpty()) {
+            return null; // no space found or no file name
+        }
+        final String factList = parts[0];
+        if (!factList.endsWith(";")) {
+            return null;
+        }
+        final FTPFile file = new FTPFile();
+        file.setRawListing(entry);
+        file.setName(parts[1]);
+        final String[] facts = factList.split(";");
+        final boolean hasUnixMode = parts[0].toLowerCase(Locale.ENGLISH).contains("unix.mode=");
+        for(final String fact : facts) {
+            final String []factparts = fact.split("=", -1); // Don't drop empty values
+// Sample missing permission
+// drwx------   2 mirror   mirror       4096 Mar 13  2010 subversion
+// modify=20100313224553;perm=;type=dir;unique=811U282598;UNIX.group=500;UNIX.mode=0700;UNIX.owner=500; subversion
+            if (factparts.length != 2) {
+                return null; // invalid - there was no "=" sign
+            }
+            final String factname = factparts[0].toLowerCase(Locale.ENGLISH);
+            final String factvalue = factparts[1];
+            if (factvalue.isEmpty()) {
+                continue; // nothing to see here
+            }
+            final String valueLowerCase = factvalue.toLowerCase(Locale.ENGLISH);
+            if ("size".equals(factname) || "sizd".equals(factname)) {
+                file.setSize(Long.parseLong(factvalue));
+            }
+            else if ("modify".equals(factname)) {
+                final Calendar parsed = parseGMTdateTime(factvalue);
+                if (parsed == null) {
+                    return null;
+                }
+                file.setTimestamp(parsed);
+            }
+            else if ("type".equals(factname)) {
+                    final Integer intType = TYPE_TO_INT.get(valueLowerCase);
+                    if (intType == null) {
+                        file.setType(FTPFile.UNKNOWN_TYPE);
+                    } else {
+                        file.setType(intType.intValue());
+                    }
+            }
+            else if (factname.startsWith("unix.")) {
+                final String unixfact = factname.substring("unix.".length()).toLowerCase(Locale.ENGLISH);
+                if ("group".equals(unixfact)){
+                    file.setGroup(factvalue);
+                } else if ("owner".equals(unixfact)){
+                    file.setUser(factvalue);
+                } else if ("mode".equals(unixfact)){ // e.g. 0[1]755
+                    final int off = factvalue.length()-3; // only parse last 3 digits
+                    for(int i=0; i < 3; i++){
+                        final int ch = factvalue.charAt(off+i)-'0';
+                        if (ch >= 0 && ch <= 7) { // Check it's valid octal
+                            for(final int p : UNIX_PERMS[ch]) {
+                                file.setPermission(UNIX_GROUPS[i], p, true);
+                            }
+                        } else {
+                            // TODO should this cause failure, or can it be reported somehow?
+                        }
+                    } // digits
+                } // mode
+            } // unix.
+            else if (!hasUnixMode && "perm".equals(factname)) { // skip if we have the UNIX.mode
+                doUnixPerms(file, valueLowerCase);
+            } // process "perm"
+        } // each fact
+        return file;
     }
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java
index 7dce7db..fa06533 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java
@@ -39,13 +39,6 @@ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
     static final int JES_LEVEL_1_LIST_TYPE = 3;
     static final int JES_LEVEL_2_LIST_TYPE = 4;
 
-    private int isType = UNKNOWN_LIST_TYPE;
-
-    /**
-     * Fallback parser for Unix-style listings
-     */
-    private UnixFTPEntryParser unixFTPEntryParser;
-
     /**
      * Dates are ignored for file lists, but are used for member lists where
      * possible
@@ -156,6 +149,13 @@ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
             "(\\S+).*" // rest ignored
     ;
 
+    private int isType = UNKNOWN_LIST_TYPE;
+
+    /**
+     * Fallback parser for Unix-style listings
+     */
+    private UnixFTPEntryParser unixFTPEntryParser;
+
     /*
      * ---------------------------------------------------------------------
      * Very brief and incomplete description of the zOS/MVS-file system. (Note:
@@ -252,32 +252,13 @@ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
         super.configure(null); // configure parser with default configurations
     }
 
-    /**
-     * Parses a line of an z/OS - MVS FTP server file listing and converts it
-     * into a usable format in the form of an <code> FTPFile </code> instance.
-     * If the file listing line doesn't describe a file, then
-     * <code> null </code> is returned. Otherwise a <code> FTPFile </code>
-     * instance representing the file is returned.
-     *
-     * @param entry
-     *            A line of text from the file listing
-     * @return An FTPFile instance corresponding to the supplied entry
+    /*
+     * @return
      */
     @Override
-    public FTPFile parseFTPEntry(final String entry) {
-        if (isType == FILE_LIST_TYPE) {
-            return parseFileList(entry);
-        } else if (isType == MEMBER_LIST_TYPE) {
-            return parseMemberList(entry);
-        } else if (isType == UNIX_LIST_TYPE) {
-             return unixFTPEntryParser.parseFTPEntry(entry);
-        } else if (isType == JES_LEVEL_1_LIST_TYPE) {
-            return parseJeslevel1List(entry);
-        } else if (isType == JES_LEVEL_2_LIST_TYPE) {
-            return parseJeslevel2List(entry);
-        }
-
-        return null;
+    protected FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(FTPClientConfig.SYST_MVS,
+                DEFAULT_DATE_FORMAT, null);
     }
 
     /**
@@ -330,59 +311,30 @@ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
     }
 
     /**
-     * Parse entries within a partitioned dataset.
-     *
-     * Format of a memberlist within a PDS:
-     * <pre>
-     *    0         1        2          3        4     5     6      7    8
-     *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
-     *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
-     *   TBTOOL    01.12 2002/09/12 2004/11/26 19:54    51    28     0 KIL001
-     *
-     * -------------------------------------------
-     * [1] Name
-     * [2] VV.MM: Version . modification
-     * [3] Created: yyyy / MM / dd
-     * [4,5] Changed: yyyy / MM / dd HH:mm
-     * [6] Size: number of lines
-     * [7] Init: number of lines when first created
-     * [8] Mod: number of modified lines a last save
-     * [9] Id: User id for last update
-     * </pre>
+     * Parses a line of an z/OS - MVS FTP server file listing and converts it
+     * into a usable format in the form of an <code> FTPFile </code> instance.
+     * If the file listing line doesn't describe a file, then
+     * <code> null </code> is returned. Otherwise a <code> FTPFile </code>
+     * instance representing the file is returned.
      *
-     * @param entry zosDirectoryEntry
-     * @return null: entry was not parsed.
+     * @param entry
+     *            A line of text from the file listing
+     * @return An FTPFile instance corresponding to the supplied entry
      */
-    private FTPFile parseMemberList(final String entry) {
-        final FTPFile file = new FTPFile();
-        if (matches(entry)) {
-            file.setRawListing(entry);
-            final String name = group(1);
-            final String datestr = group(2) + " " + group(3);
-            file.setName(name);
-            file.setType(FTPFile.FILE_TYPE);
-            try {
-                file.setTimestamp(super.parseTimestamp(datestr));
-            } catch (final ParseException e) {
-                // just ignore parsing errors.
-                // TODO check this is ok
-                // Drop thru to try simple parser
-            }
-            return file;
+    @Override
+    public FTPFile parseFTPEntry(final String entry) {
+        if (isType == FILE_LIST_TYPE) {
+            return parseFileList(entry);
+        } else if (isType == MEMBER_LIST_TYPE) {
+            return parseMemberList(entry);
+        } else if (isType == UNIX_LIST_TYPE) {
+             return unixFTPEntryParser.parseFTPEntry(entry);
+        } else if (isType == JES_LEVEL_1_LIST_TYPE) {
+            return parseJeslevel1List(entry);
+        } else if (isType == JES_LEVEL_2_LIST_TYPE) {
+            return parseJeslevel2List(entry);
         }
 
-        /*
-         * Assigns the name to the first word of the entry. Only to be used from a
-         * safe context, for example from a memberlist, where the regex for some
-         * reason fails. Then just assign the name field of FTPFile.
-         */
-        if (entry != null && !entry.trim().isEmpty()) {
-            file.setRawListing(entry);
-            final String name = entry.split(" ")[0];
-            file.setName(name);
-            file.setType(FTPFile.FILE_TYPE);
-            return file;
-        }
         return null;
     }
 
@@ -458,6 +410,63 @@ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
     }
 
     /**
+     * Parse entries within a partitioned dataset.
+     *
+     * Format of a memberlist within a PDS:
+     * <pre>
+     *    0         1        2          3        4     5     6      7    8
+     *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
+     *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
+     *   TBTOOL    01.12 2002/09/12 2004/11/26 19:54    51    28     0 KIL001
+     *
+     * -------------------------------------------
+     * [1] Name
+     * [2] VV.MM: Version . modification
+     * [3] Created: yyyy / MM / dd
+     * [4,5] Changed: yyyy / MM / dd HH:mm
+     * [6] Size: number of lines
+     * [7] Init: number of lines when first created
+     * [8] Mod: number of modified lines a last save
+     * [9] Id: User id for last update
+     * </pre>
+     *
+     * @param entry zosDirectoryEntry
+     * @return null: entry was not parsed.
+     */
+    private FTPFile parseMemberList(final String entry) {
+        final FTPFile file = new FTPFile();
+        if (matches(entry)) {
+            file.setRawListing(entry);
+            final String name = group(1);
+            final String datestr = group(2) + " " + group(3);
+            file.setName(name);
+            file.setType(FTPFile.FILE_TYPE);
+            try {
+                file.setTimestamp(super.parseTimestamp(datestr));
+            } catch (final ParseException e) {
+                // just ignore parsing errors.
+                // TODO check this is ok
+                // Drop thru to try simple parser
+            }
+            return file;
+        }
+
+        /*
+         * Assigns the name to the first word of the entry. Only to be used from a
+         * safe context, for example from a memberlist, where the regex for some
+         * reason fails. Then just assign the name field of FTPFile.
+         */
+        if (entry != null && !entry.trim().isEmpty()) {
+            file.setRawListing(entry);
+            final String name = entry.split(" ")[0];
+            file.setName(name);
+            file.setType(FTPFile.FILE_TYPE);
+            return file;
+        }
+        return null;
+    }
+
+    /**
      * preParse is called as part of the interface. Per definition is is called
      * before the parsing takes place.
      * Three kind of lists is recognize:
@@ -509,13 +518,4 @@ public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
         isType = type;
     }
 
-    /*
-     * @return
-     */
-    @Override
-    protected FTPClientConfig getDefaultConfiguration() {
-        return new FTPClientConfig(FTPClientConfig.SYST_MVS,
-                DEFAULT_DATE_FORMAT, null);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java
index bcd5c24..d2200f2 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java
@@ -115,6 +115,20 @@ public class MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
     }
 
     /**
+     * Defines a default configuration to be used when this class is
+     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
+     * parameter being specified.
+     * @return the default configuration for this parser.
+     */
+    @Override
+    protected FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(
+                FTPClientConfig.SYST_UNIX,
+                DEFAULT_DATE_FORMAT,
+                DEFAULT_RECENT_DATE_FORMAT);
+    }
+
+    /**
      * Parses a line of a unix (standard) FTP server file listing and converts
      * it into a usable format in the form of an <code> FTPFile </code>
      * instance.  If the file listing line doesn't describe a file,
@@ -260,18 +274,4 @@ public class MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
         return null;
     }
 
-    /**
-     * Defines a default configuration to be used when this class is
-     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
-     * parameter being specified.
-     * @return the default configuration for this parser.
-     */
-    @Override
-    protected FTPClientConfig getDefaultConfiguration() {
-        return new FTPClientConfig(
-                FTPClientConfig.SYST_UNIX,
-                DEFAULT_DATE_FORMAT,
-                DEFAULT_RECENT_DATE_FORMAT);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
index 31570b6..3ce5d30 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
@@ -37,8 +37,6 @@ public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
     private static final String DEFAULT_DATE_FORMAT2
         = "MM-dd-yy kk:mm"; //11-09-01 18:30
 
-    private final FTPTimestampParser timestampParser;
-
     /**
      * this is the regular expression used by this parser.
      */
@@ -47,6 +45,8 @@ public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
         + "(?:(<DIR>)|([0-9]+))\\s+"    // <DIR> or ddddd; swallow trailing spaces
         + "(\\S.*)";                    // First non-space followed by rest of line (name)
 
+    private final FTPTimestampParser timestampParser;
+
     /**
      * The sole constructor for an NTFTPEntryParser object.
      *
@@ -86,6 +86,20 @@ public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
     }
 
     /**
+     * Defines a default configuration to be used when this class is
+     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
+     * parameter being specified.
+     * @return the default configuration for this parser.
+     */
+    @Override
+    public FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(
+                FTPClientConfig.SYST_NT,
+                DEFAULT_DATE_FORMAT,
+                null);
+    }
+
+    /**
      * Parses a line of an NT FTP server file listing and converts it into a
      * usable format in the form of an <code> FTPFile </code> instance.  If the
      * file listing line doesn't describe a file, <code> null </code> is
@@ -149,18 +163,4 @@ public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
         return null;
     }
 
-    /**
-     * Defines a default configuration to be used when this class is
-     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
-     * parameter being specified.
-     * @return the default configuration for this parser.
-     */
-    @Override
-    public FTPClientConfig getDefaultConfiguration() {
-        return new FTPClientConfig(
-                FTPClientConfig.SYST_NT,
-                DEFAULT_DATE_FORMAT,
-                null);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java
index 67f66a9..808ed2b 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java
@@ -84,6 +84,18 @@ public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
     }
 
     /**
+     * Defines a default configuration to be used when this class is
+     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
+     * parameter being specified.
+     * @return the default configuration for this parser.
+     */
+    @Override
+    protected FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(FTPClientConfig.SYST_NETWARE,
+                DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT);
+    }
+
+    /**
      * Parses a line of an NetwareFTP server file listing and converts it into a
      * usable format in the form of an <code> FTPFile </code> instance.  If the
      * file listing line doesn't describe a file, <code> null </code> is
@@ -161,16 +173,4 @@ public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
 
     }
 
-    /**
-     * Defines a default configuration to be used when this class is
-     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
-     * parameter being specified.
-     * @return the default configuration for this parser.
-     */
-    @Override
-    protected FTPClientConfig getDefaultConfiguration() {
-        return new FTPClientConfig(FTPClientConfig.SYST_NETWARE,
-                DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java
index 33dfc96..1cc6333 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java
@@ -74,6 +74,20 @@ public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
     }
 
     /**
+     * Defines a default configuration to be used when this class is
+     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
+     * parameter being specified.
+     * @return the default configuration for this parser.
+     */
+    @Override
+    protected FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(
+                FTPClientConfig.SYST_OS2,
+                DEFAULT_DATE_FORMAT,
+                null);
+    }
+
+    /**
      * Parses a line of an OS2 FTP server file listing and converts it into a
      * usable format in the form of an <code> FTPFile </code> instance.  If the
      * file listing line doesn't describe a file, <code> null </code> is
@@ -128,18 +142,4 @@ public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
 
     }
 
-    /**
-     * Defines a default configuration to be used when this class is
-     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
-     * parameter being specified.
-     * @return the default configuration for this parser.
-     */
-    @Override
-    protected FTPClientConfig getDefaultConfiguration() {
-        return new FTPClientConfig(
-                FTPClientConfig.SYST_OS2,
-                DEFAULT_DATE_FORMAT,
-                null);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
index 437ab0c..3e15035 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
@@ -278,6 +278,34 @@ public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
     }
 
 
+    /**
+     * Defines a default configuration to be used when this class is
+     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
+     * parameter being specified.
+     * @return the default configuration for this parser.
+     */
+    @Override
+    protected FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(
+                FTPClientConfig.SYST_OS400,
+                DEFAULT_DATE_FORMAT,
+                null);
+    }
+
+    /**
+     *
+     * @param string String value that is checked for <code>null</code>
+     * or empty.
+     * @return <code>true</code> for <code>null</code> or empty values,
+     * else <code>false</code>.
+     */
+    private boolean isNullOrEmpty(final String string) {
+        if (string == null || string.isEmpty()) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
... 29022 lines suppressed ...