Prepare for 3.1 RC1

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/net/tags/NET_3_1_RC1@1413589 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/NOTICE.txt b/NOTICE.txt
index 4b68a7d..bce27a7 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -3,4 +3,3 @@
 
 This product includes software developed by
 The Apache Software Foundation (http://www.apache.org/).
-
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 91c889d..552b758 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,46 +1,50 @@
+              Apache Commons Net
+                  Version 3.2
+                 RELEASE NOTES
 
-              Apache Commons Net 3.1 RELEASE NOTES
-
-The Commons Net team is pleased to announce the release of commons-net-3.1
+The Commons Net team is pleased to announce the release of Apache Commons Net 3.2
 
 Apache Commons Net library contains a collection of network utilities and protocol implementations.
 Supported protocols include: Echo, Finger, FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois
 
-This release fixes a few bugs and adds some new functionality (see below).
- It is binary compatible with previous releases
+This release fixes bugs and adds some new functionality (see below).
+It is binary compatible with previous releases.
 
 Changes in this version include:
 
 New features:
-o NET-440:  Allow user to provide default value in case SYST command fails. 
-o NET-346:  FTP should support reporting NATed external IP address Thanks to Kevin Samuel. 
-o NET-433:  NET site should link to the examples 
-o NET-426:  FTPS: Hook to customize _openDataConnection_ SSLSocket before startHandshake() is called Thanks to Ketan. 
-o NET-436:  Support for SYST "Mac OS" listing - "MACOS Peter's Server" Thanks to Jürgen Jung. 
+o NET-468:  Request for native support for socks proxy routing with Commons net FTP. Thanks to Bogdan Drozdowski.
+o NET-465:  FTPClient setSendBufferSize and setReceiveBufferSize on data socket. Thanks to Bogdan Drozdowski.
+o NET-462:  FTPClient in PASSIVE_LOCAL_DATA_CONNECTION_MODE cannot work when host have several different IP. Thanks to Bogdan Drozdowski.
+o           The examples can now be run using "java -jar commons-net-examples-m.n.jar".
+            This will automatically include the main net jar in the classpath.
+            See documentation.
+            FTPClientExample now supports "-A" for anonymous login
 
 Fixed Bugs:
-o NET-441:  mlistDir doc should be "MLSD" not "MSLD". Thanks to consiliens. 
-o NET-438:  POP3Client.capa() should call POP3Client.getAdditionalReply() Thanks to Norman Maurer. 
-o NET-412:  TFTP implementation subject to Sorcerer's Apprentice Syndrome Thanks to Chuck Wolber. 
-o NET-410:  TFTP does not handle RFC 783 retransmits Thanks to Chuck Wolber. 
-o NET-437:  TelnetInputStream doesn't support non-blocking IO when reader thread is not enabled Thanks to Gavin Camp. 
-o NET-422:  FTP using HTTP proxy not working Thanks to Tomas Mysik / Magnus Johansson. 
-o NET-423:  FTPClient.storeFIle might fail when ControlKeepAliveTimeout is set (ditto for FTPCLient.retrieveFile) Thanks to Jens Koch. 
-o NET-430:  Can't login to POP3S Server using explicit mode Thanks to Thomas Mathis. 
-o NET-434:  FTPClient fails to close local listener socket when command socket channel encounter "ReadTimeoutException" Thanks to zhangyong. 
-o NET-428:  SubnetUtils throws ArrayIndexOutOfBoundsException for new SubnetUtils( "1.2.3.4/32" ).getInfo().getAllAddresses() Thanks to sebb. 
-o NET-421:  Problem connecting to TLS/SSL SMTP server using explicit mode. Thanks to Oliver Saggau. 
-o NET-415:  typo in migration how-to. Thanks to george thomas. 
+o NET-46:   retrieveFileStream fails randomly or hangs
+o NET-485:  Remove unnecessary Base64 methods.
+o NET-484:  Base64.CHUNK_SEPARATOR should be private
+o NET-483:  Base64.encodeBase64(byte[], boolean, boolean, int) does not calculate output size correctly for unchunked output.
+o NET-466:  Regression: TelnetInputStream#available() blocks. Thanks to Martin Oberhuber.
+o NET-426:  FTPS: Hook to customize _openDataConnection_ SSLSocket before startHandshake() is called.
+            Implement _openDataConnection(String, String) method to properly
+            interface with FTPClient.openDataConnection(String, String) Thanks to Ketan.
+o NET-456:  TelnetClient hangs when reader-thread startup is delayed.
+o NET-449:  listFiles bug with folder that begins with "-". Clarify Javadoc.
+o NET-473:  FTPClient setSoTimeout (int time) will result in NullPointerException. Clarify Javadoc.
+o NET-475:  FtpClient sends REST when calling listFiles. Clarified Javadoc.
+o NET-467:  IMAPClient#fetch() does not handle literal strings.
+o NET-458:  MVSFTPEntryParser.parseSimpleEntry - ArrayIndexOutOfBoundsException. Thanks to Denis Molony.
+o NET-450:  Bug in documentation for FTPClient. Thanks to Roger Hardiman.
+o NET-442:  StringIndexOutOfBoundsException: String index out of range: -1 if server respond with root is current directory.
+o NET-444:  FTPTimestampParserImpl fails to parse future dates correctly on Feb 28th in a leap year.
 
 Changes:
-o NET-425:  _openDataConnection_, __storeFile, and __storeFileStream should be protected and take String for FTP command.
-            Likewise for receiveFile and receiveFileStream. Thanks to Steven Jardine. 
-o NET-416:  Increasing sub-negotiation message holder array size Thanks to Abhijeet Gaikwad. 
+o NET-482:  Support XOAUTH. Thanks to Houman Atashbar.
 
 
 For complete information on Commons Net, including instructions on how to submit bug reports,
 patches, or suggestions for improvement, see the Apache Commons Net website:
 
-http://commons.apache.org/net/
-
-
+http://commons.apache.org/net/
\ No newline at end of file
diff --git a/doap_net.rdf b/doap_net.rdf
index 6806f09..d5d97a3 100644
--- a/doap_net.rdf
+++ b/doap_net.rdf
@@ -36,6 +36,11 @@
     <release>
       <Version>
         <name>commons-net</name>
+        <created>2012-02-20</created>
+        <revision>3.1</revision>
+      </Version>
+      <Version>
+        <name>commons-net</name>
         <created>2011-06-06</created>
         <revision>3.0.1</revision>
       </Version>
diff --git a/findbugs-exclude-filter.xml b/findbugs-exclude-filter.xml
index 4c944b4..704f299 100644
--- a/findbugs-exclude-filter.xml
+++ b/findbugs-exclude-filter.xml
@@ -49,13 +49,6 @@
     <Bug code="BC" />
   </Match>
 
-  <!-- Switch fallthrough is deliberate -->
-  <Match>
-    <Class name="org.apache.commons.net.ftp.parser.UnixFTPEntryParser" />
-    <Method name="parseFTPEntry" />
-    <Bug code="SF" />
-  </Match>
-
   <!-- False positive - missing default -->
   <Match>
     <Class name="org.apache.commons.net.io.DotTerminatedMessageWriter" />
diff --git a/pom.xml b/pom.xml
index 5923c13..86422a0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,12 +22,12 @@
     <parent>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-parent</artifactId>
-        <version>23</version>
+        <version>27</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <groupId>commons-net</groupId>
     <artifactId>commons-net</artifactId>
-    <version>3.1</version>
+    <version>3.2</version>
     <name>Commons Net</name>
     <!-- N.B. the description content is deliberately not indented 
      ! to improve the layout of the Release Notes generated by mvn changes:announcement-generate 
@@ -120,12 +120,20 @@
         </dependency>
     </dependencies>
 
+    <distributionManagement>
+      <site>
+        <id>apache.website</id>
+        <name>Apache Commons Site</name>
+        <url>${commons.deployment.protocol}://people.apache.org/www/commons.apache.org/${commons.componentid}</url>
+      </site>
+    </distributionManagement>
+
     <properties>
         <maven.compile.source>1.5</maven.compile.source>
         <maven.compile.target>1.5</maven.compile.target>
         <commons.javadoc.java.link>http://download.oracle.com/javase/1.5.0/docs/api/</commons.javadoc.java.link>
         <commons.componentid>net</commons.componentid>
-        <commons.release.version>3.1</commons.release.version>
+        <commons.release.version>3.2</commons.release.version>
         <commons.rc.version>RC1</commons.rc.version>
         <commons.release.desc>(Requires Java 1.5 or later)</commons.release.desc>
         <commons.release.2.version>1.4.1</commons.release.2.version>
@@ -178,8 +186,8 @@
                 <artifactId>maven-assembly-plugin</artifactId>
                 <configuration>
                     <descriptors>
-                        <descriptor>src/assembly/bin.xml</descriptor>
-                        <descriptor>src/assembly/src.xml</descriptor>
+                        <descriptor>src/main/assembly/bin.xml</descriptor>
+                        <descriptor>src/main/assembly/src.xml</descriptor>
                     </descriptors>
                     <tarLongFileMode>gnu</tarLongFileMode>
                 </configuration>
@@ -223,6 +231,10 @@
                                         <attribute name="Implementation-Build" value="${implementation.build}"/>
                                         <attribute name="X-Compile-Source-JDK" value="${maven.compile.source}" />
                                         <attribute name="X-Compile-Target-JDK" value="${maven.compile.target}" />
+                                        <!-- Helper application -->
+                                        <attribute name="Main-Class" value="examples/Main" />
+                                        <!-- Allow java -jar examples.jar to work -->
+                                        <attribute name="Class-Path" value="commons-net-${project.version}.jar" />
                                     </manifest>
                                     <fileset dir="target/classes" includes="examples/**" />
                                 </jar>
@@ -239,7 +251,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
-                <version>1.5</version>
+                <version>1.7</version>
                 <executions>
                     <execution>
                         <id>attach-artifacts</id>
@@ -289,6 +301,9 @@
                     <resources>
                       <resource>
                         <directory>src/main/java/examples</directory>
+                        <excludes>
+                          <exclude>**/Main.java</exclude>
+                        </excludes>
                         <filtering>false</filtering>
                       </resource>
                     </resources>
@@ -315,15 +330,14 @@
                     <templateDirectory>src/changes</templateDirectory>
                     <!--  Add sample JIRA report - 'mvn changes:jira-report' or 'mvn site' -->
                     <onlyCurrentVersion>false</onlyCurrentVersion>
-                    <columnNames>Fix Version,Key,Summary,Type,Resolution,Status</columnNames>
+                    <columnNames>Fix Version,Key,Component,Summary,Type,Resolution,Status</columnNames>
                     <!-- Sort cols have to be reversed in JIRA 4 -->
                     <sortColumnNames>Key DESC,Type,Fix Version DESC</sortColumnNames>
                     <resolutionIds>Fixed</resolutionIds>
                     <statusIds>Resolved,Closed</statusIds>
                     <!-- Don't include sub-task -->
                     <typeIds>Bug,New Feature,Task,Improvement,Wish,Test</typeIds>
-                    <!-- TODO: update for next release -->
-                    <fixVersionIds>3.0.1,3.0</fixVersionIds>
+                    <fixVersionIds>${commons.release.version}</fixVersionIds>
                     <!-- The default is 100 -->
                     <maxEntries>100</maxEntries>
                 </configuration>
@@ -340,6 +354,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.5.2</version>
                 <configuration>
                     <excludeFilterFile>findbugs-exclude-filter.xml</excludeFilterFile>
                 </configuration>
@@ -348,9 +363,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>clirr-maven-plugin</artifactId>
-                <version>2.3</version>
                 <configuration>
-                    <minSeverity>info</minSeverity>
                     <excludes>
                       <exclude>examples/**</exclude>
                     </excludes>
@@ -368,29 +381,23 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>2.8</version>
+                <version>2.9.1</version>
                 <configuration>
                     <configLocation>${basedir}/checkstyle.xml</configLocation>
                     <suppressionsLocation>${basedir}/checkstyle-suppressions.xml</suppressionsLocation>
                     <enableRulesSummary>false</enableRulesSummary>
                 </configuration>
+                <!-- Workround for CHECKSTYLE-167/172 -->
+                <reportSets>
+                  <reportSet>
+                    <reports>
+                      <report>checkstyle</report>
+                    </reports>
+                  </reportSet>
+                </reportSets>
             </plugin>
 
         </plugins>
     </reporting>
 
-    <profiles>   
-      <profile>
-        <id>rc</id>
-        <distributionManagement>
-          <!-- Cannot define in parent ATM, see COMMONSSITE-26 -->
-          <site>
-            <id>apache.website</id>
-            <name>Apache Commons Release Candidate Staging Site</name>
-            <url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/builds/commons/${commons.componentid}/${commons.release.version}/${commons.rc.version}/site</url>
-          </site>
-        </distributionManagement>
-      </profile>
-    </profiles>
-
 </project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index fb7a813..a8b33a4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -62,15 +62,85 @@
      -->
 
     <body>
-        <release version="3.1" date="TBA" description="
+        <release version="3.2" date="TBA" description="
+This release fixes bugs and adds some new functionality (see below).
+ It is binary compatible with previous releases.
+        ">
+            <action issue="NET-46" dev="sebb" type="fix">
+            retrieveFileStream fails randomly or hangs
+            </action>
+            <action issue="NET-485" dev="sebb" type="fix">
+            Remove unnecessary Base64 methods.
+            </action>
+            <action issue="NET-482" dev="sebb" type="update" due-to="Houman Atashbar">
+            Support XOAUTH.
+            </action>
+            <action issue="NET-484" dev="sebb" type="fix">
+            Base64.CHUNK_SEPARATOR should be private
+            </action>
+            <action issue="NET-483" dev="sebb" type="fix">
+            Base64.encodeBase64(byte[], boolean, boolean, int) does not calculate output size correctly for unchunked output.
+            </action>
+            <action issue="NET-466" dev="sebb" type="fix" due-to="Martin Oberhuber">
+            Regression: TelnetInputStream#available() blocks.
+            </action>
+            <action issue="NET-426" dev="sebb" type="fix" due-to="Ketan">
+            FTPS: Hook to customize _openDataConnection_ SSLSocket before startHandshake() is called.
+            Implement _openDataConnection(String, String) method to properly
+            interface with FTPClient.openDataConnection(String, String)
+            </action>
+            <action issue="NET-456" dev="sebb" type="fix">
+            TelnetClient hangs when reader-thread startup is delayed.
+            </action>
+            <action issue="NET-449" dev="sebb" type="fix">
+            listFiles bug with folder that begins with "-". Clarify Javadoc.
+            </action>
+            <action issue="NET-473" dev="sebb" type="fix">
+            FTPClient setSoTimeout (int time) will result in NullPointerException. Clarify Javadoc.
+            </action>
+            <action issue="NET-468" dev="sebb" type="add" due-to="Bogdan Drozdowski">
+            Request for native support for socks proxy routing with Commons net FTP.
+            </action>
+            <action issue="NET-475" dev="sebb" type="fix">
+            FtpClient sends REST when calling listFiles. Clarified Javadoc.
+            </action>
+            <action issue="NET-465" dev="sebb" type="add" due-to="Bogdan Drozdowski">
+            FTPClient setSendBufferSize and setReceiveBufferSize on data socket.
+            </action>
+            <action issue="NET-462" dev="sebb" type="add" due-to="Bogdan Drozdowski">
+            FTPClient in PASSIVE_LOCAL_DATA_CONNECTION_MODE cannot work when host have several different IP.
+            </action>
+            <action issue="NET-467" dev="sebb" type="fix">
+            IMAPClient#fetch() does not handle literal strings.
+            </action>
+            <action issue="NET-458" dev="sebb" type="fix" due-to="Denis Molony">
+            MVSFTPEntryParser.parseSimpleEntry - ArrayIndexOutOfBoundsException.
+            </action>
+            <action issue="NET-450" dev="sebb" type="fix" due-to="Roger Hardiman">
+            Bug in documentation for FTPClient.
+            </action>
+            <action dev="sebb" type="add">
+            The examples can now be run using "java -jar commons-net-examples-m.n.jar".
+            This will automatically include the main net jar in the classpath.
+            See documentation.
+            FTPClientExample now supports "-A" for anonymous login
+            </action>
+            <action issue="NET-442" dev="sebb" type="fix">
+            StringIndexOutOfBoundsException: String index out of range: -1 if server respond with root is current directory.
+            </action>
+            <action issue="NET-444" dev="sebb" type="fix">
+            FTPTimestampParserImpl fails to parse future dates correctly on Feb 28th in a leap year.
+            </action>
+        </release>
+        <release version="3.1" date="Feb 20, 2012" description="
 This release fixes a few bugs and adds some new functionality (see below).
   It is binary compatible with previous releases
         ">
             <action issue="NET-441" dev="sebb" type="fix" due-to="consiliens">
-            mlistDir doc should be "MLSD" not "MSLD".
+            [FTP] mlistDir doc should be "MLSD" not "MSLD".
             </action>
             <action issue="NET-440" dev="sebb" type="add">
-            Allow user to provide default value in case SYST command fails.
+            [FTP] Allow user to provide default value in case SYST command fails.
             </action>
             <action issue="NET-438" dev="sebb" type="fix" due-to="Norman Maurer">
             POP3Client.capa() should call POP3Client.getAdditionalReply()
@@ -88,7 +158,7 @@
             FTP should support reporting NATed external IP address
             </action>
             <action issue="NET-433" dev="sebb" type="add">
-            NET site should link to the examples
+            Commons NET site should link to the examples
             </action>
             <action issue="NET-422" dev="sebb" type="fix" due-to="Tomas Mysik / Magnus Johansson">
             FTP using HTTP proxy not working
@@ -106,14 +176,14 @@
             FTPClient fails to close local listener socket when command socket channel encounter "ReadTimeoutException"
             </action>
             <action issue="NET-436" dev="sebb" type="add" due-to="Jürgen Jung">
-            Support for SYST "Mac OS" listing - "MACOS Peter's Server"
+            [FTP] Support for SYST "Mac OS" listing - "MACOS Peter's Server"
             </action>
             <action issue="NET-425" dev="sebb" type="update" due-to="Steven Jardine">
-            _openDataConnection_, __storeFile, and __storeFileStream should be protected and take String for FTP command.
+            [FTP] _openDataConnection_, __storeFile, and __storeFileStream should be protected and take String for FTP command.
             Likewise for receiveFile and receiveFileStream.
             </action>
             <action issue="NET-416" dev="sebb" type="update" due-to="Abhijeet Gaikwad">
-            Increasing sub-negotiation message holder array size
+            [Telnet] Increasing sub-negotiation message holder array size
             </action>
             <action issue="NET-428" dev="sebb" type="fix" due-to="sebb">
             SubnetUtils throws ArrayIndexOutOfBoundsException for new SubnetUtils( "1.2.3.4/32" ).getInfo().getAllAddresses()
@@ -122,7 +192,7 @@
             Problem connecting to TLS/SSL SMTP server using explicit mode.
             </action>
             <action issue="NET-415" dev="sebb" type="fix" due-to="george thomas">
-            typo in migration how-to.
+            [Site] typo in migration how-to.
             </action>
         </release>
 
diff --git a/src/changes/release-notes.vm b/src/changes/release-notes.vm
index ea718f2..311726b 100644
--- a/src/changes/release-notes.vm
+++ b/src/changes/release-notes.vm
@@ -14,119 +14,88 @@
 ## KIND, either express or implied.  See the License for the
 ## specific language governing permissions and limitations
 ## under the License.
+              Apache ${project.name}
+                  Version ${version}
+                 RELEASE NOTES
 
-              Apache ${project.name} ${version} RELEASE NOTES
-
-The ${developmentTeam} is pleased to announce the release of ${finalName}
+The ${developmentTeam} is pleased to announce the release of Apache ${project.name} ${version}
 
 $introduction.replaceAll("(?<!\015)\012", "
-")
+").replaceAll("(?m)^ +","")
 
 ## N.B. the available variables are described here:
 ## http://maven.apache.org/plugins/maven-changes-plugin/examples/using-a-custom-announcement-template.html
-##
 ## Hack to improve layout: replace all pairs of spaces with a single new-line
 $release.description.replaceAll("  ", "
 ")
 
+## set up indent sizes. Only change indent1
+#set($props=${project.properties})
+#set($jiralen=$props.get("commons.jira.id").length())
+## indent1 =   POOL-nnnn:
+#set($blanklen=$jiralen+6)## +6 for "-nnnn:"
+## must be at least as long as the longest JIRA id
+#set($blanks="                                  ")
+#set($indent1=$blanks.substring(0,$blanklen))
+## indent2 allows for issue wrapper
+#set($indent2="$indent1   ")
+##
+#macro ( processaction )
+## Use replaceAll to fix up LF-only line ends on Windows.
+#set($action=$actionItem.getAction().replaceAll("\n","
+"))
+## Fix up indentation for multi-line action descriptions
+#set($action=$action.replaceAll("(?m)^  +",$indent2))
+#if ($actionItem.getIssue())
+#set($issue="$actionItem.getIssue():")
+## Pad shorter issue numbers
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#else
+#set($issue=$indent1)
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=" Thanks to $actionItem.getDueTo().")
+#else
+#set($dueto="")
+#end
+o $issue ${action}$dueto
+#set($action="")
+#set($issue="")
+#set($dueto="")
+#end
+##
 #if ($release.getActions().size() == 0)
 No changes defined in this version.
 #else
 Changes in this version include:
 
-## indent to be used if there is no issue attribute.
-## should be the same as the indent in the changes.xml file
-## less 2 spaces for the 'o' and trailing space
-#set($indent='          ')
 #if ($release.getActions('add').size() !=0)
 New features:
 #foreach($actionItem in $release.getActions('add'))
-## Use replaceAll to fix up LF-only line ends on Windows.
-#set($action=$actionItem.getAction().replaceAll("\n","
-"))
-#if ($actionItem.getIssue())
-#set($issue=$actionItem.getIssue())
-#else
-#set($issue="")
-#end
-#if ($actionItem.getDueTo())
-#set($dueto=$actionItem.getDueTo())
-#else
-#set($dueto="")
-#end
-o#if($!issue != "") $issue: #else$indent#end ${action} #if($!dueto != "")Thanks to $dueto. #end
-
-#set($issue="")
-#set($dueto="")
+#processaction()
 #end 
 #end
 
 #if ($release.getActions('fix').size() !=0)
 Fixed Bugs:
 #foreach($actionItem in $release.getActions('fix'))
-## Use replaceAll to fix up LF-only line ends on Windows.
-#set($action=$actionItem.getAction().replaceAll("\n","
-"))
-#if ($actionItem.getIssue())
-#set($issue=$actionItem.getIssue())
-#else
-#set($issue="")
-#end
-#if ($actionItem.getDueTo())
-#set($dueto=$actionItem.getDueTo())
-#else
-#set($dueto="")
-#end
-o#if($!issue != "") $issue: #else$indent#end ${action} #if($!dueto != "")Thanks to $dueto. #end
-
-#set($issue="")
-#set($dueto="")
+#processaction()
 #end
 #end
 
 #if ($release.getActions('update').size() !=0)
 Changes:
 #foreach($actionItem in $release.getActions('update'))
-## Use replaceAll to fix up LF-only line ends on Windows.
-#set($action=$actionItem.getAction().replaceAll("\n","
-"))
-#if ($actionItem.getIssue())
-#set($issue=$actionItem.getIssue())
-#else
-#set($issue="")
-#end
-#if ($actionItem.getDueTo())
-#set($dueto=$actionItem.getDueTo())
-#else
-#set($dueto="")
-#end
-o#if($!issue != "") $issue: #else$indent#end ${action} #if($!dueto != "")Thanks to $dueto. #end
-
-#set($issue="")
-#set($dueto="")
+#processaction()
 #end
 #end
 
 #if ($release.getActions('remove').size() !=0)
 Removed:
 #foreach($actionItem in $release.getActions('remove'))
-## Use replaceAll to fix up LF-only line ends on Windows.
-#set($action=$actionItem.getAction().replaceAll("\n","
-"))
-#if ($actionItem.getIssue())
-#set($issue=$actionItem.getIssue())
-#else
-#set($issue="")
-#end
-#if ($actionItem.getDueTo())
-#set($dueto=$actionItem.getDueTo())
-#else
-#set($dueto="")
-#end
-o#if($!issue != "") $issue: #else$indent#end ${action} #if($!dueto != "")Thanks to $dueto. #end
-
-#set($issue="")
-#set($dueto="")
+#processaction()
 #end
 #end
 ## End of main loop
@@ -135,6 +104,4 @@
 For complete information on ${project.name}, including instructions on how to submit bug reports,
 patches, or suggestions for improvement, see the Apache ${project.name} website:
 
-${project.url}
-
-
+${project.url}
\ No newline at end of file
diff --git a/src/assembly/bin.xml b/src/main/assembly/bin.xml
similarity index 100%
rename from src/assembly/bin.xml
rename to src/main/assembly/bin.xml
diff --git a/src/assembly/src.xml b/src/main/assembly/src.xml
similarity index 96%
rename from src/assembly/src.xml
rename to src/main/assembly/src.xml
index 928baee..222dbbe 100644
--- a/src/assembly/src.xml
+++ b/src/main/assembly/src.xml
@@ -30,6 +30,7 @@
         <include>RELEASE-NOTES.txt</include>
         <include>pom.xml</include>
         <include>findbugs-exclude-filter.xml</include>
+        <include>checkstyle*.xml</include>
       </includes>
     </fileSet>
     <fileSet>
diff --git a/src/main/java/examples/Main.java b/src/main/java/examples/Main.java
new file mode 100644
index 0000000..fa197a4
--- /dev/null
+++ b/src/main/java/examples/Main.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+
+package examples;
+
+import java.lang.reflect.Method;
+import java.security.CodeSource;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+public class Main {
+
+    /**
+     * Helper application for example classes.
+     * Lists available classes, and provides shorthand invocation.
+     * For example:<br/>
+     * <code>java -jar commons-net-examples-m.n.jar FTPClientExample -l host user password</code>
+     * 
+     * @param args the first argument is used to name the class; remaining arguments
+     * are passed to the target class.
+     * @throws Exception 
+     * @throws Exception 
+     */
+    public static void main(String[] args) throws Exception  {
+        if (args.length==0) {
+            System.out.println("Usage: java -jar examples.jar <exampleClass> <exampleClass parameters>");
+        }
+        CodeSource codeSource = Main.class.getProtectionDomain().getCodeSource();
+        Map<String, String> map = new HashMap<String, String>();
+        if ( codeSource != null) {
+            final String sourceFile = codeSource.getLocation().getFile();
+            if (sourceFile.endsWith(".jar")) {
+                if (args.length==0) {
+                    System.out.println("\nClasses found in the jar:");
+                }
+                JarFile jf = new JarFile(sourceFile);
+                Enumeration<JarEntry> e = jf.entries();
+                while (e.hasMoreElements()) {
+                  JarEntry je = e.nextElement();
+                  String name = je.getName();
+                  if (!name.endsWith(".class") 
+                          || name.contains("$") // subclasses
+                          || name.equals("examples/nntp/NNTPUtils.class") // no main class
+                          || name.equals("examples/util/IOUtil.class") // no main class
+                          || name.equals("examples/Main.class")) {
+                      continue;
+                  }
+                  name = name.replace(".class", "");
+                  int lastSep = name.lastIndexOf('/');
+                  String alias = name.substring(lastSep+1);
+                  if (args.length==0) {
+                      System.out.printf("%-25s %s%n",alias,name);
+                  }
+                  map.put(alias, name);
+                }
+                jf.close();
+            }
+        }
+
+        if (args.length==0) {
+            return;
+        }
+
+        String shortName = args[0];
+        String fullName = map.get(shortName);
+        if (fullName == null) {
+            fullName = shortName;
+        }
+        fullName = fullName.replace('/', '.');
+        Class<?> clazz = Class.forName(fullName);
+        Method m = clazz.getDeclaredMethod("main", new Class[]{args.getClass()});
+        String[] args2 = new String[args.length-1];
+        System.arraycopy(args, 1, args2, 0, args2.length);
+        m.invoke(null, (Object)args2);
+    }
+}
diff --git a/src/main/java/examples/ftp/FTPClientExample.java b/src/main/java/examples/ftp/FTPClientExample.java
index f9fb799..318a344 100644
--- a/src/main/java/examples/ftp/FTPClientExample.java
+++ b/src/main/java/examples/ftp/FTPClientExample.java
@@ -23,6 +23,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 import org.apache.commons.net.PrintCommandListener;
 import org.apache.commons.net.ftp.FTP;
@@ -52,6 +54,7 @@
         "Usage: ftp [options] <hostname> <username> <password> [<remote file> [<local file>]]\n" +
         "\nDefault behavior is to download a file and use ASCII transfer mode.\n" +
         "\t-a - use local active mode (default is local passive)\n" +
+        "\t-A - anonymous login\n" +
         "\t-b - use binary transfer mode\n" +
         "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n" +
         "\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n" +
@@ -72,7 +75,7 @@
         "\t-PrP password - HTTP Proxy server password\n" +
         "\t-# - add hash display during transfers\n";
 
-    public static final void main(String[] args)
+    public static void main(String[] args) throws UnknownHostException
     {
         boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false;
         boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false;
@@ -88,6 +91,8 @@
         int proxyPort = 80;
         String proxyUser = null;
         String proxyPassword = null;
+        String username = null;
+        String password = null;
 
         int base = 0;
         for (base = 0; base < args.length; base++)
@@ -98,6 +103,10 @@
             else if (args[base].equals("-a")) {
                 localActive = true;
             }
+            else if (args[base].equals("-A")) {
+                username = "anonymous";
+                password = System.getProperty("user.name")+"@"+InetAddress.getLocalHost().getHostName();
+            }
             else if (args[base].equals("-b")) {
                 binaryTransfer = true;
             }
@@ -169,6 +178,9 @@
         }
 
         int remain = args.length - base;
+        if (username != null) {
+            minParams -= 2;
+        }
         if (remain < minParams) // server, user, pass, remote, local [protocol]
         {
             System.err.println(USAGE);
@@ -182,8 +194,10 @@
             server=parts[0];
             port=Integer.parseInt(parts[1]);
         }
-        String username = args[base++];
-        String password = args[base++];
+        if (username == null) {
+            username = args[base++];
+            password = args[base++];
+        }
 
         String remote = null;
         if (args.length - base > 0) {
diff --git a/src/main/java/examples/ftp/ServerToServerFTP.java b/src/main/java/examples/ftp/ServerToServerFTP.java
index 727bfbc..8fa29dd 100644
--- a/src/main/java/examples/ftp/ServerToServerFTP.java
+++ b/src/main/java/examples/ftp/ServerToServerFTP.java
@@ -40,7 +40,7 @@
 public final class ServerToServerFTP
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         String server1, username1, password1, file1;
         String server2, username2, password2, file2;
diff --git a/src/main/java/examples/ftp/TFTPExample.java b/src/main/java/examples/ftp/TFTPExample.java
index 1616b52..5dc0858 100644
--- a/src/main/java/examples/ftp/TFTPExample.java
+++ b/src/main/java/examples/ftp/TFTPExample.java
@@ -59,7 +59,7 @@
         "\t-a Use ASCII transfer mode\n" +
         "\t-b Use binary transfer mode\n";
 
-    public final static void main(String[] args)
+    public static void main(String[] args)
     {
         boolean receiveFile = true, closed;
         int transferMode = TFTP.BINARY_MODE, argc;
diff --git a/src/main/java/examples/mail/IMAPMail.java b/src/main/java/examples/mail/IMAPMail.java
index 86e05f4..4a0f219 100644
--- a/src/main/java/examples/mail/IMAPMail.java
+++ b/src/main/java/examples/mail/IMAPMail.java
@@ -34,7 +34,7 @@
 public final class IMAPMail
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         if (args.length < 3)
         {
diff --git a/src/main/java/examples/mail/POP3Mail.java b/src/main/java/examples/mail/POP3Mail.java
index 76dfaee..6b1800b 100644
--- a/src/main/java/examples/mail/POP3Mail.java
+++ b/src/main/java/examples/mail/POP3Mail.java
@@ -56,7 +56,7 @@
         System.out.println(Integer.toString(id) + " From: " + from + "  Subject: " + subject);
     }
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         if (args.length < 3)
         {
diff --git a/src/main/java/examples/mail/SMTPMail.java b/src/main/java/examples/mail/SMTPMail.java
index 82800d4..f68953d 100644
--- a/src/main/java/examples/mail/SMTPMail.java
+++ b/src/main/java/examples/mail/SMTPMail.java
@@ -43,7 +43,7 @@
 public final class SMTPMail
 {
 
-    public final static void main(String[] args)
+    public static void main(String[] args)
     {
         String sender, recipient, subject, filename, server, cc;
         List<String> ccList = new ArrayList<String>();
diff --git a/src/main/java/examples/nntp/ArticleReader.java b/src/main/java/examples/nntp/ArticleReader.java
new file mode 100644
index 0000000..0e7e227
--- /dev/null
+++ b/src/main/java/examples/nntp/ArticleReader.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package examples.nntp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.SocketException;
+
+import org.apache.commons.net.PrintCommandListener;
+import org.apache.commons.net.nntp.NNTPClient;
+import org.apache.commons.net.nntp.NewsgroupInfo;
+
+/**
+ * Sample program demonstrating the use of article header and body retrieval
+ */
+public class ArticleReader {
+
+    public static void main(String[] args) throws SocketException, IOException {
+
+        if (args.length != 2 && args.length != 3 && args.length != 5) {
+            System.out.println("Usage: MessageThreading <hostname> <groupname> [<article specifier> [<user> <password>]]");
+            return;
+        }
+
+        String hostname = args[0];
+        String newsgroup = args[1];
+        // Article specifier can be numeric or Id in form <m.n.o.x@host>
+        String articleSpec = args.length >= 3 ? args[2] : null;
+
+        NNTPClient client = new NNTPClient();
+        client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
+        client.connect(hostname);
+
+        if (args.length == 5) { // Optional auth
+            String user = args[3];
+            String password = args[4];
+            if(!client.authenticate(user, password)) {
+                System.out.println("Authentication failed for user " + user + "!");
+                System.exit(1);
+            }
+        }
+
+        NewsgroupInfo group = new NewsgroupInfo();
+        client.selectNewsgroup(newsgroup, group);
+
+        BufferedReader br;
+        String line;
+        if (articleSpec != null) {
+            br = (BufferedReader) client.retrieveArticleHeader(articleSpec);
+        } else {
+            long articleNum = group.getLastArticleLong();
+            br = client.retrieveArticleHeader(articleNum);
+        }
+        if (br != null) {
+            while((line=br.readLine()) != null) {
+                System.out.println(line);
+            }
+            br.close();
+        }
+        if (articleSpec != null) {
+            br = (BufferedReader) client.retrieveArticleBody(articleSpec);
+        } else {
+            long articleNum = group.getLastArticleLong();
+            br = client.retrieveArticleBody(articleNum);
+        }
+        if (br != null) {
+            while((line=br.readLine()) != null) {
+                System.out.println(line);
+            }
+            br.close();
+        }
+    }
+
+}
diff --git a/src/main/java/examples/nntp/ExtendedNNTPOps.java b/src/main/java/examples/nntp/ExtendedNNTPOps.java
index 5bcefc0..8686768 100644
--- a/src/main/java/examples/nntp/ExtendedNNTPOps.java
+++ b/src/main/java/examples/nntp/ExtendedNNTPOps.java
@@ -73,8 +73,9 @@
 
             // LIST ACTIVE
             NewsgroupInfo[] fanGroups = client.listNewsgroups("alt.fan.*");
-            for (int i = 0; i < fanGroups.length; ++i) {
-                System.out.println(fanGroups[i].getNewsgroup());
+            for (NewsgroupInfo fanGroup : fanGroups)
+            {
+                System.out.println(fanGroup.getNewsgroup());
             }
 
         } catch (IOException e) {
diff --git a/src/main/java/examples/nntp/ListNewsgroups.java b/src/main/java/examples/nntp/ListNewsgroups.java
index 764998a..20aab63 100644
--- a/src/main/java/examples/nntp/ListNewsgroups.java
+++ b/src/main/java/examples/nntp/ListNewsgroups.java
@@ -33,7 +33,7 @@
 public final class ListNewsgroups
 {
 
-    public final static void main(String[] args)
+    public static void main(String[] args)
     {
         if (args.length < 1)
         {
diff --git a/src/main/java/examples/nntp/PostMessage.java b/src/main/java/examples/nntp/PostMessage.java
index ba78e1c..1149976 100644
--- a/src/main/java/examples/nntp/PostMessage.java
+++ b/src/main/java/examples/nntp/PostMessage.java
@@ -42,7 +42,7 @@
 public final class PostMessage
 {
 
-    public final static void main(String[] args)
+    public static void main(String[] args)
     {
         String from, subject, newsgroup, filename, server, organization;
         String references;
diff --git a/src/main/java/examples/ntp/NTPClient.java b/src/main/java/examples/ntp/NTPClient.java
index 8d110a0..e906bc4 100644
--- a/src/main/java/examples/ntp/NTPClient.java
+++ b/src/main/java/examples/ntp/NTPClient.java
@@ -147,7 +147,7 @@
                 + ", clock offset(ms)=" + offset); // offset in ms
     }
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         if (args.length == 0) {
             System.err.println("Usage: NTPClient <hostname-or-address-list>");
@@ -159,11 +159,11 @@
         client.setDefaultTimeout(10000);
         try {
             client.open();
-            for (int i = 0; i < args.length; i++)
+            for (String arg : args)
             {
                 System.out.println();
                 try {
-                    InetAddress hostAddr = InetAddress.getByName(args[i]);
+                    InetAddress hostAddr = InetAddress.getByName(arg);
                     System.out.println("> " + hostAddr.getHostName() + "/" + hostAddr.getHostAddress());
                     TimeInfo info = client.getTime(hostAddr);
                     processResponse(info);
diff --git a/src/main/java/examples/ntp/TimeClient.java b/src/main/java/examples/ntp/TimeClient.java
index 30dede4..d104289 100644
--- a/src/main/java/examples/ntp/TimeClient.java
+++ b/src/main/java/examples/ntp/TimeClient.java
@@ -63,7 +63,7 @@
         client.close();
     }
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
 
         if (args.length == 1)
diff --git a/src/main/java/examples/unix/chargen.java b/src/main/java/examples/unix/chargen.java
index f67e490..cf71ca4 100644
--- a/src/main/java/examples/unix/chargen.java
+++ b/src/main/java/examples/unix/chargen.java
@@ -118,7 +118,7 @@
     }
 
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
 
         if (args.length == 1)
diff --git a/src/main/java/examples/unix/daytime.java b/src/main/java/examples/unix/daytime.java
index 32fdd59..1358442 100644
--- a/src/main/java/examples/unix/daytime.java
+++ b/src/main/java/examples/unix/daytime.java
@@ -61,7 +61,7 @@
     }
 
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
 
         if (args.length == 1)
diff --git a/src/main/java/examples/unix/echo.java b/src/main/java/examples/unix/echo.java
index 677d2d2..cffd16f 100644
--- a/src/main/java/examples/unix/echo.java
+++ b/src/main/java/examples/unix/echo.java
@@ -130,7 +130,7 @@
     }
 
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
 
         if (args.length == 1)
diff --git a/src/main/java/examples/unix/finger.java b/src/main/java/examples/unix/finger.java
index 74027ad..0ffcc73 100644
--- a/src/main/java/examples/unix/finger.java
+++ b/src/main/java/examples/unix/finger.java
@@ -36,7 +36,7 @@
 public final class finger
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         boolean longOutput = false;
         int arg = 0, index;
diff --git a/src/main/java/examples/unix/fwhois.java b/src/main/java/examples/unix/fwhois.java
index 2f55259..ebab700 100644
--- a/src/main/java/examples/unix/fwhois.java
+++ b/src/main/java/examples/unix/fwhois.java
@@ -31,7 +31,7 @@
 public final class fwhois
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         int index;
         String handle, host;
diff --git a/src/main/java/examples/unix/rdate.java b/src/main/java/examples/unix/rdate.java
index e743be2..d89ba1b 100644
--- a/src/main/java/examples/unix/rdate.java
+++ b/src/main/java/examples/unix/rdate.java
@@ -62,7 +62,7 @@
     }
 
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
 
         if (args.length == 1)
diff --git a/src/main/java/examples/unix/rexec.java b/src/main/java/examples/unix/rexec.java
index 3d34838..e5ed6fa 100644
--- a/src/main/java/examples/unix/rexec.java
+++ b/src/main/java/examples/unix/rexec.java
@@ -40,7 +40,7 @@
 public final class rexec
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         String server, username, password, command;
         RExecClient client;
diff --git a/src/main/java/examples/unix/rlogin.java b/src/main/java/examples/unix/rlogin.java
index 9315dbd..1adeb2d 100644
--- a/src/main/java/examples/unix/rlogin.java
+++ b/src/main/java/examples/unix/rlogin.java
@@ -51,7 +51,7 @@
 public final class rlogin
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         String server, localuser, remoteuser, terminal;
         RLoginClient client;
diff --git a/src/main/java/examples/unix/rshell.java b/src/main/java/examples/unix/rshell.java
index c625f4b..95112e2 100644
--- a/src/main/java/examples/unix/rshell.java
+++ b/src/main/java/examples/unix/rshell.java
@@ -44,7 +44,7 @@
 public final class rshell
 {
 
-    public static final void main(String[] args)
+    public static void main(String[] args)
     {
         String server, localuser, remoteuser, command;
         RCommandClient client;
diff --git a/src/main/java/org/apache/commons/net/DefaultSocketFactory.java b/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
index ee5b401..67f023b 100644
--- a/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
+++ b/src/main/java/org/apache/commons/net/DefaultSocketFactory.java
@@ -19,6 +19,8 @@
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.UnknownHostException;
@@ -40,6 +42,44 @@
 
 public class DefaultSocketFactory extends SocketFactory
 {
+    /** The proxy to use when creating new sockets. */
+    private final Proxy connProxy;
+
+    /**
+     * The default constructor.
+     */
+    public DefaultSocketFactory()
+    {
+        this(null);
+    }
+
+    /**
+     * A constructor for sockets with proxy support.
+     * 
+     * @param proxy The Proxy to use when creating new Sockets.
+     * @since 3.2
+     */
+    public DefaultSocketFactory(Proxy proxy)
+    {
+        connProxy = proxy;
+    }
+
+    /**
+     * Creates an unconnected Socket.
+     * 
+     * @return A new unconnected Socket.
+     * @exception IOException If an I/O error occurs while creating the Socket.
+     * @since 3.2
+     */
+    @Override
+    public Socket createSocket() throws IOException
+    {
+        if (connProxy != null)
+        {
+            return new Socket(connProxy);
+        }
+        return new Socket();
+    }
 
     /***
      * Creates a Socket connected to the given host and port.
@@ -54,6 +94,12 @@
     public Socket createSocket(String host, int port)
     throws UnknownHostException, IOException
     {
+        if (connProxy != null)
+        {
+            Socket s = new Socket(connProxy);
+            s.connect(new InetSocketAddress(host, port));
+            return s;
+        }
         return new Socket(host, port);
     }
 
@@ -69,6 +115,12 @@
     public Socket createSocket(InetAddress address, int port)
     throws IOException
     {
+        if (connProxy != null)
+        {
+            Socket s = new Socket(connProxy);
+            s.connect(new InetSocketAddress(address, port));
+            return s;
+        }
         return new Socket(address, port);
     }
 
@@ -89,6 +141,13 @@
                                InetAddress localAddr, int localPort)
     throws UnknownHostException, IOException
     {
+        if (connProxy != null)
+        {
+            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);
     }
 
@@ -108,6 +167,13 @@
                                InetAddress localAddr, int localPort)
     throws IOException
     {
+        if (connProxy != null)
+        {
+            Socket s = new Socket(connProxy);
+            s.bind(new InetSocketAddress(localAddr, localPort));
+            s.connect(new InetSocketAddress(address, port));
+            return s;
+        }
         return new Socket(address, port, localAddr, localPort);
     }
 
diff --git a/src/main/java/org/apache/commons/net/SocketClient.java b/src/main/java/org/apache/commons/net/SocketClient.java
index 65d8c13..766fc33 100644
--- a/src/main/java/org/apache/commons/net/SocketClient.java
+++ b/src/main/java/org/apache/commons/net/SocketClient.java
@@ -23,6 +23,7 @@
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.net.Socket;
 import java.net.SocketException;
 
@@ -103,6 +104,9 @@
     /** Hint for SO_SNDBUF size */
     private int sendBufferSize = -1;
 
+    /** The proxy to use when connecting. */
+    private Proxy connProxy;
+
     /**
      * Default constructor for SocketClient.  Initializes
      * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
@@ -446,9 +450,12 @@
      * 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.
      * @exception SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
     public void setSoTimeout(int timeout) throws SocketException
     {
@@ -501,6 +508,7 @@
      * <p>
      * @return The timeout in milliseconds of the currently opened socket.
      * @exception SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
     public int getSoTimeout() throws SocketException
     {
@@ -513,6 +521,7 @@
      * <p>
      * @param on  True if Nagle's algorithm is to be enabled, false if not.
      * @exception SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
     public void setTcpNoDelay(boolean on) throws SocketException
     {
@@ -527,6 +536,7 @@
      * @return True if Nagle's algorithm is enabled on the currently opened
      *        socket, false otherwise.
      * @exception SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
     public boolean getTcpNoDelay() throws SocketException
     {
@@ -542,6 +552,7 @@
      * other systems.
      * @param  keepAlive If true, keepAlive is turned on
      * @throws SocketException
+     * @throws NullPointerException if the socket is not currently open
      * @since 2.2
      */
     public void setKeepAlive(boolean keepAlive) throws SocketException {
@@ -553,6 +564,7 @@
      * Delegates to {@link Socket#getKeepAlive()}
      * @return True if SO_KEEPALIVE is enabled.
      * @throws SocketException
+     * @throws NullPointerException if the socket is not currently open
      * @since 2.2
      */
     public boolean getKeepAlive() throws SocketException {
@@ -565,6 +577,7 @@
      * @param on  True if linger is to be enabled, false if not.
      * @param val The linger timeout (in hundredths of a second?)
      * @exception SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
     public void setSoLinger(boolean on, int val) throws SocketException
     {
@@ -578,6 +591,7 @@
      * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
      *         -1.
      * @exception SocketException If the operation fails.
+     * @throws NullPointerException if the socket is not currently open
      */
     public int getSoLinger() throws SocketException
     {
@@ -592,6 +606,7 @@
      * <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
      */
     public int getLocalPort()
     {
@@ -604,6 +619,7 @@
      * 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()
     {
@@ -617,6 +633,7 @@
      * <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()
     {
@@ -627,6 +644,7 @@
     /**
      * @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 InetAddress getRemoteAddress()
     {
@@ -659,6 +677,7 @@
      * 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.
      */
@@ -669,6 +688,10 @@
         } 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;
     }
 
     /**
@@ -781,6 +804,27 @@
         return __commandSupport;
     }
 
+    /**
+     * 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
+     */
+    public void setProxy(Proxy proxy) {
+        setSocketFactory(new DefaultSocketFactory(proxy));
+        connProxy = proxy;
+    }
+
+    /**
+     * Gets the proxy for use with all the connections.
+     * @return the current proxy for connections.
+     */
+    public Proxy getProxy() {
+        return connProxy;
+    }
+
     /*
      *  N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility,
      *  so the abstract method is needed to pass the instance to the methods which were moved here.
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 507e948..570ab7d 100644
--- a/src/main/java/org/apache/commons/net/bsd/RCommandClient.java
+++ b/src/main/java/org/apache/commons/net/bsd/RCommandClient.java
@@ -135,7 +135,7 @@
             throw new BindException("All ports in use.");
         }
 
-        _output_.write(Integer.toString(server.getLocalPort()).getBytes());
+        _output_.write(Integer.toString(server.getLocalPort()).getBytes("UTF-8")); // $NON-NLS
         _output_.write('\0');
         _output_.flush();
 
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 0e94048..76d5ecd 100644
--- a/src/main/java/org/apache/commons/net/bsd/RExecClient.java
+++ b/src/main/java/org/apache/commons/net/bsd/RExecClient.java
@@ -89,7 +89,7 @@
 
         server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress());
 
-        _output_.write(Integer.toString(server.getLocalPort()).getBytes());
+        _output_.write(Integer.toString(server.getLocalPort()).getBytes("UTF-8")); // $NON-NLS-1$
         _output_.write('\0');
         _output_.flush();
 
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 42430a9..2e85151 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTP.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTP.java
@@ -212,6 +212,10 @@
      * with {@link #setControlEncoding setControlEncoding}.
      */
     public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1";
+
+    /** Length of the FTP reply code (3 alphanumerics) */
+    public static final int REPLY_CODE_LEN = 3;
+
     private static final String __modes = "AEILNTCFRPSBC";
 
     protected int _replyCode;
@@ -269,7 +273,7 @@
 
     // The RFC-compliant multiline termination check
     private boolean __strictCheck(String line, String code) {
-        return (!(line.startsWith(code) && line.charAt(3) == ' '));
+        return (!(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' '));
     }
 
     // The strict check is too strong a condition because of non-conforming ftp
@@ -278,7 +282,7 @@
     // test that the line starts with a digit rather than starting with
     // the code.
     private boolean __lenientCheck(String line) {
-        return (!(line.length() >= 4 && line.charAt(3) != '-' &&
+        return (!(line.length() > REPLY_CODE_LEN&& line.charAt(REPLY_CODE_LEN) != '-' &&
                 Character.isDigit(line.charAt(0))));
     }
 
@@ -317,7 +321,7 @@
         // In case we run into an anomaly we don't want fatal index exceptions
         // to be thrown.
         length = line.length();
-        if (length < 3) {
+        if (length < REPLY_CODE_LEN) {
             throw new MalformedServerReplyException(
                 "Truncated server reply: " + line);
         }
@@ -325,7 +329,7 @@
         String code = null;
         try
         {
-            code = line.substring(0, 3);
+            code = line.substring(0, REPLY_CODE_LEN);
             _replyCode = Integer.parseInt(code);
         }
         catch (NumberFormatException e)
@@ -337,7 +341,7 @@
         _replyLines.add(line);
 
         // Get extra lines if message continues.
-        if (length > 3 && line.charAt(3) == '-')
+        if (length > REPLY_CODE_LEN && line.charAt(REPLY_CODE_LEN) == '-')
         {
             do
             {
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPClient.java b/src/main/java/org/apache/commons/net/ftp/FTPClient.java
index a834458..fd2df13 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPClient.java
@@ -52,7 +52,7 @@
 import org.apache.commons.net.io.ToNetASCIIOutputStream;
 import org.apache.commons.net.io.Util;
 
-/***
+/**
  * FTPClient encapsulates all the functionality necessary to store and
  * retrieve files from an FTP server.  This class takes care of all
  * low level details of interacting with an FTP server and provides
@@ -66,6 +66,10 @@
  * Then you need to check the FTP reply code to see if the connection
  * was successful.  For example:
  * <pre>
+ *    FTPClient ftp = new FTPClient();
+ *    FTPClientConfig config = new FTPClientConfig();
+ *    config.setXXX(YYY); // change required options
+ *    ftp.configure(config );
  *    boolean error = false;
  *    try {
  *      int reply;
@@ -192,7 +196,7 @@
  *    FTPClient f = new FTPClient();
  *    f.connect(server);
  *    f.login(username, password);
- *    FTPFile[] files = listFiles(directory);
+ *    FTPFile[] files = f.listFiles(directory);
  * </pre>
  * <p>
  * Paged access, using a parser not accessible by auto-detect.  The class
@@ -268,7 +272,7 @@
  * @see FTPClientConfig
  *
  * @see org.apache.commons.net.MalformedServerReplyException
- **/
+ */
 public class FTPClient extends FTP
 implements Configurable
 {
@@ -304,48 +308,56 @@
      */
     public static final String SYSTEM_TYPE_PROPERTIES = "/systemType.properties";
 
-    /***
+    /**
      * A constant indicating the FTP session is expecting all transfers
      * to occur between the client (local) and server and that the server
      * should connect to the client's data port to initiate a data transfer.
      * This is the default data connection mode when and FTPClient instance
      * is created.
-     ***/
+     */
     public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0;
-    /***
+    /**
      * A constant indicating the FTP session is expecting all transfers
      * to occur between two remote servers and that the server
      * the client is connected to should connect to the other server's
      * data port to initiate a data transfer.
-     ***/
+     */
     public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1;
-    /***
+    /**
      * A constant indicating the FTP session is expecting all transfers
      * to occur between the client (local) and server and that the server
      * is in passive mode, requiring the client to connect to the
      * server's data port to initiate a transfer.
-     ***/
+     */
     public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2;
-    /***
+    /**
      * A constant indicating the FTP session is expecting all transfers
      * to occur between two remote servers and that the server
      * the client is connected to is in passive mode, requiring the other
      * server to connect to the first server's data port to initiate a data
      * transfer.
-     ***/
+     */
     public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3;
 
-    private int __dataConnectionMode, __dataTimeout;
+    private int __dataConnectionMode;
+    private int __dataTimeout;
     private int __passivePort;
     private String __passiveHost;
     private final Random __random;
-    private int __activeMinPort, __activeMaxPort;
+    private int __activeMinPort;
+    private int __activeMaxPort;
     private InetAddress __activeExternalHost;
     private InetAddress __reportActiveExternalHost; // overrides __activeExternalHost in EPRT/PORT commands
+    /** The address to bind to on passive connections, if necessary. */
+    private InetAddress __passiveLocalHost;
 
     private int __fileType;
     @SuppressWarnings("unused") // fields are written, but currently not read
-    private int __fileFormat, __fileStructure, __fileTransferMode;
+    private int __fileFormat;
+    @SuppressWarnings("unused") // field is written, but currently not read
+    private int __fileStructure;
+    @SuppressWarnings("unused") // field is written, but currently not read
+    private int __fileTransferMode;
     private boolean __remoteVerificationEnabled;
     private long __restartOffset;
     private FTPFileEntryParserFactory __parserFactory;
@@ -434,7 +446,7 @@
      * config.setLenientFutureDates(true);
      * ftp.configure(config );
      * </pre>
-     **/
+     */
     public FTPClient()
     {
         __initDefaults();
@@ -445,6 +457,7 @@
         __listHiddenFiles = false;
         __useEPSVwithIPv4 = false;
         __random = new Random();
+        __passiveLocalHost   = null;
     }
 
 
@@ -469,15 +482,35 @@
         __featuresMap = null;
     }
 
-    private String __parsePathname(String reply)
+    /**
+     * Parse the pathname from a CWD reply.
+     * According to RFC959 (http://www.ietf.org/rfc/rfc959.txt), 
+     * it should be the same as for MKD, i.e.
+     * 257<space>"<directory-name>"<space><commentary>
+     * where any embedded double-quotes are doubled.
+     * 
+     * However, see NET-442 for an exception.
+     * 
+     * @param reply
+     * @return
+     */
+    private static String __parsePathname(String reply)
     {
-        int begin = reply.indexOf('"') + 1;
-        int end = reply.indexOf('"', begin);
-
-        return reply.substring(begin, end);
+        int begin = reply.indexOf('"'); // find first double quote
+        if (begin == -1) { // not found, return all after reply code and space
+            return reply.substring(REPLY_CODE_LEN + 1);
+        }
+        int end = reply.lastIndexOf("\" "); // N.B. assume commentary does not contain double-quote
+        if (end != -1 ){ // found end of quoted string, de-duplicate any embedded quotes
+            return reply.substring(begin+1, end).replace("\"\"", "\"");            
+        }
+        // malformed reply, return all after reply code and space
+        return reply.substring(REPLY_CODE_LEN + 1);
     }
 
-
+    /**
+     * @since 3.1
+     */
     protected void _parsePassiveModeReply(String reply)
     throws MalformedServerReplyException
     {
@@ -504,11 +537,14 @@
         try {
             InetAddress host = InetAddress.getByName(__passiveHost);
             // reply is a local address, but target is not - assume NAT box changed the PASV reply
-            if (host.isSiteLocalAddress() && !getRemoteAddress().isSiteLocalAddress()){
-                String hostAddress = getRemoteAddress().getHostAddress();
-                fireReplyReceived(0,
-                            "[Replacing site local address "+__passiveHost+" with "+hostAddress+"]\n");
-                __passiveHost = hostAddress;
+            if (host.isSiteLocalAddress()) {
+                InetAddress remote = getRemoteAddress();
+                if (!remote.isSiteLocalAddress()){ 
+                    String hostAddress = remote.getHostAddress();
+                    fireReplyReceived(0,
+                                "[Replacing site local address "+__passiveHost+" with "+hostAddress+"]\n");
+                    __passiveHost = hostAddress;                    
+                }
             }
         } catch (UnknownHostException e) { // Should not happen as we are passing in an IP address
             throw new MalformedServerReplyException(
@@ -557,19 +593,25 @@
         return _storeFile(FTPCommand.getCommand(command), remote, local);
     }
     
+    /**
+     * @since 3.1
+     */
     protected boolean _storeFile(String command, String remote, InputStream local)
     throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(command, remote);
 
-        if ((socket = _openDataConnection_(command, remote)) == null) {
+        if (socket == null) {
             return false;
         }
 
-        OutputStream output = new BufferedOutputStream(socket.getOutputStream(), getBufferSize());
+        OutputStream output;
 
         if (__fileType == ASCII_FILE_TYPE) {
-            output = new ToNetASCIIOutputStream(output);
+            output = new ToNetASCIIOutputStream(
+                     new BufferedOutputStream(socket.getOutputStream(), getDefaultedBufferSize()));
+        } else {
+            output = new BufferedOutputStream(socket.getOutputStream(), getDefaultedBufferSize());
         }
 
         CSL csl = null;
@@ -580,7 +622,7 @@
         // Treat everything else as binary for now
         try
         {
-            Util.copyStream(local, output, getBufferSize(),
+            Util.copyStream(local, output, getDefaultedBufferSize(),
                     CopyStreamEvent.UNKNOWN_STREAM_SIZE, __mergeListeners(csl),
                     false);
         }
@@ -606,12 +648,15 @@
         return _storeFileStream(FTPCommand.getCommand(command), remote);
     }
 
+    /**
+     * @since 3.1
+     */
     protected OutputStream _storeFileStream(String command, String remote)
     throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(command, remote);
 
-        if ((socket = _openDataConnection_(command, remote)) == null) {
+        if (socket == null) {
             return null;
         }
 
@@ -625,7 +670,7 @@
             // own if they want to wrap the SocketOutputStream we return
             // for file types other than ASCII.
             output = new BufferedOutputStream(output,
-                    getBufferSize());
+                    getDefaultedBufferSize());
             output = new ToNetASCIIOutputStream(output);
 
         }
@@ -674,6 +719,7 @@
      *         the connection.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
+     * @since 3.1
      */
     protected Socket _openDataConnection_(String command, String arg)
     throws IOException
@@ -727,6 +773,11 @@
                     server.setSoTimeout(__dataTimeout);
                 }
                 socket = server.accept();
+                
+                // Ensure the timeout is set before any commands are issued on the new socket
+                if (__dataTimeout >= 0) {
+                    socket.setSoTimeout(__dataTimeout);
+                }
             } finally {
                 server.close();
             }
@@ -759,6 +810,18 @@
             }
 
             socket = _socketFactory_.createSocket();
+            if (__passiveLocalHost != null) {
+                socket.bind(new InetSocketAddress(__passiveLocalHost, 0));
+            }
+
+            // For now, let's just use the data timeout value for waiting for
+            // the data connection.  It may be desirable to let this be a
+            // separately configurable value.  In any case, we really want
+            // to allow preventing the accept from blocking indefinitely.
+            if (__dataTimeout >= 0) {
+                socket.setSoTimeout(__dataTimeout);
+            }
+
             socket.connect(new InetSocketAddress(__passiveHost, __passivePort), connectTimeout);
             if ((__restartOffset > 0) && !restart(__restartOffset))
             {
@@ -782,8 +845,9 @@
                     " is not same as server " + getRemoteAddress().getHostAddress());
         }
 
-        if (__dataTimeout >= 0) {
-            socket.setSoTimeout(__dataTimeout);
+        if ( __bufferSize > 0 ) {
+            socket.setReceiveBufferSize(__bufferSize);
+            socket.setSendBufferSize(__bufferSize);
         }
 
         return socket;
@@ -817,14 +881,16 @@
     }
 
 
-    /***
+    /**
      * Sets the timeout in milliseconds to use when reading from the
      * data connection.  This timeout will be set immediately after
-     * opening the data connection.
+     * opening the data connection, provided that the value is &ge; 0.
      * <p>
+     * <b>Note:</b> the timeout will also be applied when calling accept()
+     * whilst establishing an active local data connection.
      * @param  timeout The default timeout in milliseconds that is used when
-     *        opening a data connection socket.
-     ***/
+     *        opening a data connection socket. The value 0 means an infinite timeout.
+     */
     public void setDataTimeout(int timeout)
     {
         __dataTimeout = timeout;
@@ -844,12 +910,12 @@
     }
 
 
-    /***
+    /**
      * Closes the connection to the FTP server and restores
      * connection parameters to the default values.
      * <p>
      * @exception IOException If an error occurs while disconnecting.
-     ***/
+     */
     @Override
     public void disconnect() throws IOException
     {
@@ -858,7 +924,7 @@
     }
 
 
-    /***
+    /**
      * Enable or disable verification that the remote host taking part
      * of a data connection is the same as the host to which the control
      * connection is attached.  The default is for verification to be
@@ -866,25 +932,25 @@
      * FTPClient is currently connected or not.
      * <p>
      * @param enable True to enable verification, false to disable verification.
-     ***/
+     */
     public void setRemoteVerificationEnabled(boolean enable)
     {
         __remoteVerificationEnabled = enable;
     }
 
-    /***
+    /**
      * Return whether or not verification of the remote host participating
      * in data connections is enabled.  The default behavior is for
      * verification to be enabled.
      * <p>
      * @return True if verification is enabled, false if not.
-     ***/
+     */
     public boolean isRemoteVerificationEnabled()
     {
         return __remoteVerificationEnabled;
     }
 
-    /***
+    /**
      * Login to the FTP server using the provided username and password.
      * <p>
      * @param username The username to login under.
@@ -897,7 +963,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean login(String username, String password) throws IOException
     {
 
@@ -917,7 +983,7 @@
     }
 
 
-    /***
+    /**
      * Login to the FTP server using the provided username, password,
      * and account.  If no account is required by the server, only
      * the username and password, the account information is not used.
@@ -933,7 +999,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean login(String username, String password, String account)
     throws IOException
     {
@@ -962,7 +1028,7 @@
         return FTPReply.isPositiveCompletion(acct(account));
     }
 
-    /***
+    /**
      * Logout of the FTP server by sending the QUIT command.
      * <p>
      * @return True if successfully completed, false if not.
@@ -973,14 +1039,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean logout() throws IOException
     {
         return FTPReply.isPositiveCompletion(quit());
     }
 
 
-    /***
+    /**
      * Change the current working directory of the FTP session.
      * <p>
      * @param pathname  The new current working directory.
@@ -992,14 +1058,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean changeWorkingDirectory(String pathname) throws IOException
     {
         return FTPReply.isPositiveCompletion(cwd(pathname));
     }
 
 
-    /***
+    /**
      * Change to the parent directory of the current working directory.
      * <p>
      * @return True if successfully completed, false if not.
@@ -1010,14 +1076,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean changeToParentDirectory() throws IOException
     {
         return FTPReply.isPositiveCompletion(cdup());
     }
 
 
-    /***
+    /**
      * Issue the FTP SMNT command.
      * <p>
      * @param pathname The pathname to mount.
@@ -1029,13 +1095,13 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean structureMount(String pathname) throws IOException
     {
         return FTPReply.isPositiveCompletion(smnt(pathname));
     }
 
-    /***
+    /**
      * Reinitialize the FTP session.  Not all FTP servers support this
      * command, which issues the FTP REIN command.
      * <p>
@@ -1047,7 +1113,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     boolean reinitialize() throws IOException
     {
         rein();
@@ -1066,7 +1132,7 @@
     }
 
 
-    /***
+    /**
      * Set the current data connection mode to
      * <code>ACTIVE_LOCAL_DATA_CONNECTION_MODE</code>.  No communication
      * with the FTP server is conducted, but this causes all future data
@@ -1074,7 +1140,7 @@
      * data port.  Additionally, to accommodate differences between socket
      * implementations on different platforms, this method causes the
      * client to issue a PORT command before every data transfer.
-     ***/
+     */
     public void enterLocalActiveMode()
     {
         __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
@@ -1083,7 +1149,7 @@
     }
 
 
-    /***
+    /**
      * Set the current data connection mode to
      * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code>.  Use this
      * method only for data transfers between the client and server.
@@ -1097,7 +1163,7 @@
      * <p>
      * <b>N.B.</b> currently calling any connect method will reset the mode to
      * ACTIVE_LOCAL_DATA_CONNECTION_MODE.
-     ***/
+     */
     public void enterLocalPassiveMode()
     {
         __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE;
@@ -1108,7 +1174,7 @@
     }
 
 
-    /***
+    /**
      * Set the current data connection mode to
      * <code> ACTIVE_REMOTE_DATA_CONNECTION </code>.  Use this method only
      * for server to server data transfers.  This method issues a PORT
@@ -1131,7 +1197,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean enterRemoteActiveMode(InetAddress host, int port)
     throws IOException
     {
@@ -1145,7 +1211,7 @@
         return false;
     }
 
-    /***
+    /**
      * Set the current data connection mode to
      * <code> PASSIVE_REMOTE_DATA_CONNECTION_MODE </code>.  Use this
      * method only for server to server data transfers.
@@ -1166,7 +1232,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean enterRemotePassiveMode() throws IOException
     {
         if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
@@ -1179,7 +1245,7 @@
         return true;
     }
 
-    /***
+    /**
      * Returns the hostname or IP address (in the form of a string) returned
      * by the server when entering passive mode.  If not in passive mode,
      * returns null.  This method only returns a valid value AFTER a
@@ -1190,13 +1256,13 @@
      * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
      * <p>
      * @return The passive host name if in passive mode, otherwise null.
-     ***/
+     */
     public String getPassiveHost()
     {
         return __passiveHost;
     }
 
-    /***
+    /**
      * If in passive mode, returns the data port of the passive host.
      * This method only returns a valid value AFTER a
      * data connection has been opened after a call to
@@ -1207,20 +1273,20 @@
      * <p>
      * @return The data port of the passive server.  If not in passive
      *         mode, undefined.
-     ***/
+     */
     public int getPassivePort()
     {
         return __passivePort;
     }
 
 
-    /***
+    /**
      * Returns the current data connection mode (one of the
      * <code> _DATA_CONNECTION_MODE </code> constants.
      * <p>
      * @return The current data connection mode (one of the
      * <code> _DATA_CONNECTION_MODE </code> constants.
-     ***/
+     */
     public int getDataConnectionMode()
     {
         return __dataConnectionMode;
@@ -1283,33 +1349,67 @@
         }
     }
 
-    /***
+    /**
      * Set the client side port range in active mode.
      * <p>
      * @param minPort The lowest available port (inclusive).
      * @param maxPort The highest available port (inclusive).
      * @since 2.2
-     ***/
+     */
     public void setActivePortRange(int minPort, int maxPort)
     {
         this.__activeMinPort = minPort;
         this.__activeMaxPort = maxPort;
     }
 
-    /***
+    /**
      * Set the external IP address in active mode.
      * Useful when there are multiple network cards.
      * <p>
      * @param ipAddress The external IP address of this machine.
      * @throws UnknownHostException if the ipAddress cannot be resolved
      * @since 2.2
-     ***/
+     */
     public void setActiveExternalIPAddress(String ipAddress) throws UnknownHostException
     {
         this.__activeExternalHost = InetAddress.getByName(ipAddress);
     }
 
     /**
+     * Set the local IP address to use in passive mode.
+     * Useful when there are multiple network cards.
+     * <p>
+     * @param ipAddress The local IP address of this machine.
+     * @throws UnknownHostException if the ipAddress cannot be resolved
+     */
+    public void setPassiveLocalIPAddress(String ipAddress) throws UnknownHostException
+    {
+        this.__passiveLocalHost = InetAddress.getByName(ipAddress);
+    }
+
+    /**
+     * Set the local IP address to use in passive mode.
+     * Useful when there are multiple network cards.
+     * <p>
+     * @param inetAddress The local IP address of this machine.
+     */
+    public void setPassiveLocalIPAddress(InetAddress inetAddress)
+    {
+        this.__passiveLocalHost = inetAddress;
+    }
+
+    /**
+     * Set the local IP address in passive mode.
+     * Useful when there are multiple network cards.
+     * <p>
+     * @return The local IP address in passive mode.
+     */
+    public InetAddress getPassiveLocalIPAddress()
+    {
+        return this.__passiveLocalHost;
+    }
+
+    /**
      * Set the external IP address to report in EPRT/PORT commands in active mode.
      * Useful when there are multiple network cards.
      * <p>
@@ -1324,7 +1424,7 @@
     }
 
 
-    /***
+    /**
      * Sets the file type to be transferred.  This should be one of
      * <code> FTP.ASCII_FILE_TYPE </code>, <code> FTP.BINARY_FILE_TYPE</code>,
      * etc.  The file type only needs to be set when you want to change the
@@ -1344,7 +1444,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean setFileType(int fileType) throws IOException
     {
         if (FTPReply.isPositiveCompletion(type(fileType)))
@@ -1357,7 +1457,7 @@
     }
 
 
-    /***
+    /**
      * Sets the file type to be transferred and the format.  The type should be
      * one of  <code> FTP.ASCII_FILE_TYPE </code>,
      * <code> FTP.BINARY_FILE_TYPE </code>, etc.  The file type only needs to
@@ -1387,7 +1487,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean setFileType(int fileType, int formatOrByteSize)
     throws IOException
     {
@@ -1401,7 +1501,7 @@
     }
 
 
-    /***
+    /**
      * Sets the file structure.  The default structure is
      * <code> FTP.FILE_STRUCTURE </code> if this method is never called.
      * <p>
@@ -1415,7 +1515,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean setFileStructure(int structure) throws IOException
     {
         if (FTPReply.isPositiveCompletion(stru(structure)))
@@ -1427,7 +1527,7 @@
     }
 
 
-    /***
+    /**
      * Sets the transfer mode.  The default transfer mode
      * <code> FTP.STREAM_TRANSFER_MODE </code> if this method is never called.
      * <p>
@@ -1441,7 +1541,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean setFileTransferMode(int mode) throws IOException
     {
         if (FTPReply.isPositiveCompletion(mode(mode)))
@@ -1453,7 +1553,7 @@
     }
 
 
-    /***
+    /**
      * Initiate a server to server file transfer.  This method tells the
      * server to which the client is connected to retrieve a given file from
      * the other server.
@@ -1467,7 +1567,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean remoteRetrieve(String filename) throws IOException
     {
         if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
@@ -1478,7 +1578,7 @@
     }
 
 
-    /***
+    /**
      * Initiate a server to server file transfer.  This method tells the
      * server to which the client is connected to store a file on
      * the other server using the given filename.  The other server must
@@ -1494,7 +1594,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean remoteStore(String filename) throws IOException
     {
         if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
@@ -1505,7 +1605,7 @@
     }
 
 
-    /***
+    /**
      * Initiate a server to server file transfer.  This method tells the
      * server to which the client is connected to store a file on
      * the other server using a unique filename based on the given filename.
@@ -1522,7 +1622,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean remoteStoreUnique(String filename) throws IOException
     {
         if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
@@ -1533,7 +1633,7 @@
     }
 
 
-    /***
+    /**
      * Initiate a server to server file transfer.  This method tells the
      * server to which the client is connected to store a file on
      * the other server using a unique filename.
@@ -1550,7 +1650,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean remoteStoreUnique() throws IOException
     {
         if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
@@ -1561,7 +1661,7 @@
     }
 
     // For server to server transfers
-    /***
+    /**
      * Initiate a server to server file transfer.  This method tells the
      * server to which the client is connected to append to a given file on
      * the other server.  The other server must have had a
@@ -1578,7 +1678,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean remoteAppend(String filename) throws IOException
     {
         if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
@@ -1588,7 +1688,7 @@
         return false;
     }
 
-    /***
+    /**
      * There are a few FTPClient methods that do not complete the
      * entire sequence of FTP commands to complete a transaction.  These
      * commands require some action by the programmer after the reception
@@ -1631,14 +1731,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean completePendingCommand() throws IOException
     {
         return FTPReply.isPositiveCompletion(getReply());
     }
 
 
-    /***
+    /**
      * Retrieves a named file from the server and writes it to the given
      * OutputStream.  This method does NOT close the given OutputStream.
      * If the current file type is ASCII, line separators in the file are
@@ -1662,26 +1762,31 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean retrieveFile(String remote, OutputStream local)
     throws IOException
     {
         return _retrieveFile(FTPCommand.getCommand(FTPCommand.RETR), remote, local);
     }
 
+    /**
+     * @since 3.1
+     */
     protected boolean _retrieveFile(String command, String remote, OutputStream local)
     throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(command, remote);
 
-        if ((socket = _openDataConnection_(command, remote)) == null) {
+        if (socket == null) {
             return false;
         }
 
-        InputStream input = new BufferedInputStream(socket.getInputStream(),
-                getBufferSize());
+        InputStream input;
         if (__fileType == ASCII_FILE_TYPE) {
-            input = new FromNetASCIIInputStream(input);
+            input = new FromNetASCIIInputStream(
+                    new BufferedInputStream(socket.getInputStream(), getDefaultedBufferSize()));
+        } else {
+            input = new BufferedInputStream(socket.getInputStream(), getDefaultedBufferSize());
         }
 
         CSL csl = null;
@@ -1692,7 +1797,7 @@
         // Treat everything else as binary for now
         try
         {
-            Util.copyStream(input, local, getBufferSize(),
+            Util.copyStream(input, local, getDefaultedBufferSize(),
                     CopyStreamEvent.UNKNOWN_STREAM_SIZE, __mergeListeners(csl),
                     false);
         } finally {
@@ -1707,7 +1812,7 @@
         return ok;
     }
 
-    /***
+    /**
      * Returns an InputStream from which a named file from the server
      * can be read.  If the current file type is ASCII, the returned
      * InputStream will convert line separators in the file to
@@ -1733,18 +1838,21 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public InputStream retrieveFileStream(String remote) throws IOException
     {
         return _retrieveFileStream(FTPCommand.getCommand(FTPCommand.RETR), remote);
     }
 
+    /**
+     * @since 3.1
+     */
     protected InputStream _retrieveFileStream(String command, String remote)
     throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(command, remote);
 
-        if ((socket = _openDataConnection_(command, remote)) == null) {
+        if (socket == null) {
             return null;
         }
 
@@ -1758,14 +1866,14 @@
             // own if they want to wrap the SocketInputStream we return
             // for file types other than ASCII.
             input = new BufferedInputStream(input,
-                    getBufferSize());
+                    getDefaultedBufferSize());
             input = new FromNetASCIIInputStream(input);
         }
         return new org.apache.commons.net.io.SocketInputStream(socket, input);
     }
 
 
-    /***
+    /**
      * Stores a file on the server using the given name and taking input
      * from the given InputStream.  This method does NOT close the given
      * InputStream.  If the current file type is ASCII, line separators in
@@ -1788,7 +1896,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean storeFile(String remote, InputStream local)
     throws IOException
     {
@@ -1796,7 +1904,7 @@
     }
 
 
-    /***
+    /**
      * Returns an OutputStream through which data can be written to store
      * a file on the server using the given name.  If the current file type
      * is ASCII, the returned OutputStream will convert line separators in
@@ -1820,13 +1928,13 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public OutputStream storeFileStream(String remote) throws IOException
     {
         return __storeFileStream(FTPCommand.STOR, remote);
     }
 
-    /***
+    /**
      * Appends to a file on the server with the given name, taking input
      * from the given InputStream.  This method does NOT close the given
      * InputStream.  If the current file type is ASCII, line separators in
@@ -1850,14 +1958,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean appendFile(String remote, InputStream local)
     throws IOException
     {
         return __storeFile(FTPCommand.APPE, remote, local);
     }
 
-    /***
+    /**
      * Returns an OutputStream through which data can be written to append
      * to a file on the server with the given name.  If the current file type
      * is ASCII, the returned OutputStream will convert line separators in
@@ -1881,13 +1989,13 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public OutputStream appendFileStream(String remote) throws IOException
     {
         return __storeFileStream(FTPCommand.APPE, remote);
     }
 
-    /***
+    /**
      * Stores a file on the server using a unique name derived from the
      * given name and taking input
      * from the given InputStream.  This method does NOT close the given
@@ -1912,7 +2020,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean storeUniqueFile(String remote, InputStream local)
     throws IOException
     {
@@ -1920,7 +2028,7 @@
     }
 
 
-    /***
+    /**
      * Returns an OutputStream through which data can be written to store
      * a file on the server using a unique name derived from the given name.
      * If the current file type
@@ -1946,7 +2054,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public OutputStream storeUniqueFileStream(String remote) throws IOException
     {
         return __storeFileStream(FTPCommand.STOU, remote);
@@ -2011,7 +2119,7 @@
         return __storeFileStream(FTPCommand.STOU, null);
     }
 
-    /***
+    /**
      * Reserve a number of bytes on the server for the next file transfer.
      * <p>
      * @param bytes  The number of bytes which the server should allocate.
@@ -2023,7 +2131,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean allocate(int bytes) throws IOException
     {
         return FTPReply.isPositiveCompletion(allo(bytes));
@@ -2298,7 +2406,7 @@
         return engine.getFiles(filter);
     }
 
-    /***
+    /**
      * Restart a <code>STREAM_TRANSFER_MODE</code> file transfer starting
      * from the given offset.  This will only work on FTP servers supporting
      * the REST comand for the stream transfer mode.  However, most FTP
@@ -2315,22 +2423,30 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     * @since 3.1 (changed from private to protected)
+     */
     protected boolean restart(long offset) throws IOException
     {
         __restartOffset = 0;
         return FTPReply.isPositiveIntermediate(rest(Long.toString(offset)));
     }
 
-    /***
-     * Sets the restart offset.  The restart command is sent to the server
-     * only before sending the file transfer command.  When this is done,
-     * the restart marker is reset to zero.
+    /**
+     * Sets the restart offset for file transfers.
+     * <p>  
+     * The restart command is not sent to the server immediately.
+     * It is sent when a data connection is created as part of a 
+     * subsequent command.
+     * The restart marker is reset to zero after use.
+     * </p>
      * <p>
+     * <b>Note: This method should only be invoked immediately prior to
+     * the transfer to which it applies.</b>
+     * 
      * @param offset  The offset into the remote file at which to start the
      *           next file transfer.  This must be a value greater than or
      *           equal to zero.
-     ***/
+     */
     public void setRestartOffset(long offset)
     {
         if (offset >= 0) {
@@ -2338,12 +2454,12 @@
         }
     }
 
-    /***
+    /**
      * Fetches the restart offset.
      * <p>
      * @return offset  The offset into the remote file at which to start the
      *           next file transfer.
-     ***/
+     */
     public long getRestartOffset()
     {
         return __restartOffset;
@@ -2351,7 +2467,7 @@
 
 
 
-    /***
+    /**
      * Renames a remote file.
      * <p>
      * @param from  The name of the remote file to rename.
@@ -2364,7 +2480,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean rename(String from, String to) throws IOException
     {
         if (!FTPReply.isPositiveIntermediate(rnfr(from))) {
@@ -2375,7 +2491,7 @@
     }
 
 
-    /***
+    /**
      * Abort a transfer in progress.
      * <p>
      * @return True if successfully completed, false if not.
@@ -2386,13 +2502,13 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean abort() throws IOException
     {
         return FTPReply.isPositiveCompletion(abor());
     }
 
-    /***
+    /**
      * Deletes a file on the FTP server.
      * <p>
      * @param pathname   The pathname of the file to be deleted.
@@ -2404,14 +2520,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean deleteFile(String pathname) throws IOException
     {
         return FTPReply.isPositiveCompletion(dele(pathname));
     }
 
 
-    /***
+    /**
      * Removes a directory on the FTP server (if empty).
      * <p>
      * @param pathname  The pathname of the directory to remove.
@@ -2423,14 +2539,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean removeDirectory(String pathname) throws IOException
     {
         return FTPReply.isPositiveCompletion(rmd(pathname));
     }
 
 
-    /***
+    /**
      * Creates a new subdirectory on the FTP server in the current directory
      * (if a relative pathname is given) or where specified (if an absolute
      * pathname is given).
@@ -2444,14 +2560,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean makeDirectory(String pathname) throws IOException
     {
         return FTPReply.isPositiveCompletion(mkd(pathname));
     }
 
 
-    /***
+    /**
      * Returns the pathname of the current working directory.
      * <p>
      * @return The pathname of the current working directory.  If it cannot
@@ -2463,7 +2579,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public String printWorkingDirectory() throws IOException
     {
         if (pwd() != FTPReply.PATHNAME_CREATED) {
@@ -2492,7 +2608,7 @@
     }
 
 
-    /***
+    /**
      * Fetches the system type from the server and returns the string.
      * This value is cached for the duration of the connection after the
      * first call to this method.  In other words, only the first time
@@ -2512,7 +2628,7 @@
      *  command to the server or receiving a reply from the server (and the default
      *  system type property is not defined)
      *  @since 2.2
-     ***/
+     */
     public String getSystemType() throws IOException
     {
         //if (syst() == FTPReply.NAME_SYSTEM_TYPE)
@@ -2537,7 +2653,7 @@
     }
 
 
-    /***
+    /**
      * Fetches the system help information from the server and returns the
      * full string.
      * <p>
@@ -2550,7 +2666,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *  command to the server or receiving a reply from the server.
-     ***/
+     */
     public String listHelp() throws IOException
     {
         if (FTPReply.isPositiveCompletion(help())) {
@@ -2583,7 +2699,7 @@
     }
 
 
-    /***
+    /**
      * Sends a NOOP command to the FTP server.  This is useful for preventing
      * server timeouts.
      * <p>
@@ -2595,14 +2711,14 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public boolean sendNoOp() throws IOException
     {
         return FTPReply.isPositiveCompletion(noop());
     }
 
 
-    /***
+    /**
      * Obtain a list of filenames in a directory (or just the name of a given
      * file, which is not particularly useful).  This information is obtained
      * through the NLST command.  If the given pathname is a directory and
@@ -2615,6 +2731,11 @@
      * expand glob expressions.
      * <p>
      * @param pathname  The file or directory to list.
+     *                  Warning: the server may treat a leading '-' as an
+     *                  option introducer. If so, try using an absolute path,
+     *                  or prefix the path with ./ (unix style servers).
+     *                  Some servers may support "--" as meaning end of options,
+     *                  in which case "-- -xyz" should work.
      * @return The list of filenames contained in the given path.  null if
      *     the list could not be obtained.  If there are no filenames in
      *     the directory, a zero-length array is returned.
@@ -2625,12 +2746,12 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public String[] listNames(String pathname) throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(FTPCommand.NLST, getListArguments(pathname));
 
-        if ((socket = _openDataConnection_(FTPCommand.NLST, getListArguments(pathname))) == null) {
+        if (socket == null) {
             return null;
         }
 
@@ -2656,7 +2777,7 @@
     }
 
 
-    /***
+    /**
      * Obtain a list of filenames in the current working directory
      * This information is obtained through the NLST command.  If the current
      * directory contains no files, a zero length array is returned only
@@ -2676,7 +2797,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public String[] listNames() throws IOException
     {
         return listNames(null);
@@ -2697,6 +2818,11 @@
      *                  or may not expand glob expressions, using them here
      *                  is not recommended and may well cause this method to
      *                  fail.
+     *                  Also, some servers treat a leading '-' as being an option.
+     *                  To avoid this interpretation, use an absolute pathname
+     *                  or prefix the pathname with ./ (unix style servers).
+     *                  Some servers may support "--" as meaning end of options,
+     *                  in which case "-- -xyz" should work.
      *
      * @return The list of file information contained in the given path in
      *         the format determined by the autodetection mechanism
@@ -3091,10 +3217,10 @@
             FTPFileEntryParser parser, String pathname)
     throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(FTPCommand.LIST, getListArguments(pathname));
 
         FTPListParseEngine engine = new FTPListParseEngine(parser);
-        if ((socket = _openDataConnection_(FTPCommand.LIST, getListArguments(pathname))) == null)
+        if (socket == null)
         {
             return engine;
         }
@@ -3119,9 +3245,9 @@
      */
     private FTPListParseEngine initiateMListParsing(String pathname) throws IOException
     {
-        Socket socket;
+        Socket socket = _openDataConnection_(FTPCommand.MLSD, pathname);
         FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance());
-        if ((socket = _openDataConnection_(FTPCommand.MLSD, pathname)) == null)
+        if (socket == null)
         {
             return engine;
         }
@@ -3159,7 +3285,7 @@
     }
 
 
-    /***
+    /**
      * Issue the FTP STAT command to the server.
      * <p>
      * @return The status information returned by the server.
@@ -3170,7 +3296,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public String getStatus() throws IOException
     {
         if (FTPReply.isPositiveCompletion(stat())) {
@@ -3180,7 +3306,7 @@
     }
 
 
-    /***
+    /**
      * Issue the FTP STAT command to the server for a given pathname.  This
      * should produce a listing of the file or directory.
      * <p>
@@ -3192,7 +3318,7 @@
      *      as an IOException or independently as itself.
      * @exception IOException  If an I/O error occurs while either sending a
      *      command to the server or receiving a reply from the server.
-     ***/
+     */
     public String getStatus(String pathname) throws IOException
     {
         if (FTPReply.isPositiveCompletion(stat(pathname))) {
@@ -3244,22 +3370,29 @@
 
 
     /**
-     * Set the internal buffer size.
+     * Set the internal buffer size for data sockets.
      *
-     * @param bufSize The size of the buffer
+     * @param bufSize The size of the buffer. Use a non-positive value to use the default.
      */
     public void setBufferSize(int bufSize) {
         __bufferSize = bufSize;
     }
 
     /**
-     * Retrieve the current internal buffer size.
+     * Retrieve the current internal buffer size for data sockets.
      * @return The current buffer size.
      */
     public int getBufferSize() {
         return __bufferSize;
     }
 
+    /**
+     * Get the buffer size, with default set to Util.DEFAULT_COPY_BUFFER_SIZE.
+     * @return the buffer size.
+     */
+    private int getDefaultedBufferSize() {
+        return __bufferSize > 0 ? __bufferSize : Util.DEFAULT_COPY_BUFFER_SIZE;
+    }
 
     /**
      * Implementation of the {@link Configurable Configurable} interface.
@@ -3452,6 +3585,9 @@
 
     /**
      * Enables or disables automatic server encoding detection (only UTF-8 supported).
+     * <p>
+     * Does not affect existing connections; must be invoked before a connection is established.
+     * 
      * @param autodetect If true, automatic server encoding detection will be enabled.
      */
     public void setAutodetectUTF8(boolean autodetect)
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPFile.java b/src/main/java/org/apache/commons/net/ftp/FTPFile.java
index 01ee2ca..c95646f 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPFile.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPFile.java
@@ -405,6 +405,7 @@
         }
         sb.append(' ');
         sb.append(getName());
+        fmt.close();
         return sb.toString();
     }
 
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 5255150..b838a28 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPHTTPClient.java
@@ -74,6 +74,7 @@
      * {@inheritDoc}
      *
      * @throws IllegalStateException if connection mode is not passive
+     * @since 3.1
      */
     @Override
     protected Socket _openDataConnection_(String command, String arg) 
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 2642699..9337edb 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
@@ -107,7 +107,8 @@
     private KeyManager keyManager = null;
 
     /**
-     * Constructor for FTPSClient.
+     * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}.
+     * 
      * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false)
      */
     public FTPSClient() {
@@ -116,6 +117,7 @@
 
     /**
      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
+     * Calls {@link #FTPSClient(String, boolean)}
      * @param isImplicit The security mode (Implicit/Explicit).
      */
     public FTPSClient(boolean isImplicit) {
@@ -123,7 +125,8 @@
     }
 
     /**
-     * Constructor for FTPSClient, using explict mode
+     * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}.
+     * 
      * @param protocol the protocol to use
      */
     public FTPSClient(String protocol) {
@@ -134,7 +137,7 @@
      * 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).
      */
@@ -149,6 +152,7 @@
 
     /**
      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
+     * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
      * @param isImplicit The security mode(Implicit/Explicit).
      * @param context A pre-configured SSL Context
      */
@@ -160,7 +164,7 @@
     /**
      * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
      * and isImplicit {@code false}
-     *
+     * Calls {@link #FTPSClient(boolean, SSLContext)}
      * @param context A pre-configured SSL Context
      */
     public FTPSClient(SSLContext context) {
@@ -470,18 +474,19 @@
     }
     
     /**
-     * PROT command.<br/>
-     * C - Clear<br/>
-     * S - Safe(SSL protocol only)<br/>
-     * E - Confidential(SSL protocol only)<br/>
-     * P - Private
-     * <p>
+     * PROT command.
+     * <ul>
+     * <li>C - Clear</li>
+     * <li>S - Safe(SSL protocol only)</li>
+     * <li>E - Confidential(SSL protocol only)</li>
+     * <li>P - Private</li>
+     * </ul>
      * <b>N.B.</b> the method calls
      *  {@link #setSocketFactory(javax.net.SocketFactory)} and
      *  {@link #setServerSocketFactory(javax.net.ServerSocketFactory)}
      *
-     * @param prot Data Channel Protection Level.
-     * @throws SSLException If the server reply code does not equal "200".
+     * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}. 
+     * @throws SSLException If the server reply code does not equal  {@code 200}.
      * @throws IOException If an I/O error occurs while sending
      * the command.
      */
@@ -511,8 +516,9 @@
      * @return True - A set point is right / False - A set point is not right
      */
     private boolean checkPROTValue(String prot) {
-        for (int p = 0; p < PROT_COMMAND_VALUE.length; p++) {
-            if (PROT_COMMAND_VALUE[p].equals(prot)) {
+        for (String element : PROT_COMMAND_VALUE)
+        {
+            if (element.equals(prot)) {
                 return true;
             }
         }
@@ -554,7 +560,7 @@
     /**
      * 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 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.
@@ -565,8 +571,30 @@
      * @see FTPClient#_openDataConnection_(int, String)
      */
     @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
     protected Socket _openDataConnection_(int command, String arg)
             throws IOException {
+        return _openDataConnection_(FTPCommand.getCommand(command), arg);
+    }
+
+   /**
+     * 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_(String command, String arg)
+            throws IOException {
         Socket socket = super._openDataConnection_(command, arg);
         _prepareDataSocket_(socket);
         if (socket instanceof SSLSocket) {
@@ -599,6 +627,7 @@
     * after creating the socket.
     * The default implementation is a no-op
     * @throws IOException 
+    * @since 3.1
     */
     protected void _prepareDataSocket_(Socket socket)
             throws IOException {
@@ -614,10 +643,11 @@
     }
 
     /**
-     * Override the default {@link TrustManager} to use.
+     * 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.
-    * @see org.apache.commons.net.util.TrustManagerUtils
+     * @param trustManager The TrustManager implementation to set, may be {@code null}
+     * @see org.apache.commons.net.util.TrustManagerUtils
      */
     public void setTrustManager(TrustManager trustManager) {
         this.trustManager = trustManager;
@@ -666,7 +696,7 @@
     {
         if (data != null)
         {
-            return sendCommand(CMD_ADAT, new String(Base64.encodeBase64(data)));
+            return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
         }
         else
         {
@@ -712,7 +742,7 @@
     {
         if (data != null)
         {
-            return sendCommand(CMD_MIC, new String(Base64.encodeBase64(data)));
+            return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
         }
         else
         {
@@ -732,7 +762,7 @@
     {
         if (data != null)
         {
-            return sendCommand(CMD_CONF, new String(Base64.encodeBase64(data)));
+            return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
         }
         else
         {
@@ -752,7 +782,7 @@
     {
         if (data != null)
         {
-            return sendCommand(CMD_ENC, new String(Base64.encodeBase64(data)));
+            return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
         }
         else
         {
diff --git a/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java b/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java
index 3f4277e..478ebc3 100644
--- a/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java
+++ b/src/main/java/org/apache/commons/net/ftp/FTPSTrustManager.java
@@ -42,9 +42,9 @@
 
     public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException
     {
-        for (int i = 0; i < certificates.length; ++i)
+        for (X509Certificate certificate : certificates)
         {
-            certificates[i].checkValidity();
+            certificate.checkValidity();
         }
     }
 
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java
index b2efb92..f3dcea6 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/CompositeFileEntryParser.java
@@ -55,10 +55,8 @@
         }
         else
         {
-            for (int iterParser=0; iterParser < ftpFileEntryParsers.length; iterParser++)
+            for (FTPFileEntryParser ftpFileEntryParser : ftpFileEntryParsers)
             {
-                FTPFileEntryParser ftpFileEntryParser = ftpFileEntryParsers[iterParser];
-
                 FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry);
                 if (matched != null)
                 {
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 37f7e92..fb23ba2 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
@@ -64,10 +64,9 @@
      * member has not been defined, attempt to parse with the defaultDateFormat
      * member.  If that fails, throw a ParseException.
      *
-     * This method allows a {@link Calendar} instance to be passed in which represents the
-     * current (system) time.
-     *
-     * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
+     * This method assumes that the server time is the same as the local time.
+     * 
+     * @see FTPTimestampParserImpl#parseTimestamp(String, Calendar)
      *
      * @param timestampStr The timestamp to be parsed
      */
@@ -77,82 +76,77 @@
     }
 
     /**
-     * Implements the one {@link  FTPTimestampParser#parseTimestamp(String)  method}
-     * in the {@link  FTPTimestampParser  FTPTimestampParser} interface
-     * according to this algorithm:
-     *
      * If the recentDateFormat member has been defined, try to parse the
      * supplied string with that.  If that parse fails, or if the recentDateFormat
      * member has not been defined, attempt to parse with the defaultDateFormat
      * member.  If that fails, throw a ParseException.
      *
-     * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
+     * This method allows a {@link Calendar} instance to be passed in which represents the
+     * current (system) time.
+     *
+     * @see FTPTimestampParser#parseTimestamp(String)
      * @param timestampStr The timestamp to be parsed
      * @param serverTime The current time for the server
      * @since 1.5
      */
     public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException {
-        Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it
-        now.setTimeZone(this.getServerTimeZone());
-        Calendar working = (Calendar) now.clone();
+        Calendar working = (Calendar) serverTime.clone();
         working.setTimeZone(getServerTimeZone()); // is this needed?
-        ParsePosition pp = new ParsePosition(0);
 
         Date parsed = null;
+
         if (recentDateFormat != null) {
+            Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it
+            now.setTimeZone(this.getServerTimeZone());
             if (lenientFutureDates) {
                 // add a day to "now" so that "slop" doesn't cause a date
                 // slightly in the future to roll back a full year.  (Bug 35181 => NET-83)
                 now.add(Calendar.DATE, 1);
             }
-            parsed = recentDateFormat.parse(timestampStr, pp);
-        }
-        if (parsed != null && pp.getIndex() == timestampStr.length())
-        {
-            working.setTime(parsed);
-            working.set(Calendar.YEAR, now.get(Calendar.YEAR));
-
-            if (working.after(now)) {
-                working.add(Calendar.YEAR, -1);
-            }
-        } else {
+            // The Java SimpleDateFormat class uses the epoch year 1970 if not present in the input
+            // As 1970 was not a leap year, it cannot parse "Feb 29" correctly.
+            // Java 1.5+ returns Mar 1 1970
             // Temporarily add the current year to the short date time
             // to cope with short-date leap year strings.
-            // e.g. Java's DateFormatter will assume that "Feb 29 12:00" refers to
-            // Feb 29 1970 (an invalid date) rather than a potentially valid leap year date.
-            // This is pretty bad hack to work around the deficiencies of the JDK date/time classes.
-            if (recentDateFormat != null) {
-                pp = new ParsePosition(0);
-                int year = now.get(Calendar.YEAR);
-                String timeStampStrPlusYear = timestampStr + " " + year;
-                SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy",
-                        recentDateFormat.getDateFormatSymbols());
-                hackFormatter.setLenient(false);
-                hackFormatter.setTimeZone(recentDateFormat.getTimeZone());
-                parsed = hackFormatter.parse(timeStampStrPlusYear, pp);
-            }
-            if (parsed != null && pp.getIndex() == timestampStr.length() + 5) {
+            // Since Feb 29 is more that 6 months from the end of the year, this should be OK for
+            // all instances of short dates which are +- 6 months from current date.
+            // TODO this won't always work for systems that use short dates +0/-12months
+            // e.g. if today is Jan 1 2001 and the short date is Feb 29
+            String year = Integer.toString(now.get(Calendar.YEAR));
+            String timeStampStrPlusYear = timestampStr + " " + year;
+            SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy",
+                    recentDateFormat.getDateFormatSymbols());
+            hackFormatter.setLenient(false);
+            hackFormatter.setTimeZone(recentDateFormat.getTimeZone());
+            ParsePosition pp = new ParsePosition(0);
+            parsed = hackFormatter.parse(timeStampStrPlusYear, pp);
+            // Check if we parsed the full string, if so it must have been a short date originally
+            if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) {
                 working.setTime(parsed);
-            }
-            else {
-                pp = new ParsePosition(0);
-                parsed = defaultDateFormat.parse(timestampStr, pp);
-                // note, length checks are mandatory for us since
-                // SimpleDateFormat methods will succeed if less than
-                // full string is matched.  They will also accept,
-                // despite "leniency" setting, a two-digit number as
-                // a valid year (e.g. 22:04 will parse as 22 A.D.)
-                // so could mistakenly confuse an hour with a year,
-                // if we don't insist on full length parsing.
-                if (parsed != null && pp.getIndex() == timestampStr.length()) {
-                    working.setTime(parsed);
-                } else {
-                    throw new ParseException(
-                            "Timestamp could not be parsed with older or recent DateFormat",
-                            pp.getErrorIndex());
+                if (working.after(now)) { // must have been last year instead
+                    working.add(Calendar.YEAR, -1);
                 }
+                return working;
             }
         }
+
+        ParsePosition pp = new ParsePosition(0);
+        parsed = defaultDateFormat.parse(timestampStr, pp);
+        // note, length checks are mandatory for us since
+        // SimpleDateFormat methods will succeed if less than
+        // full string is matched.  They will also accept,
+        // despite "leniency" setting, a two-digit number as
+        // a valid year (e.g. 22:04 will parse as 22 A.D.)
+        // so could mistakenly confuse an hour with a year,
+        // if we don't insist on full length parsing.
+        if (parsed != null && pp.getIndex() == timestampStr.length()) {
+            working.setTime(parsed);
+        } else {
+            throw new ParseException(
+                    "Timestamp '"+timestampStr+"' could not be parsed using a server time of "
+                        +serverTime.getTime().toString(),
+                    pp.getErrorIndex());
+        }
         return working;
     }
 
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 da10818..d05be1a 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
@@ -209,6 +209,7 @@
                     file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true);
                     break;
                 default:
+                    break;
                     // ignore unexpected flag for now.
             } // switch
         } // each char
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 5e9367d..4547e64 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
@@ -355,7 +355,7 @@
      * @return
      */
     private boolean parseSimpleEntry(FTPFile file, String entry) {
-        if (entry != null && entry.length() > 0) {
+        if (entry != null && entry.trim().length() > 0) {
             file.setRawListing(entry);
             String name = entry.split(" ")[0];
             file.setName(name);
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 606861a..4c89751 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
@@ -169,8 +169,8 @@
             case 'b':
             case 'c':
                 isDevice = true;
-                // break; - fall through
-                //$FALL-THROUGH$ TODO change this if DEVICE_TYPE implemented
+                type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
+                break;
             case 'f':
             case '-':
                 type = FTPFile.FILE_TYPE;
diff --git a/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java b/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
index 025b69b..5b43f6e 100644
--- a/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
+++ b/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
@@ -208,8 +208,8 @@
             case 'b':
             case 'c':
                 isDevice = true;
-                // break; - fall through
-                //$FALL-THROUGH$ TODO change this if DEVICE_TYPE implemented
+                type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
+                break;
             case 'f':
             case '-':
                 type = FTPFile.FILE_TYPE;
diff --git a/src/main/java/org/apache/commons/net/imap/AuthenticatingIMAPClient.java b/src/main/java/org/apache/commons/net/imap/AuthenticatingIMAPClient.java
index 35c9d9f..f1f922f 100644
--- a/src/main/java/org/apache/commons/net/imap/AuthenticatingIMAPClient.java
+++ b/src/main/java/org/apache/commons/net/imap/AuthenticatingIMAPClient.java
@@ -150,9 +150,7 @@
             {
                 // the server sends an empty response ("+ "), so we don't have to read it.
                 int result = sendData(
-                    new String(
-                        Base64.encodeBase64(("\000" + username + "\000" + password).getBytes())
-                        )
+                    Base64.encodeBase64StringUnChunked(("\000" + username + "\000" + password).getBytes())
                     );
                 if (result == IMAPReply.OK)
                 {
@@ -176,7 +174,7 @@
                 toEncode[usernameBytes.length] = ' ';
                 System.arraycopy(hmacResult, 0, toEncode, usernameBytes.length + 1, hmacResult.length);
                 // send the reply and read the server code:
-                int result = sendData(new String(Base64.encodeBase64(toEncode)));
+                int result = sendData(Base64.encodeBase64StringUnChunked(toEncode));
                 if (result == IMAPReply.OK)
                 {
                     setState(IMAP.IMAPState.AUTH_STATE);
@@ -188,12 +186,20 @@
                 // the server sends fixed responses (base64("Username") and
                 // base64("Password")), so we don't have to read them.
                 if (sendData(
-                    new String(Base64.encodeBase64(username.getBytes()))) != IMAPReply.CONT)
+                    Base64.encodeBase64StringUnChunked(username.getBytes())) != IMAPReply.CONT)
                 {
                     return false;
                 }
-                int result = sendData(
-                    new String(Base64.encodeBase64(password.getBytes())));
+                int result = sendData(Base64.encodeBase64StringUnChunked(password.getBytes()));
+                if (result == IMAPReply.OK)
+                {
+                    setState(IMAP.IMAPState.AUTH_STATE);
+                }
+                return result == IMAPReply.OK;
+            }
+            case XOAUTH:
+            {
+                int result = sendData(new String(username.getBytes()));
                 if (result == IMAPReply.OK)
                 {
                     setState(IMAP.IMAPState.AUTH_STATE);
@@ -214,12 +220,12 @@
     private String _convertToHexString(byte[] a)
     {
         StringBuilder result = new StringBuilder(a.length*2);
-        for (int i = 0; i < a.length; i++)
+        for (byte element : a)
         {
-            if ( (a[i] & 0x0FF) <= 15 ) {
+            if ( (element & 0x0FF) <= 15 ) {
                 result.append("0");
             }
-            result.append(Integer.toHexString(a[i] & 0x0FF));
+            result.append(Integer.toHexString(element & 0x0FF));
         }
         return result.toString();
     }
@@ -234,8 +240,10 @@
         /** The standarised (RFC2195) CRAM-MD5 method, which doesn't send the password (secure). */
         CRAM_MD5("CRAM-MD5"),
         /** The unstandarised Microsoft LOGIN method, which sends the password unencrypted (insecure). */
-        LOGIN("LOGIN");
-
+        LOGIN("LOGIN"),
+        /** XOAUTH */
+        XOAUTH("XOAUTH");
+        
         private final String authName;
         
         private AUTH_METHOD(String name){
diff --git a/src/main/java/org/apache/commons/net/imap/IMAP.java b/src/main/java/org/apache/commons/net/imap/IMAP.java
index fb1dda5..aff68a5 100644
--- a/src/main/java/org/apache/commons/net/imap/IMAP.java
+++ b/src/main/java/org/apache/commons/net/imap/IMAP.java
@@ -110,6 +110,15 @@
 
         if (wantTag) {
             while(IMAPReply.isUntagged(line)) {
+                int literalCount = IMAPReply.literalCount(line);
+                while (literalCount >= 0) {
+                    line=_reader.readLine();
+                    if (line == null) {
+                        throw new EOFException("Connection closed without indication.");
+                    }
+                    _replyLines.add(line);
+                    literalCount -= (line.length() + 2); // Allow for CRLF
+                }
                 line = _reader.readLine();
                 if (line == null) {
                     throw new EOFException("Connection closed without indication.");
diff --git a/src/main/java/org/apache/commons/net/imap/IMAPCommand.java b/src/main/java/org/apache/commons/net/imap/IMAPCommand.java
index 8e972db..2455a1a 100644
--- a/src/main/java/org/apache/commons/net/imap/IMAPCommand.java
+++ b/src/main/java/org/apache/commons/net/imap/IMAPCommand.java
@@ -36,6 +36,8 @@
     AUTHENTICATE(1),
     LOGIN(2),
     
+    XOAUTH(1),
+    
     // commands valid in authenticated state
     SELECT(1),
     EXAMINE(1),
diff --git a/src/main/java/org/apache/commons/net/imap/IMAPReply.java b/src/main/java/org/apache/commons/net/imap/IMAPReply.java
index 64bb873..f1be7ae 100644
--- a/src/main/java/org/apache/commons/net/imap/IMAPReply.java
+++ b/src/main/java/org/apache/commons/net/imap/IMAPReply.java
@@ -86,7 +86,7 @@
      * Intepret the String reply code - OK, NO, BAD - in a tagged response as a integer.
      *
      * @param line the tagged line to be checked
-     * @return {@link #OK} or {@link #NO} or {@link #BAD}
+     * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT}
      * @throws IOException if the input has an unexpected format
      */
     public static int getReplyCode(String line) throws IOException {
@@ -96,11 +96,26 @@
     private static final String UNTAGGED_RESPONSE = "^\\* (\\S+).*";
     private static final Pattern UNTAGGED_PATTERN = Pattern.compile(UNTAGGED_RESPONSE);
 
+    private static final Pattern LITERAL_PATTERN = Pattern.compile("\\{(\\d+)\\}$"); // {dd}
+
+    /**
+     * Checks if the line introduces a literal, i.e. ends with {dd}
+     * 
+     * @return the literal count, or -1 if there was no literal.
+     */
+    public static int literalCount(String line) {
+        Matcher m = LITERAL_PATTERN.matcher(line);
+        if (m.find()) {
+            return Integer.parseInt(m.group(1)); // Should always parse because we matched \d+
+        }
+        return -1;
+    }
+
     /**
      * Intepret the String reply code - OK, NO, BAD - in an untagged response as a integer.
      *
      * @param line the untagged line to be checked
-     * @return {@link #OK} or {@link #NO} or {@link #BAD}
+     * @return {@link #OK} or {@link #NO} or {@link #BAD} or {@link #CONT}
      * @throws IOException if the input has an unexpected format
      */
     public static int getUntaggedReplyCode(String line) throws IOException {
@@ -113,7 +128,7 @@
             return CONT;
         }
         Matcher m = pattern.matcher(line);
-        if (m.matches()) {
+        if (m.matches()) { // TODO would lookingAt() be more efficient? If so, then drop trailing .* from patterns
             String code = m.group(1);
             if (code.equals(IMAP_OK)) {
                 return OK;
diff --git a/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java b/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
index 29d1db9..612f565 100644
--- a/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
+++ b/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
@@ -166,18 +166,19 @@
     @Override
     public int read(char[] buffer, int offset, int length) throws IOException
     {
-        int ch, off;
+        if (length < 1)
+        {
+            return 0;
+        }
+        int ch;
         synchronized (lock)
         {
-            if (length < 1)
-            {
-                return 0;
-            }
             if ((ch = read()) == -1)
             {
                 return -1;
             }
-            off = offset;
+
+            int off = offset;
 
             do
             {
diff --git a/src/main/java/org/apache/commons/net/nntp/Article.java b/src/main/java/org/apache/commons/net/nntp/Article.java
index 4cad8e7..5976680 100644
--- a/src/main/java/org/apache/commons/net/nntp/Article.java
+++ b/src/main/java/org/apache/commons/net/nntp/Article.java
@@ -144,7 +144,7 @@
             for (int i = 0; i < depth; ++i) {
                 System.out.print("==>");
             }
-            System.out.println(article.getSubject() + "\t" + article.getFrom());
+            System.out.println(article.getSubject() + "\t" + article.getFrom()+"\t"+article.getArticleId());            
             if (article.kid != null) {
                 printThread(article.kid, depth + 1);
             }
diff --git a/src/main/java/org/apache/commons/net/nntp/NNTPClient.java b/src/main/java/org/apache/commons/net/nntp/NNTPClient.java
index f213ece..bd72dd0 100644
--- a/src/main/java/org/apache/commons/net/nntp/NNTPClient.java
+++ b/src/main/java/org/apache/commons/net/nntp/NNTPClient.java
@@ -253,16 +253,18 @@
         Vector<NewsgroupInfo> list = new Vector<NewsgroupInfo>(2048);
 
         String line;
-        while ((line = reader.readLine()) != null)
-        {
-            NewsgroupInfo tmp = __parseNewsgroupListEntry(line);
-            if (tmp != null) {
-                list.addElement(tmp);
-            } else {
-                throw new MalformedServerReplyException(line);
+        try {
+            while ((line = reader.readLine()) != null) {
+                NewsgroupInfo tmp = __parseNewsgroupListEntry(line);
+                if (tmp != null) {
+                    list.addElement(tmp);
+                } else {
+                    throw new MalformedServerReplyException(line);
+                }
             }
+        } finally {
+            reader.close();
         }
-
         int size;
         if ((size = list.size()) < 1) {
             return new NewsgroupInfo[0];
@@ -1215,8 +1217,12 @@
         BufferedReader reader = new DotTerminatedMessageReader(_reader_);
 
         String line;
-        while ((line = reader.readLine()) != null) {
-            list.addElement(line);
+        try {
+            while ((line = reader.readLine()) != null) {
+                list.addElement(line);
+            }
+        } finally {
+            reader.close();
         }
 
         int size = list.size();
diff --git a/src/main/java/org/apache/commons/net/nntp/Threader.java b/src/main/java/org/apache/commons/net/nntp/Threader.java
index 410b27c..d2f7edd 100644
--- a/src/main/java/org/apache/commons/net/nntp/Threader.java
+++ b/src/main/java/org/apache/commons/net/nntp/Threader.java
@@ -127,8 +127,8 @@
         ThreadContainer parentRef = null;
         {
             String[] references = threadable.messageThreadReferences();
-            for (int i = 0; i < references.length; ++i) {
-                String refString = references[i];
+            for (String refString : references)
+            {
                 ThreadContainer ref = idTable.get(refString);
 
                 // if this id doesnt have a container, create one
diff --git a/src/main/java/org/apache/commons/net/ntp/NTPUDPClient.java b/src/main/java/org/apache/commons/net/ntp/NTPUDPClient.java
index e53f167..8c0db52 100644
--- a/src/main/java/org/apache/commons/net/ntp/NTPUDPClient.java
+++ b/src/main/java/org/apache/commons/net/ntp/NTPUDPClient.java
@@ -36,7 +36,7 @@
  * connectionless protocol and the Network Time Protocol is stateless.
  *
  * @author Jason Mathews, MITRE Corp
- * @version $Revision$ $Date$
+ * @version $Revision$
  ***/
 
 public final class NTPUDPClient extends DatagramSocketClient
diff --git a/src/main/java/org/apache/commons/net/ntp/NtpUtils.java b/src/main/java/org/apache/commons/net/ntp/NtpUtils.java
index cebc82d..a14ceb4 100644
--- a/src/main/java/org/apache/commons/net/ntp/NtpUtils.java
+++ b/src/main/java/org/apache/commons/net/ntp/NtpUtils.java
@@ -22,7 +22,7 @@
  *
  * @author Jason Mathews, MITRE Corp
  *
- * @version $Revision$ $Date$
+ * @version $Revision$
  */
 public final class NtpUtils {
 
diff --git a/src/main/java/org/apache/commons/net/ntp/NtpV3Impl.java b/src/main/java/org/apache/commons/net/ntp/NtpV3Impl.java
index 6877842..eafe591 100644
--- a/src/main/java/org/apache/commons/net/ntp/NtpV3Impl.java
+++ b/src/main/java/org/apache/commons/net/ntp/NtpV3Impl.java
@@ -25,7 +25,7 @@
  * @author Naz Irizarry, MITRE Corp
  * @author Jason Mathews, MITRE Corp
  *
- * @version $Revision$ $Date$
+ * @version $Revision$
  */
 public class NtpV3Impl implements NtpV3Packet
 {
diff --git a/src/main/java/org/apache/commons/net/ntp/NtpV3Packet.java b/src/main/java/org/apache/commons/net/ntp/NtpV3Packet.java
index 15ed7fa..86a8582 100644
--- a/src/main/java/org/apache/commons/net/ntp/NtpV3Packet.java
+++ b/src/main/java/org/apache/commons/net/ntp/NtpV3Packet.java
@@ -25,7 +25,7 @@
  *
  * @author Naz Irizarry, MITRE Corp
  * @author Jason Mathews, MITRE Corp
- * @version $Revision$ $Date$
+ * @version $Revision$
  */
 public interface NtpV3Packet
 {
diff --git a/src/main/java/org/apache/commons/net/ntp/TimeInfo.java b/src/main/java/org/apache/commons/net/ntp/TimeInfo.java
index 57bf583..c8a6b1a 100644
--- a/src/main/java/org/apache/commons/net/ntp/TimeInfo.java
+++ b/src/main/java/org/apache/commons/net/ntp/TimeInfo.java
@@ -26,7 +26,7 @@
  *
  * @author Jason Mathews, MITRE Corp
  *
- * @version $Revision$ $Date$
+ * @version $Revision$
  */
 public class TimeInfo {
 
diff --git a/src/main/java/org/apache/commons/net/ntp/TimeStamp.java b/src/main/java/org/apache/commons/net/ntp/TimeStamp.java
index 4a518cf..ed8fc00 100644
--- a/src/main/java/org/apache/commons/net/ntp/TimeStamp.java
+++ b/src/main/java/org/apache/commons/net/ntp/TimeStamp.java
@@ -39,7 +39,7 @@
  * </p>
  *
  * @author Jason Mathews, MITRE Corp
- * @version $Revision$ $Date$
+ * @version $Revision$
  * @see java.util.Date
  */
 public class TimeStamp implements java.io.Serializable, Comparable<TimeStamp>
diff --git a/src/main/java/org/apache/commons/net/pop3/ExtendedPOP3Client.java b/src/main/java/org/apache/commons/net/pop3/ExtendedPOP3Client.java
index 5463dd9..89fcc6a 100644
--- a/src/main/java/org/apache/commons/net/pop3/ExtendedPOP3Client.java
+++ b/src/main/java/org/apache/commons/net/pop3/ExtendedPOP3Client.java
@@ -95,7 +95,7 @@
                 toEncode[usernameBytes.length] = ' ';
                 System.arraycopy(hmacResult, 0, toEncode, usernameBytes.length + 1, hmacResult.length);
                 // send the reply and read the server code:
-                return sendCommand(new String(Base64.encodeBase64(toEncode))) == POP3Reply.OK;
+                return sendCommand(Base64.encodeBase64StringUnChunked(toEncode)) == POP3Reply.OK;
             default:
                 return false;
         }
@@ -111,12 +111,12 @@
     private String _convertToHexString(byte[] a)
     {
         StringBuilder result = new StringBuilder(a.length*2);
-        for (int i = 0; i < a.length; i++)
+        for (byte element : a)
         {
-            if ( (a[i] & 0x0FF) <= 15 ) {
+            if ( (element & 0x0FF) <= 15 ) {
                 result.append("0");
             }
-            result.append(Integer.toHexString(a[i] & 0x0FF));
+            result.append(Integer.toHexString(element & 0x0FF));
         }
         return result.toString();
     }
diff --git a/src/main/java/org/apache/commons/net/pop3/POP3Client.java b/src/main/java/org/apache/commons/net/pop3/POP3Client.java
index 4be3628..f82f007 100644
--- a/src/main/java/org/apache/commons/net/pop3/POP3Client.java
+++ b/src/main/java/org/apache/commons/net/pop3/POP3Client.java
@@ -118,6 +118,7 @@
      * @return True if the command was successful, false if not.
      * @exception IOException If a network I/O error occurs in the process of
      *        sending the CAPA command.
+     * @since 3.1 (was previously in ExtendedPOP3Client)
      ***/
     public boolean capa() throws IOException
     {
diff --git a/src/main/java/org/apache/commons/net/smtp/AuthenticatingSMTPClient.java b/src/main/java/org/apache/commons/net/smtp/AuthenticatingSMTPClient.java
index 02e37b9..665d529 100644
--- a/src/main/java/org/apache/commons/net/smtp/AuthenticatingSMTPClient.java
+++ b/src/main/java/org/apache/commons/net/smtp/AuthenticatingSMTPClient.java
@@ -144,7 +144,14 @@
     /***
      * Authenticate to the SMTP server by sending the AUTH command with the
      * selected mechanism, using the given username and the given password.
-     * <p>
+     *
+     * @param method the method to use, one of the {@link AuthenticatingSMTPClient.AUTH_METHOD} enum values
+     * @param username the user name. 
+     *        If the method is XOAUTH, then this is used as the plain text oauth protocol parameter string
+     *        which is Base64-encoded for transmission.        
+     * @param password the password for the username.
+     *        Ignored for XOAUTH.
+     * 
      * @return True if successfully completed, false if not.
      * @exception SMTPConnectionClosedException
      *      If the SMTP server prematurely closes the connection as a result
@@ -174,9 +181,7 @@
         {
             // the server sends an empty response ("334 "), so we don't have to read it.
             return SMTPReply.isPositiveCompletion(sendCommand(
-                new String(
-                    Base64.encodeBase64(("\000" + username + "\000" + password).getBytes())
-                    )
+                    Base64.encodeBase64StringUnChunked(("\000" + username + "\000" + password).getBytes())
                 ));
         }
         else if (method.equals(AUTH_METHOD.CRAM_MD5))
@@ -196,18 +201,24 @@
             System.arraycopy(hmacResult, 0, toEncode, usernameBytes.length + 1, hmacResult.length);
             // send the reply and read the server code:
             return SMTPReply.isPositiveCompletion(sendCommand(
-                new String(Base64.encodeBase64(toEncode))));
+                Base64.encodeBase64StringUnChunked(toEncode)));
         }
         else if (method.equals(AUTH_METHOD.LOGIN))
         {
             // the server sends fixed responses (base64("Username") and
             // base64("Password")), so we don't have to read them.
             if (!SMTPReply.isPositiveIntermediate(sendCommand(
-                new String(Base64.encodeBase64(username.getBytes()))))) {
+                Base64.encodeBase64StringUnChunked(username.getBytes())))) {
                 return false;
             }
             return SMTPReply.isPositiveCompletion(sendCommand(
-                new String(Base64.encodeBase64(password.getBytes()))));
+                Base64.encodeBase64StringUnChunked(password.getBytes())));
+        }
+        else if (method.equals(AUTH_METHOD.XOAUTH))
+        {
+            return SMTPReply.isPositiveIntermediate(sendCommand(
+                    Base64.encodeBase64StringUnChunked(username.getBytes())
+            ));
         } else {
             return false; // safety check
         }
@@ -223,12 +234,12 @@
     private String _convertToHexString(byte[] a)
     {
         StringBuilder result = new StringBuilder(a.length*2);
-        for (int i = 0; i < a.length; i++)
+        for (byte element : a)
         {
-            if ( (a[i] & 0x0FF) <= 15 ) {
+            if ( (element & 0x0FF) <= 15 ) {
                 result.append("0");
             }
-            result.append(Integer.toHexString(a[i] & 0x0FF));
+            result.append(Integer.toHexString(element & 0x0FF));
         }
         return result.toString();
     }
@@ -243,7 +254,9 @@
         /** The standarised (RFC2195) CRAM-MD5 method, which doesn't send the password (secure). */
         CRAM_MD5,
         /** The unstandarised Microsoft LOGIN method, which sends the password unencrypted (insecure). */
-        LOGIN;
+        LOGIN,
+        /** XOAuth method which accepts a signed and base64ed OAuth URL. */
+        XOAUTH;
 
         /**
          * Gets the name of the given authentication method suitable for the server.
@@ -258,6 +271,8 @@
                 return "CRAM-MD5";
             } else if (method.equals(AUTH_METHOD.LOGIN)) {
                 return "LOGIN";
+            } else if (method.equals(AUTH_METHOD.XOAUTH)) {
+                return "XOAUTH";
             } else {
                 return null;
             }
diff --git a/src/main/java/org/apache/commons/net/smtp/SMTP.java b/src/main/java/org/apache/commons/net/smtp/SMTP.java
index 373ac81..16f5c4c 100644
--- a/src/main/java/org/apache/commons/net/smtp/SMTP.java
+++ b/src/main/java/org/apache/commons/net/smtp/SMTP.java
@@ -92,7 +92,11 @@
     // the wire.
     private static final String __DEFAULT_ENCODING = "ISO-8859-1";
 
-    /** The encoding to use (user-settable) */
+    /**
+     * The encoding to use (user-settable).
+     * 
+     * @since 3.1 (changed from private to protected)
+     */
     protected final String encoding;
 
     /**
diff --git a/src/main/java/org/apache/commons/net/telnet/Telnet.java b/src/main/java/org/apache/commons/net/telnet/Telnet.java
index 0682917..0f25fe4 100644
--- a/src/main/java/org/apache/commons/net/telnet/Telnet.java
+++ b/src/main/java/org/apache/commons/net/telnet/Telnet.java
@@ -775,9 +775,9 @@
         {
             _output_.write(_COMMAND_SB);
             // Note _output_ is buffered, so might as well simplify by writing single bytes
-            for (int ii = 0; ii < subn.length; ii++)
+            for (int element : subn)
             {
-                byte b = (byte) subn[ii];
+                byte b = (byte) element;
                 if (b == (byte) TelnetCommand.IAC) { // cast is necessary because IAC is outside the signed byte range
                     _output_.write(b); // double any IAC bytes
                 }
diff --git a/src/main/java/org/apache/commons/net/telnet/TelnetInputStream.java b/src/main/java/org/apache/commons/net/telnet/TelnetInputStream.java
index c489dd4..ebc9538 100644
--- a/src/main/java/org/apache/commons/net/telnet/TelnetInputStream.java
+++ b/src/main/java/org/apache/commons/net/telnet/TelnetInputStream.java
@@ -108,6 +108,7 @@
         __thread.setPriority(priority);
         __thread.setDaemon(true);
         __thread.start();
+        __threaded = true; // tell _processChar that we are running threaded
     }
 
 
@@ -560,7 +561,11 @@
         // Critical section because run() may change __bytesAvailable
         synchronized (__queue)
         {
-            return __bytesAvailable + super.available();
+            if (__threaded) { // Must not call super.available when running threaded: NET-466
+                return __bytesAvailable;
+            } else {
+                return __bytesAvailable + super.available();
+            }
         }
     }
 
@@ -596,8 +601,6 @@
     {
         int ch;
 
-        __threaded = true; // tell _processChar that we are running threaded
-
         try
         {
 _outerLoop:
diff --git a/src/main/java/org/apache/commons/net/util/Base64.java b/src/main/java/org/apache/commons/net/util/Base64.java
index 5aa1eca..5ddfb01 100644
--- a/src/main/java/org/apache/commons/net/util/Base64.java
+++ b/src/main/java/org/apache/commons/net/util/Base64.java
@@ -68,13 +68,11 @@
     /**
      * Chunk separator per RFC 2045 section 2.1.
      *
-     * <p>
-     * N.B. The next major release may break compatibility and make this field private.
-     * </p>
-     *
      * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
      */
-    static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
+    private static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
+    
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
     /**
      * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet"
@@ -318,7 +316,7 @@
     public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) {
         if (lineSeparator == null) {
             lineLength = 0;  // disable chunk-separating
-            lineSeparator = CHUNK_SEPARATOR;  // this just gets ignored
+            lineSeparator = EMPTY_BYTE_ARRAY;  // this just gets ignored
         }
         this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0;
         this.lineSeparator = new byte[lineSeparator.length];
@@ -478,6 +476,8 @@
                         buffer[pos++] = PAD;
                     }
                     break;
+                default:
+                    break;  // other values ignored
             }
             if (lineLength > 0 && pos > 0) {
                 System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
@@ -579,6 +579,8 @@
                     buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
                     buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
                     break;
+                default:
+                    break;  // other values ignored
             }
         }
     }
@@ -621,8 +623,9 @@
      * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
      */
     private static boolean containsBase64Byte(byte[] arrayOctet) {
-        for (int i = 0; i < arrayOctet.length; i++) {
-            if (isBase64(arrayOctet[i])) {
+        for (byte element : arrayOctet)
+        {
+            if (isBase64(element)) {
                 return true;
             }
         }
@@ -642,7 +645,9 @@
 
     /**
      * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF.
-     *
+     * <p>
+     * For a non-chunking version, see {@link #encodeBase64StringUnChunked(byte[])}.
+     * 
      * @param binaryData
      *            binary data to encode
      * @return String containing Base64 characters.
@@ -653,6 +658,33 @@
     }
 
     /**
+     * Encodes binary data using the base64 algorithm, without using chunking.
+     * <p>
+     * For a chunking version, see {@link #encodeBase64String(byte[])}.
+     * 
+     * @param binaryData
+     *            binary data to encode
+     * @return String containing Base64 characters.
+     * @since 3.2
+     */
+    public static String encodeBase64StringUnChunked(byte[] binaryData) {
+        return newStringUtf8(encodeBase64(binaryData, false));
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm.
+     * 
+     * @param binaryData
+     *            binary data to encode
+     * @param useChunking whether to split the output into chunks
+     * @return String containing Base64 characters.
+     * @since 3.2
+     */
+    public static String encodeBase64String(byte[] binaryData, boolean useChunking) {
+        return newStringUtf8(encodeBase64(binaryData, useChunking));
+    }
+
+    /**
      * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
      * url-safe variation emits - and _ instead of + and / characters.
      *
@@ -690,26 +722,6 @@
     }
 
     /**
-     * Decodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the
-     * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String.
-     *
-     * @param pObject
-     *            Object to decode
-     * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String supplied.
-     * @throws RuntimeException
-     *             if the parameter supplied is not of type byte[]
-     */
-    public Object decode(Object pObject) {
-        if (pObject instanceof byte[]) {
-            return decode((byte[]) pObject);
-        } else if (pObject instanceof String) {
-            return decode((String) pObject);
-        } else {
-            throw new RuntimeException("Parameter supplied to Base64 decode is not a byte[] or a String");
-        }
-    }
-
-    /**
      * Decodes a String containing containing characters in the Base64 alphabet.
      *
      * @param pArray
@@ -812,7 +824,7 @@
             return binaryData;
         }
 
-        long len = getEncodeLength(binaryData, CHUNK_SIZE, CHUNK_SEPARATOR);
+        long len = getEncodeLength(binaryData, isChunked ? CHUNK_SIZE : 0, isChunked ? CHUNK_SEPARATOR : EMPTY_BYTE_ARRAY);
         if (len > maxResultSize) {
             throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
                 len +
@@ -868,28 +880,9 @@
         }
     }
 
-    // Implementation of the Encoder Interface
-
-    /**
-     * Encodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the
-     * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].
-     *
-     * @param pObject
-     *            Object to encode
-     * @return An object (of type byte[]) containing the base64 encoded data which corresponds to the byte[] supplied.
-     * @throws RuntimeException
-     *             if the parameter supplied is not of type byte[]
-     */
-    public Object encode(Object pObject)  {
-        if (!(pObject instanceof byte[])) {
-            throw new RuntimeException("Parameter supplied to Base64 encode is not a byte[]");
-        }
-        return encode((byte[]) pObject);
-    }
-
     /**
      * Encodes a byte[] containing binary data, into a String containing characters in the Base64 alphabet.
-     *
+     * 
      * @param pArray
      *            a byte array containing binary data
      * @return A String containing only Base64 character data
@@ -1043,4 +1036,13 @@
         eof = false;
     }
 
+    // Getters for use in testing
+    
+    int getLineLength() {
+        return lineLength;
+    }
+    
+    byte[] getLineSeparator() {
+        return lineSeparator.clone();
+    }
 }
diff --git a/src/main/java/org/apache/commons/net/util/SubnetUtils.java b/src/main/java/org/apache/commons/net/util/SubnetUtils.java
index 278a96e..5d0065f 100644
--- a/src/main/java/org/apache/commons/net/util/SubnetUtils.java
+++ b/src/main/java/org/apache/commons/net/util/SubnetUtils.java
@@ -101,6 +101,7 @@
             return (isInclusiveHostCount() ? network() :
                 broadcast() - network() > 1 ? network() + 1 : 0); 
         }
+        
         private int high() { 
             return (isInclusiveHostCount() ? broadcast() :
                 broadcast() - network() > 1 ? broadcast() -1  : 0); 
@@ -113,17 +114,30 @@
          * @param address A dot-delimited IPv4 address, e.g. "192.168.0.1"
          * @return True if in range, false otherwise
          */
-        public boolean isInRange(String address)    { return isInRange(toInteger(address)); }
-
-        private boolean isInRange(int address)      {
-            int diff = address-low();
-            return (diff >= 0 && (diff <= (high()-low())));
+        public boolean isInRange(String address) {
+            return isInRange(toInteger(address));
         }
 
-        public String getBroadcastAddress()         { return format(toArray(broadcast())); }
-        public String getNetworkAddress()           { return format(toArray(network())); }
-        public String getNetmask()                  { return format(toArray(netmask())); }
-        public String getAddress()                  { return format(toArray(address())); }
+        private boolean isInRange(int address) {
+            int diff = address - low();
+            return (diff >= 0 && (diff <= (high() - low())));
+        }
+
+        public String getBroadcastAddress() {
+            return format(toArray(broadcast()));
+        }
+
+        public String getNetworkAddress() {
+            return format(toArray(network()));
+        }
+
+        public String getNetmask() {
+            return format(toArray(netmask()));
+        }
+
+        public String getAddress() {
+            return format(toArray(address()));
+        }
 
         /**
          * Return the low address as a dotted IP address.
@@ -131,7 +145,9 @@
          * 
          * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address
          */
-        public String getLowAddress()               { return format(toArray(low())); }
+        public String getLowAddress() {
+            return format(toArray(low()));
+        }
 
         /**
          * Return the high address as a dotted IP address.
@@ -139,7 +155,9 @@
          * 
          * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address
          */
-        public String getHighAddress()              { return format(toArray(high())); }
+        public String getHighAddress() {
+            return format(toArray(high()));
+        }
         
         /**
          * Get the count of available addresses.
@@ -151,7 +169,9 @@
             return count < 0 ? 0 : count;
         }
 
-        public int asInteger(String address)        { return toInteger(address); }
+        public int asInteger(String address) {
+            return toInteger(address);
+        }
 
         public String getCidrSignature() {
             return toCidrNotation(
diff --git a/src/main/java/org/apache/commons/net/util/TrustManagerUtils.java b/src/main/java/org/apache/commons/net/util/TrustManagerUtils.java
index f3376ac..88271a8 100644
--- a/src/main/java/org/apache/commons/net/util/TrustManagerUtils.java
+++ b/src/main/java/org/apache/commons/net/util/TrustManagerUtils.java
@@ -54,9 +54,9 @@
             throws CertificateException
         {
             if (checkServerValidity) {
-                for (int i = 0; i < certificates.length; ++i)
+                for (X509Certificate certificate : certificates)
                 {
-                    certificates[i].checkValidity();
+                    certificate.checkValidity();
                 }
             }
         }
diff --git a/src/site/xdoc/download_net.xml b/src/site/xdoc/download_net.xml
index 63278e4..c8b6d4a 100644
--- a/src/site/xdoc/download_net.xml
+++ b/src/site/xdoc/download_net.xml
@@ -54,7 +54,7 @@
       <p>
         We recommend you use a mirror to download our release
         builds, but you <strong>must</strong> verify the integrity of
-        the downloaded files using signatures downloaded from our main 
+        the downloaded files using signatures downloaded from our main
         distribution directories. Recent releases (48 hours) may not yet
         be available from the mirrors.
       </p>
@@ -71,7 +71,7 @@
 
       <form action="[location]" method="get" id="SelectMirror">
         <p>
-          Other mirrors: 
+          Other mirrors:
           <select name="Preferred">
           [if-any http]
             [for http]<option value="[http]">[http]</option>[end]
@@ -90,37 +90,37 @@
       <p>
         The <a href="http://www.apache.org/dist/commons/KEYS">KEYS</a>
         link links to the code signing keys used to sign the product.
-        The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site. 
+        The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site.
         The <code>MD5</code> link downloads the checksum from the main site.
       </p>
     </subsection>
     </section>
-    <section name="Commons Net 3.0.1 (Requires Java 1.5 or later)">
+    <section name="Commons Net 3.2 (Requires Java 1.5 or later)">
       <subsection name="Binaries">
         <table>
           <tr>
-              <td><a href="[preferred]/commons/net/binaries/commons-net-3.0.1-bin.tar.gz">commons-net-3.0.1-bin.tar.gz</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.0.1-bin.tar.gz.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.0.1-bin.tar.gz.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/net/binaries/commons-net-3.2-bin.tar.gz">commons-net-3.2-bin.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.2-bin.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.2-bin.tar.gz.asc">pgp</a></td>
           </tr>
           <tr>
-              <td><a href="[preferred]/commons/net/binaries/commons-net-3.0.1-bin.zip">commons-net-3.0.1-bin.zip</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.0.1-bin.zip.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.0.1-bin.zip.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/net/binaries/commons-net-3.2-bin.zip">commons-net-3.2-bin.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.2-bin.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/binaries/commons-net-3.2-bin.zip.asc">pgp</a></td>
           </tr>
         </table>
       </subsection>
       <subsection name="Source">
         <table>
           <tr>
-              <td><a href="[preferred]/commons/net/source/commons-net-3.0.1-src.tar.gz">commons-net-3.0.1-src.tar.gz</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.0.1-src.tar.gz.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.0.1-src.tar.gz.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/net/source/commons-net-3.2-src.tar.gz">commons-net-3.2-src.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.2-src.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.2-src.tar.gz.asc">pgp</a></td>
           </tr>
           <tr>
-              <td><a href="[preferred]/commons/net/source/commons-net-3.0.1-src.zip">commons-net-3.0.1-src.zip</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.0.1-src.zip.md5">md5</a></td>
-              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.0.1-src.zip.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/net/source/commons-net-3.2-src.zip">commons-net-3.2-src.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.2-src.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/net/source/commons-net-3.2-src.zip.asc">pgp</a></td>
           </tr>
         </table>
       </subsection>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
index c0303e7..35bb7cd 100644
--- a/src/site/xdoc/index.xml
+++ b/src/site/xdoc/index.xml
@@ -80,9 +80,14 @@
            Commons NET includes several working sample applications that you can use.
            Source files are included in the source (and binary) archives, and a compiled jar is provided.
        </p>
-           To use one of the sample applications, ensure that you have both the examples and main jars on the classpath.
-           For example:
-           <pre>java -cp commons-net-examples-3.1.jar;commons-net-3.1.jar examples/ftp/FTPClientExample</pre>
+           To use one of the sample applications, ensure that the example and main jars are both in the same directory.
+           Then run the class as per the following example:
+           <pre>java -jar [path/]commons-net-examples-3.1.jar FTPClientExample [parameters]</pre>
+           This uses the helper application which supports shorthand class names.
+           <br/>
+           Alternatively, ensure that the example and main jars are on the classpath.
+           Then invoke the class directly, for example:
+           <pre>java -cp commons-net-examples-3.1.jar;commons-net-3.1.jar examples/ftp/FTPClientExample [parameters]</pre>
 
        <subsection name="FTP (package: examples/ftp)">
            <ul>
@@ -112,22 +117,11 @@
            </ul>
        </subsection>
 
-       <subsection name="MAIL (package: examples/mail)">
+       <subsection name="NNTP (package: examples/nntp)">
            <ul>
-               <li><a href="examples/mail/IMAPMail.java">IMAPMail</a>
-                   This is an example program demonstrating how to use the IMAP[S]Client class.
+               <li><a href="examples/nntp/ArticleReader.java">ArticleReader</a>
+                   Simple class showing one way to read an article header and body.
                </li>
-               <li><a href="examples/mail/POP3Mail.java">POP3Mail</a>
-                   This is an example program demonstrating how to use the POP3[S]Client class.
-               </li>
-               <li><a href="examples/mail/SMTPMail.java">SMTPMail</a>
-                   This is an example program demonstrating how to use the SMTP[S]Client class.
-               </li>
-           </ul>
-       </subsection>
-
-       <subsection name="MAIL (package: examples/nntp)">
-           <ul>
                <li><a href="examples/nntp/ExtendedNNTPOps.java">ExtendedNNTPOps</a>
                    Simple class showing some of the extended commands (AUTH, XOVER, LIST ACTIVE)
                </li>
@@ -148,7 +142,7 @@
            </ul>
        </subsection>
 
-       <subsection name="MAIL (package: examples/ntp)">
+       <subsection name="NTP (package: examples/ntp)">
            <ul>
                <li><a href="examples/ntp/NTPClient.java">NTPClient</a>
                    This is an example program demonstrating how to use the NTPUDPClient
@@ -167,7 +161,7 @@
            </ul>
        </subsection>
 
-       <subsection name="MAIL (package: examples/telnet)">
+       <subsection name="TELNET (package: examples/telnet)">
            <ul>
                <li><a href="examples/telnet/TelnetClientExample.java">TelnetClientExample</a>
                    This is a simple example of use of TelnetClient.
diff --git a/src/test/java/org/apache/commons/net/SocketClientFunctionalTest.java b/src/test/java/org/apache/commons/net/SocketClientFunctionalTest.java
new file mode 100644
index 0000000..fdd3166
--- /dev/null
+++ b/src/test/java/org/apache/commons/net/SocketClientFunctionalTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.net;
+
+import junit.framework.TestCase;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+
+import org.apache.commons.net.ftp.FTPClient;
+
+/**
+ * A simple functional test class for SocketClients.
+ * 
+ * Requires a Java-compatible SOCK proxy server on 127.0.0.1:9050 and access to ftp.gnu.org.
+ */
+public class SocketClientFunctionalTest extends TestCase
+{
+    // any subclass will do, but it should be able to connect to the destination host
+    SocketClient sc = new FTPClient();
+    private static final String PROXY_HOST = "127.0.0.1";
+    private static final int PROXY_PORT = 9050;
+    private static final String DEST_HOST = "ftp.gnu.org";
+    private static final int DEST_PORT = 21;
+
+    /**
+     * The constructor for this test case.
+     * @param name passed to TestCase
+     */
+    public SocketClientFunctionalTest(String name)
+    {
+        super(name);
+    }
+
+    /**
+     * A simple test to verify that the Proxy settings work.
+     * @throws Exception in case of connection errors
+     */
+    public void testProxySettings() throws Exception
+    {
+        // NOTE: HTTP Proxies seem to be invalid for raw sockets
+        Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
+        sc.setProxy(proxy);
+        sc.connect(DEST_HOST, DEST_PORT);
+        assertTrue(sc.isConnected());
+        sc.disconnect();
+    }
+}
+
diff --git a/src/test/java/org/apache/commons/net/SocketClientTest.java b/src/test/java/org/apache/commons/net/SocketClientTest.java
new file mode 100644
index 0000000..8b4f435
--- /dev/null
+++ b/src/test/java/org/apache/commons/net/SocketClientTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.net;
+
+import junit.framework.TestCase;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+
+import org.apache.commons.net.ftp.FTPClient;
+
+/**
+ * A simple test class for SocketClient settings.
+ * 
+ * @since 3.2
+ */
+public class SocketClientTest extends TestCase
+{
+    private static final String PROXY_HOST = "127.0.0.1";
+    private static final int PROXY_PORT = 1080;
+
+    /**
+     * A simple test to verify that the Proxy is being set.
+     */
+    public void testProxySettings()
+    {
+        SocketClient socketClient = new FTPClient();
+        assertNull(socketClient.getProxy());
+        Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
+        socketClient.setProxy(proxy);
+        assertEquals(proxy, socketClient.getProxy());
+        assertFalse(socketClient.isConnected());
+    }
+}
diff --git a/src/test/java/org/apache/commons/net/ftp/FTPClientConfigFunctionalTest.java b/src/test/java/org/apache/commons/net/ftp/FTPClientConfigFunctionalTest.java
index 0ec9d5c..b6be747 100644
--- a/src/test/java/org/apache/commons/net/ftp/FTPClientConfigFunctionalTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/FTPClientConfigFunctionalTest.java
@@ -110,13 +110,14 @@
         });
 
 
-        for (int i=0; i < files.length; i++) {
+        for (FTPFile file : files)
+        {
             // The directory contains a few additional files at the beginning
             // which aren't in the series we want. The series we want consists
             // of files named sn.dddd. This adjusts the file list to get rid
             // of the uninteresting ones.
-            if (files[i].getName().startsWith("sn")) {
-                sorted.add(files[i]);
+            if (file.getName().startsWith("sn")) {
+                sorted.add(file);
             }
         }
         return sorted;
@@ -143,18 +144,21 @@
             lastfile = thisfile;
         }
 
-        // test that notwithstanding any time zone differences, the newest file
-        // is older than now.
-        assertTrue(lastfile.getTimestamp().getTime().before(now));
-        Calendar first = firstfile.getTimestamp();
-
-        // test that the oldest is less than two days older than the newest
-        // and, in particular, that no files have been considered "future"
-        // by the parser and therefore been relegated to the same date a
-        // year ago.
-        first.add(Calendar.DATE, 2);
-        assertTrue(lastfile.getTimestamp().getTime().toString()+" before "+ first.getTime().toString(),lastfile.getTimestamp().before(first));
-
+        if (firstfile == null || lastfile == null)  {
+            fail("No files found");
+        } else {
+            // test that notwithstanding any time zone differences, the newest file
+            // is older than now.
+            assertTrue(lastfile.getTimestamp().getTime().before(now));
+            Calendar first = firstfile.getTimestamp();
+    
+            // test that the oldest is less than two days older than the newest
+            // and, in particular, that no files have been considered "future"
+            // by the parser and therefore been relegated to the same date a
+            // year ago.
+            first.add(Calendar.DATE, 2);
+            assertTrue(lastfile.getTimestamp().getTime().toString()+" before "+ first.getTime().toString(),lastfile.getTimestamp().before(first));
+        }
     }
 }
 
diff --git a/src/test/java/org/apache/commons/net/ftp/ListingFunctionalTest.java b/src/test/java/org/apache/commons/net/ftp/ListingFunctionalTest.java
index b74fa52..c7e27ac 100644
--- a/src/test/java/org/apache/commons/net/ftp/ListingFunctionalTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/ListingFunctionalTest.java
@@ -68,19 +68,17 @@
         Method[] methods = clasz.getDeclaredMethods();
         TestSuite allSuites = new TestSuite("FTP Listing Functional Test Suite");
 
-        for (int i = 0; i < testData.length; i++)
+        for (String[] element : testData)
         {
-            TestSuite suite = new TestSuite(testData[i][VALID_PARSERKEY]+ " @ " +testData[i][HOSTNAME]);
+            TestSuite suite = new TestSuite(element[VALID_PARSERKEY]+ " @ " +element[HOSTNAME]);
 
-            for (int j = 0; j < methods.length; j++)
+            for (Method method : methods)
             {
-                Method method = methods[j];
-
                 if (method.getName().startsWith("test"))
                 {
                     suite.addTest(new ListingFunctionalTest(
                                                             method.getName(),
-                                                            testData[i]));
+                                                            element));
                 }
             }
 
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/CompositeFTPParseTestFramework.java b/src/test/java/org/apache/commons/net/ftp/parser/CompositeFTPParseTestFramework.java
index 7971065..db9d7b1 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/CompositeFTPParseTestFramework.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/CompositeFTPParseTestFramework.java
@@ -21,7 +21,7 @@
 
 /**
  * @author <a href="mario@ops.co.at">MarioIvankovits</a>
- * @version $Id: CompositeFTPParseTestFramework.java 155429 2005-02-26 13:13:04Z dirkv $
+ * @version $Id$
  */
 public abstract class CompositeFTPParseTestFramework extends FTPParseTestFramework
 {
@@ -76,12 +76,12 @@
     {
         String goodsamples[][] = getGoodListings();
 
-        for (int i = 0; i < goodsamples.length; i++)
+        for (String[] goodsample : goodsamples)
         {
             FTPFileEntryParser parser = getParser();
-            for (int j = 0; j < goodsamples[i].length; j++)
+            for (int j = 0; j < goodsample.length; j++)
             {
-                String test = goodsamples[i][j];
+                String test = goodsample[j];
                 FTPFile f = parser.parseFTPEntry(test);
                 assertNotNull("Failed to parse " + test,
                         f);
@@ -99,12 +99,12 @@
     {
         String badsamples[][] = getBadListings();
 
-        for (int i = 0; i < badsamples.length; i++)
+        for (String[] badsample : badsamples)
         {
             FTPFileEntryParser parser = getParser();
-            for (int j = 0; j < badsamples[i].length; j++)
+            for (int j = 0; j < badsample.length; j++)
             {
-                String test = badsamples[i][j];
+                String test = badsample[j];
                 FTPFile f = parser.parseFTPEntry(test);
                 assertNull("Should have Failed to parse " + test,
                         nullFileOrNullDate(f));
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/DownloadListings.java b/src/test/java/org/apache/commons/net/ftp/parser/DownloadListings.java
index b7e639a..5ea767b 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/DownloadListings.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/DownloadListings.java
@@ -127,5 +127,6 @@
             }
         }
         os.close();
+        rdr.close();
     }
 }
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParserTest.java
index 71bf372..ccf3f89 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParserTest.java
@@ -24,7 +24,7 @@
 /**
  * Tests the EnterpriseUnixFTPEntryParser
  *
- * @version $Id: EnterpriseUnixFTPEntryParserTest.java 437134 2006-08-26 09:36:36Z rwinston $
+ * @version $Id$
  * @author <a href="mailto:Winston.Ojeda@qg.com">Winston Ojeda</a>
  */
 public class EnterpriseUnixFTPEntryParserTest extends FTPParseTestFramework
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/FTPParseTestFramework.java b/src/test/java/org/apache/commons/net/ftp/parser/FTPParseTestFramework.java
index e31cf34..72bc60c 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/FTPParseTestFramework.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/FTPParseTestFramework.java
@@ -24,7 +24,7 @@
 
 /**
  * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
- * @version $Id: FTPParseTestFramework.java 437134 2006-08-26 09:36:36Z rwinston $
+ * @version $Id$
  */
 public abstract class FTPParseTestFramework extends TestCase
 {
@@ -48,10 +48,9 @@
     {
 
         String[] badsamples = getBadListing();
-        for (int i = 0; i < badsamples.length; i++)
+        for (String test : badsamples)
         {
 
-            String test = badsamples[i];
             FTPFile f = parser.parseFTPEntry(test);
             assertNull("Should have Failed to parse " + test,
                        nullFileOrNullDate(f));
@@ -69,10 +68,9 @@
     {
 
         String[] goodsamples = getGoodListing();
-        for (int i = 0; i < goodsamples.length; i++)
+        for (String test : goodsamples)
         {
 
-            String test = goodsamples[i];
             FTPFile f = parser.parseFTPEntry(test);
             assertNotNull("Failed to parse " + test,
                           f);
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImplTest.java b/src/test/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImplTest.java
index ac05141..b99920b 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImplTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImplTest.java
@@ -62,13 +62,14 @@
 
     public void testParseTimestampWithSlop() {
         Calendar cal = Calendar.getInstance();
-        cal.add(Calendar.HOUR_OF_DAY, 1);
         cal.set(Calendar.SECOND,0);
         cal.set(Calendar.MILLISECOND,0);
-        Date anHourFromNow = cal.getTime();
-        cal.add(Calendar.DATE, 1);
-        Date anHourFromNowTomorrow = cal.getTime();
-        cal.add(Calendar.DATE, -1);
+
+        Calendar caltemp = (Calendar) cal.clone();
+        caltemp.add(Calendar.HOUR_OF_DAY, 1);
+        Date anHourFromNow = caltemp.getTime();
+        caltemp.add(Calendar.DATE, 1);
+        Date anHourFromNowTomorrow = caltemp.getTime();
 
         FTPTimestampParserImpl parser = new FTPTimestampParserImpl();
 
@@ -96,6 +97,25 @@
         }
     }
 
+    public void testNET444() throws Exception {
+        FTPTimestampParserImpl parser = new FTPTimestampParserImpl();
+        parser.setLenientFutureDates(true);
+        SimpleDateFormat sdf = new SimpleDateFormat(parser.getRecentDateFormatString());
+        GregorianCalendar now = new GregorianCalendar(2012, Calendar.FEBRUARY, 28, 12, 0);
+
+        GregorianCalendar nowplus1 = new GregorianCalendar(2012, Calendar.FEBRUARY, 28, 13, 0);
+        // Create a suitable short date
+        String future1 = sdf.format(nowplus1.getTime());
+        Calendar parsed1 = parser.parseTimestamp(future1, now);
+        assertEquals(nowplus1.get(Calendar.YEAR), parsed1.get(Calendar.YEAR));
+
+        GregorianCalendar nowplus25 = new GregorianCalendar(2012, Calendar.FEBRUARY, 29, 13, 0);
+        // Create a suitable short date
+        String future25 = sdf.format(nowplus25.getTime());
+        Calendar parsed25 = parser.parseTimestamp(future25, now);
+        assertEquals(nowplus25.get(Calendar.YEAR) - 1, parsed25.get(Calendar.YEAR));
+    }
+
     public void testParseTimestampAcrossTimeZones() {
 
 
@@ -173,8 +193,8 @@
                 fail("failed.to.parse.default");
             }
             try {
-                parser.parseTimestamp("f\u00e9v 22 2002");
-                fail("should.have.failed.to.parse.default");
+                Calendar c = parser.parseTimestamp("f\u00e9v 22 2002");
+                fail("should.have.failed.to.parse.default, but was: "+c.getTime().toString());
             } catch (ParseException e) {
                 // this is the success case
             }
@@ -236,34 +256,60 @@
      * Check how short date is interpreted at a given time.
      * Check both with and without lenient future dates
      */
-    private void checkShortParse(String msg, Calendar now, Calendar input) throws ParseException {
-        checkShortParse(msg, now, input, false);
-        checkShortParse(msg, now, input, true);
+    private void checkShortParse(String msg, Calendar serverTime, Calendar input) throws ParseException {
+        checkShortParse(msg, serverTime, input, false);
+        checkShortParse(msg, serverTime, input, true);
     }
 
     /*
+     * Check how short date is interpreted at a given time.
+     * Check both with and without lenient future dates
+     */
+    private void checkShortParse(String msg, Calendar serverTime, Calendar input, Calendar expected) throws ParseException {
+        checkShortParse(msg, serverTime, input, expected, false);
+        checkShortParse(msg, serverTime, input, expected, true);
+    }
+
+    /**
      * Check how short date is interpreted at a given time
      * Check only using specified lenient future dates setting
+     * @param msg identifying message
+     * @param servertime the time at the server
+     * @param input the time to be converted to a short date, parsed and tested against the full time
+     * @param lenient whether to use lenient mode or not.
      */
-    private void checkShortParse(String msg, Calendar now, Calendar input, boolean lenient) throws ParseException {
+    private void checkShortParse(String msg, Calendar servertime, Calendar input, boolean lenient) throws ParseException {
+        checkShortParse(msg, servertime, input, input, lenient);
+    }
+
+    /**
+     * Check how short date is interpreted at a given time
+     * Check only using specified lenient future dates setting
+     * @param msg identifying message
+     * @param servertime the time at the server
+     * @param input the time to be converted to a short date and parsed
+     * @param expected the expected result from parsing
+     * @param lenient whether to use lenient mode or not.
+     */
+    private void checkShortParse(String msg, Calendar servertime, Calendar input, Calendar expected, boolean lenient) throws ParseException {
         FTPTimestampParserImpl parser = new FTPTimestampParserImpl();
         parser.setLenientFutureDates(lenient);
         Format shortFormat = parser.getRecentDateFormat(); // It's expecting this format
-        Format longFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
 
         final String shortDate = shortFormat.format(input.getTime());
-        Calendar output=parser.parseTimestamp(shortDate, now);
+        Calendar output=parser.parseTimestamp(shortDate, servertime);
         int outyear = output.get(Calendar.YEAR);
         int outdom = output.get(Calendar.DAY_OF_MONTH);
         int outmon = output.get(Calendar.MONTH);
-        int inyear = input.get(Calendar.YEAR);
-        int indom = input.get(Calendar.DAY_OF_MONTH);
-        int inmon = input.get(Calendar.MONTH);
+        int inyear = expected.get(Calendar.YEAR);
+        int indom = expected.get(Calendar.DAY_OF_MONTH);
+        int inmon = expected.get(Calendar.MONTH);
         if (indom != outdom || inmon != outmon || inyear != outyear){
-            fail("Test: '"+msg+"' Server="+longFormat.format(now.getTime())
+            Format longFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
+            fail("Test: '"+msg+"' Server="+longFormat.format(servertime.getTime())
                     +". Failed to parse "+shortDate
                     +". Actual "+longFormat.format(output.getTime())
-                    +". Expected "+longFormat.format(input.getTime()));
+                    +". Expected "+longFormat.format(expected.getTime()));
         }
     }
 
@@ -358,13 +404,29 @@
 
     // Test Feb 29 for a known non-leap year - should fail
     public void testFeb29NonLeapYear(){
-        GregorianCalendar now = new GregorianCalendar(1999, Calendar.APRIL, 1, 12, 0);
+        GregorianCalendar server = new GregorianCalendar(1999, Calendar.APRIL, 1, 12, 0);
         // Note: we use a known leap year for the target date to avoid rounding up
+        GregorianCalendar input = new GregorianCalendar(2000, Calendar.FEBRUARY,29);
+        GregorianCalendar expected = new GregorianCalendar(1999, Calendar.FEBRUARY,29);
         try {
-            checkShortParse("Feb 29th 1999",now,new GregorianCalendar(2000, Calendar.FEBRUARY,29));
+            checkShortParse("Feb 29th 1999", server, input, expected, true);
             fail("Should have failed to parse Feb 29th 1999");
-        } catch (ParseException expected) {
+        } catch (ParseException pe) {
         }
+        try {
+            checkShortParse("Feb 29th 1999", server, input, expected, false);
+            fail("Should have failed to parse Feb 29th 1999");
+        } catch (ParseException pe) {
+        }
+    }
+
+    // This test currently fails, because we assume that short dates are +-6months when parsing Feb 29
+    public void DISABLEDtestNET446() throws Exception {
+        GregorianCalendar server = new GregorianCalendar(2001, Calendar.JANUARY, 1, 12, 0);
+        // Note: we use a known leap year for the target date to avoid rounding up
+        GregorianCalendar input = new GregorianCalendar(2000, Calendar.FEBRUARY,29);
+        GregorianCalendar expected = new GregorianCalendar(2000, Calendar.FEBRUARY,29);
+        checkShortParse("Feb 29th 2000", server, input, expected);
     }
 
     public void testParseDec31Lenient() throws Exception {
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParserTest.java
index 32a1409..2aa24c9 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParserTest.java
@@ -29,7 +29,7 @@
  *
  * Created on Apr 6, 2005<br/>
  * @author <a href="mailto:wnoto@openfinance.com">William Noto</a>
- * @version $Id: MVSFTPEntryParserTest.java,v 1.16 2005/01/02 03:17:50 scohen Exp $
+ * @version $Id$
  */
 public class MVSFTPEntryParserTest extends FTPParseTestFramework {
 
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/NTFTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/NTFTPEntryParserTest.java
index c655fb2..47108d0 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/NTFTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/NTFTPEntryParserTest.java
@@ -23,7 +23,7 @@
 
 /**
  * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
- * @version $Id: NTFTPEntryParserTest.java 629276 2008-02-19 23:31:25Z rwinston $
+ * @version $Id$
  */
 public class NTFTPEntryParserTest extends CompositeFTPParseTestFramework
 {
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParserTest.java
index 98bfa90..ed07aad 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParserTest.java
@@ -23,7 +23,7 @@
 
 /**
  * @author <a href="mailto:rwinston@apache.org">Rory Winston</a>
- * @version $Id: NetwareFTPEntryParserTest.java 492109 2007-01-03 11:24:57Z rwinston $
+ * @version $Id$
  */
 public class NetwareFTPEntryParserTest extends FTPParseTestFramework {
 
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParserTest.java
index e63716e..deaa005 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParserTest.java
@@ -21,7 +21,7 @@
 
 /**
  * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
- * @version $Id: OS2FTPEntryParserTest.java 437134 2006-08-26 09:36:36Z rwinston $
+ * @version $Id$
  */
 public class OS2FTPEntryParserTest extends FTPParseTestFramework
 {
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserTest.java
index 4fa157e..3ee1551 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParserTest.java
@@ -22,7 +22,7 @@
 import java.util.Calendar;
 
 /**
- * @version $Id: OS400FTPEntryParserTest.java 155429 2005-02-26 13:13:04Z dirkv $
+ * @version $Id$
  */
 
 public class OS400FTPEntryParserTest extends CompositeFTPParseTestFramework
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParserTest.java
index 6d9c09f..09ac512 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParserTest.java
@@ -23,7 +23,7 @@
 
 /**
  * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
- * @version $Id: UnixFTPEntryParserTest.java 629276 2008-02-19 23:31:25Z rwinston $
+ * @version $Id$
  */
 public class UnixFTPEntryParserTest extends FTPParseTestFramework {
 
diff --git a/src/test/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParserTest.java b/src/test/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParserTest.java
index 0918405..594d849 100644
--- a/src/test/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParserTest.java
+++ b/src/test/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParserTest.java
@@ -26,7 +26,7 @@
 /**
  * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
  * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
- * @version $Id: VMSFTPEntryParserTest.java 657169 2008-05-16 19:07:02Z sebb $
+ * @version $Id$
  */
 public class VMSFTPEntryParserTest extends FTPParseTestFramework
 {
@@ -124,16 +124,18 @@
     }
 
     public void assertFileInListing(FTPFile[] listing, String name) {
-        for (int i = 0; i < listing.length; i++) {
-            if (name.equals(listing[i].getName())) {
+        for (FTPFile element : listing)
+        {
+            if (name.equals(element.getName())) {
                 return;
             }
         }
         fail("File " + name + " not found in supplied listing");
     }
     public void assertFileNotInListing(FTPFile[] listing, String name) {
-        for (int i = 0; i < listing.length; i++) {
-            if (name.equals(listing[i].getName())) {
+        for (FTPFile element : listing)
+        {
+            if (name.equals(element.getName())) {
                 fail("Unexpected File " + name + " found in supplied listing");
             }
         }
diff --git a/src/test/java/org/apache/commons/net/telnet/TelnetClientTest.java b/src/test/java/org/apache/commons/net/telnet/TelnetClientTest.java
index 3d3b9ef..2ea62c5 100644
--- a/src/test/java/org/apache/commons/net/telnet/TelnetClientTest.java
+++ b/src/test/java/org/apache/commons/net/telnet/TelnetClientTest.java
@@ -756,6 +756,7 @@
         assertTrue(test1spy_ok);
         assertTrue(test2spy_ok);
         assertTrue(stopspy_ok);
+        pi.close();
     }
 
     /***
diff --git a/src/test/java/org/apache/commons/net/tftp/TFTPTest.java b/src/test/java/org/apache/commons/net/tftp/TFTPTest.java
index 1f942d4..bdfed5b 100644
--- a/src/test/java/org/apache/commons/net/tftp/TFTPTest.java
+++ b/src/test/java/org/apache/commons/net/tftp/TFTPTest.java
@@ -81,9 +81,9 @@
             {
                 tftpS.shutdown();
             }
-            for (int i = 0; i < files.length; i++)
+            for (File file : files)
             {
-                files[i].delete();
+                file.delete();
             }
         }
         super.tearDown();
diff --git a/src/test/java/org/apache/commons/net/time/TimeTestSimpleServer.java b/src/test/java/org/apache/commons/net/time/TimeTestSimpleServer.java
index 7b3237f..c36b032 100644
--- a/src/test/java/org/apache/commons/net/time/TimeTestSimpleServer.java
+++ b/src/test/java/org/apache/commons/net/time/TimeTestSimpleServer.java
@@ -36,7 +36,7 @@
  *
  * @author Jason Mathews, MITRE Corporation
  *
- * @version $Revision$ $Date$
+ * @version $Revision$
  */
 public class TimeTestSimpleServer implements Runnable
 {
diff --git a/src/test/java/org/apache/commons/net/util/Base64Test.java b/src/test/java/org/apache/commons/net/util/Base64Test.java
new file mode 100644
index 0000000..552fb72
--- /dev/null
+++ b/src/test/java/org/apache/commons/net/util/Base64Test.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+
+package org.apache.commons.net.util;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class Base64Test {
+
+    @Test
+    public void testBase64() {
+        Base64 b64 = new Base64();
+        assertFalse(b64.isUrlSafe());
+    }
+
+    @Test
+    public void testBase64Boolean() {
+        Base64 b64 = new Base64(true);
+        assertTrue(b64.isUrlSafe());
+        assertTrue(Arrays.equals(new byte[]{'\r','\n'}, b64.getLineSeparator()));
+    }
+
+    @Test
+    public void testBase64Int() {
+        Base64 b64;
+        b64 = new Base64(8);
+        assertFalse(b64.isUrlSafe());
+        assertEquals(8, b64.getLineLength());
+        b64 = new Base64(11);
+        assertEquals(8, b64.getLineLength());
+    }
+
+    @Test
+    public void testBase64IntByteArray() {
+        Base64 b64;
+        b64 = new Base64(8, new byte[]{});
+        assertFalse(b64.isUrlSafe());
+        assertTrue(Arrays.equals(new byte[]{}, b64.getLineSeparator()));
+    }
+
+    @Test
+    public void testBase64IntByteArrayBoolean() {
+        Base64 b64;
+        b64 = new Base64(8, new byte[]{}, false);
+        assertFalse(b64.isUrlSafe());
+        b64 = new Base64(8, new byte[]{}, true);
+        assertTrue(b64.isUrlSafe());
+    }
+
+    @Test
+    public void testIsBase64() {
+        assertTrue(Base64.isBase64((byte)'b'));
+        assertFalse(Base64.isBase64((byte)' '));
+    }
+
+    @Test
+    public void testIsArrayByteBase64() {
+        assertTrue(Base64.isArrayByteBase64(new byte[]{'b',' '}));
+        assertFalse(Base64.isArrayByteBase64(new byte[]{'?'}));
+    }
+
+    @Test
+    public void testEncodeBase64ByteArray() {
+        byte[] binaryData=null;
+        assertTrue(Arrays.equals(binaryData, Base64.encodeBase64(binaryData)));
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64StringByteArray() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64StringUnChunked() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64StringByteArrayBoolean() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64URLSafe() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64URLSafeString() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64Chunked() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testDecodeObject() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testDecodeString() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testDecodeByteArray() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64ByteArrayBoolean() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeBase64ByteArrayBooleanBoolean() {
+        fail("Not yet implemented");
+    }
+
+    @Test
+    public void testEncodeBase64ByteArrayBooleanBooleanInt() {
+        byte[] binaryData = new byte[]{'1','2','3'};
+        byte[] encoded;
+        encoded = Base64.encodeBase64(binaryData, false, false);
+        assertNotNull(encoded);
+        assertEquals(4, encoded.length);
+        try {
+            Base64.encodeBase64(binaryData, false, false, 3);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            // expected
+        }
+        encoded = Base64.encodeBase64(binaryData, false, false, 4); // NET-483
+        assertNotNull(encoded);
+        assertEquals(4, encoded.length);
+        encoded = Base64.encodeBase64(binaryData, true, false);
+        assertNotNull(encoded);
+        assertEquals(6, encoded.length); // always adds trailer
+        try {
+            Base64.encodeBase64(binaryData, true, false, 5);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            // expected
+        }
+        encoded = Base64.encodeBase64(binaryData, true, false, 6);
+        assertNotNull(encoded);
+        assertEquals(6, encoded.length);
+    }
+
+    @Test @Ignore
+    public void testDecodeBase64String() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testDecodeBase64ByteArray() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeObject() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeToString() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeByteArray() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testDecodeInteger() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testEncodeInteger() {
+        fail("Not yet implemented");
+    }
+
+    @Test @Ignore
+    public void testToIntegerBytes() {
+        fail("Not yet implemented");
+    }
+
+}