001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.net.ftp;
018import java.io.BufferedInputStream;
019import java.io.BufferedOutputStream;
020import java.io.BufferedReader;
021import java.io.BufferedWriter;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.io.OutputStream;
026import java.io.OutputStreamWriter;
027import java.io.Reader;
028import java.net.Inet6Address;
029import java.net.InetAddress;
030import java.net.InetSocketAddress;
031import java.net.ServerSocket;
032import java.net.Socket;
033import java.net.SocketException;
034import java.net.SocketTimeoutException;
035import java.net.UnknownHostException;
036import java.util.ArrayList;
037import java.util.HashMap;
038import java.util.HashSet;
039import java.util.Locale;
040import java.util.Properties;
041import java.util.Random;
042import java.util.Set;
043
044import org.apache.commons.net.MalformedServerReplyException;
045import org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory;
046import org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory;
047import org.apache.commons.net.ftp.parser.MLSxEntryParser;
048import org.apache.commons.net.io.CRLFLineReader;
049import org.apache.commons.net.io.CopyStreamAdapter;
050import org.apache.commons.net.io.CopyStreamEvent;
051import org.apache.commons.net.io.CopyStreamListener;
052import org.apache.commons.net.io.FromNetASCIIInputStream;
053import org.apache.commons.net.io.ToNetASCIIOutputStream;
054import org.apache.commons.net.io.Util;
055
056/**
057 * FTPClient encapsulates all the functionality necessary to store and
058 * retrieve files from an FTP server.  This class takes care of all
059 * low level details of interacting with an FTP server and provides
060 * a convenient higher level interface.  As with all classes derived
061 * from {@link org.apache.commons.net.SocketClient},
062 * you must first connect to the server with
063 * {@link org.apache.commons.net.SocketClient#connect  connect }
064 * before doing anything, and finally
065 * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
066 * after you're completely finished interacting with the server.
067 * Then you need to check the FTP reply code to see if the connection
068 * was successful.  For example:
069 * <pre>
070 *    FTPClient ftp = new FTPClient();
071 *    FTPClientConfig config = new FTPClientConfig();
072 *    config.setXXX(YYY); // change required options
073 *    // for example config.setServerTimeZoneId("Pacific/Pitcairn")
074 *    ftp.configure(config );
075 *    boolean error = false;
076 *    try {
077 *      int reply;
078 *      String server = "ftp.example.com";
079 *      ftp.connect(server);
080 *      System.out.println("Connected to " + server + ".");
081 *      System.out.print(ftp.getReplyString());
082 *
083 *      // After connection attempt, you should check the reply code to verify
084 *      // success.
085 *      reply = ftp.getReplyCode();
086 *
087 *      if(!FTPReply.isPositiveCompletion(reply)) {
088 *        ftp.disconnect();
089 *        System.err.println("FTP server refused connection.");
090 *        System.exit(1);
091 *      }
092 *      ... // transfer files
093 *      ftp.logout();
094 *    } catch(IOException e) {
095 *      error = true;
096 *      e.printStackTrace();
097 *    } finally {
098 *      if(ftp.isConnected()) {
099 *        try {
100 *          ftp.disconnect();
101 *        } catch(IOException ioe) {
102 *          // do nothing
103 *        }
104 *      }
105 *      System.exit(error ? 1 : 0);
106 *    }
107 * </pre>
108 * <p>
109 * Immediately after connecting is the only real time you need to check the
110 * reply code (because connect is of type void).  The convention for all the
111 * FTP command methods in FTPClient is such that they either return a
112 * boolean value or some other value.
113 * The boolean methods return true on a successful completion reply from
114 * the FTP server and false on a reply resulting in an error condition or
115 * failure.  The methods returning a value other than boolean return a value
116 * containing the higher level data produced by the FTP command, or null if a
117 * reply resulted in an error condition or failure.  If you want to access
118 * the exact FTP reply code causing a success or failure, you must call
119 * {@link org.apache.commons.net.ftp.FTP#getReplyCode  getReplyCode } after
120 * a success or failure.
121 * <p>
122 * The default settings for FTPClient are for it to use
123 * <code> FTP.ASCII_FILE_TYPE </code>,
124 * <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
125 * <code> FTP.STREAM_TRANSFER_MODE </code>, and
126 * <code> FTP.FILE_STRUCTURE </code>.  The only file types directly supported
127 * are <code> FTP.ASCII_FILE_TYPE </code> and
128 * <code> FTP.BINARY_FILE_TYPE </code>.  Because there are at least 4
129 * different EBCDIC encodings, we have opted not to provide direct support
130 * for EBCDIC.  To transfer EBCDIC and other unsupported file types you
131 * must create your own filter InputStreams and OutputStreams and wrap
132 * them around the streams returned or required by the FTPClient methods.
133 * FTPClient uses the {@link ToNetASCIIOutputStream NetASCII}
134 * filter streams to provide transparent handling of ASCII files.  We will
135 * consider incorporating EBCDIC support if there is enough demand.
136 * <p>
137 * <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
138 * <code> FTP.STREAM_TRANSFER_MODE </code>, and
139 * <code> FTP.FILE_STRUCTURE </code> are the only supported formats,
140 * transfer modes, and file structures.
141 * <p>
142 * Because the handling of sockets on different platforms can differ
143 * significantly, the FTPClient automatically issues a new PORT (or EPRT) command
144 * prior to every transfer requiring that the server connect to the client's
145 * data port.  This ensures identical problem-free behavior on Windows, Unix,
146 * and Macintosh platforms.  Additionally, it relieves programmers from
147 * having to issue the PORT (or EPRT) command themselves and dealing with platform
148 * dependent issues.
149 * <p>
150 * Additionally, for security purposes, all data connections to the
151 * client are verified to ensure that they originated from the intended
152 * party (host and port).  If a data connection is initiated by an unexpected
153 * party, the command will close the socket and throw an IOException.  You
154 * may disable this behavior with
155 * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}.
156 * <p>
157 * You should keep in mind that the FTP server may choose to prematurely
158 * close a connection if the client has been idle for longer than a
159 * given time period (usually 900 seconds).  The FTPClient class will detect a
160 * premature FTP server connection closing when it receives a
161 * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
162 *  response to a command.
163 * When that occurs, the FTP class method encountering that reply will throw
164 * an {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
165 * .
166 * <code>FTPConnectionClosedException</code>
167 * is a subclass of <code> IOException </code> and therefore need not be
168 * caught separately, but if you are going to catch it separately, its
169 * catch block must appear before the more general <code> IOException </code>
170 * catch block.  When you encounter an
171 * {@link org.apache.commons.net.ftp.FTPConnectionClosedException}
172 * , you must disconnect the connection with
173 * {@link #disconnect  disconnect() } to properly clean up the
174 * system resources used by FTPClient.  Before disconnecting, you may check the
175 * last reply code and text with
176 * {@link org.apache.commons.net.ftp.FTP#getReplyCode  getReplyCode },
177 * {@link org.apache.commons.net.ftp.FTP#getReplyString  getReplyString },
178 * and
179 * {@link org.apache.commons.net.ftp.FTP#getReplyStrings  getReplyStrings}.
180 * You may avoid server disconnections while the client is idle by
181 * periodically sending NOOP commands to the server.
182 * <p>
183 * Rather than list it separately for each method, we mention here that
184 * every method communicating with the server and throwing an IOException
185 * can also throw a
186 * {@link org.apache.commons.net.MalformedServerReplyException}
187 * , which is a subclass
188 * of IOException.  A MalformedServerReplyException will be thrown when
189 * the reply received from the server deviates enough from the protocol
190 * specification that it cannot be interpreted in a useful manner despite
191 * attempts to be as lenient as possible.
192 * <p>
193 * Listing API Examples
194 * Both paged and unpaged examples of directory listings are available,
195 * as follows:
196 * <p>
197 * Unpaged (whole list) access, using a parser accessible by auto-detect:
198 * <pre>
199 *    FTPClient f = new FTPClient();
200 *    f.connect(server);
201 *    f.login(username, password);
202 *    FTPFile[] files = f.listFiles(directory);
203 * </pre>
204 * <p>
205 * Paged access, using a parser not accessible by auto-detect.  The class
206 * defined in the first parameter of initateListParsing should be derived
207 * from org.apache.commons.net.FTPFileEntryParser:
208 * <pre>
209 *    FTPClient f = new FTPClient();
210 *    f.connect(server);
211 *    f.login(username, password);
212 *    FTPListParseEngine engine =
213 *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
214 *
215 *    while (engine.hasNext()) {
216 *       FTPFile[] files = engine.getNext(25);  // "page size" you want
217 *       //do whatever you want with these files, display them, etc.
218 *       //expensive FTPFile objects not created until needed.
219 *    }
220 * </pre>
221 * <p>
222 * Paged access, using a parser accessible by auto-detect:
223 * <pre>
224 *    FTPClient f = new FTPClient();
225 *    f.connect(server);
226 *    f.login(username, password);
227 *    FTPListParseEngine engine = f.initiateListParsing(directory);
228 *
229 *    while (engine.hasNext()) {
230 *       FTPFile[] files = engine.getNext(25);  // "page size" you want
231 *       //do whatever you want with these files, display them, etc.
232 *       //expensive FTPFile objects not created until needed.
233 *    }
234 * </pre>
235 * <p>
236 * For examples of using FTPClient on servers whose directory listings
237 * <ul>
238 * <li>use languages other than English</li>
239 * <li>use date formats other than the American English "standard" <code>MM d yyyy</code></li>
240 * <li>are in different timezones and you need accurate timestamps for dependency checking
241 *     as in Ant</li>
242 * </ul>see {@link  FTPClientConfig  FTPClientConfig}.
243 * <p>
244 * <b>Control channel keep-alive feature</b>:
245 * <p>
246 * <b>Please note:</b> this does not apply to the methods where the user is responsible for writing or reading
247 * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)}
248 * and the other xxxFileStream methods
249 * <p>
250 * During file transfers, the data connection is busy, but the control connection is idle.
251 * FTP servers know that the control connection is in use, so won't close it through lack of activity,
252 * but it's a lot harder for network routers to know that the control and data connections are associated
253 * with each other.
254 * Some routers may treat the control connection as idle, and disconnect it if the transfer over the data
255 * connection takes longer than the allowable idle time for the router.
256 * <br>
257 * One solution to this is to send a safe command (i.e. NOOP) over the control connection to reset the router's
258 * idle timer. This is enabled as follows:
259 * <pre>
260 *     ftpClient.setControlKeepAliveTimeout(300); // set timeout to 5 minutes
261 * </pre>
262 * This will cause the file upload/download methods to send a NOOP approximately every 5 minutes.
263 * The following public methods support this:
264 * <ul>
265 * <li>{@link #retrieveFile(String, OutputStream)}</li>
266 * <li>{@link #appendFile(String, InputStream)}</li>
267 * <li>{@link #storeFile(String, InputStream)}</li>
268 * <li>{@link #storeUniqueFile(InputStream)}</li>
269 * <li>{@link #storeUniqueFileStream(String)}</li>
270 * </ul>
271 * This feature does not apply to the methods where the user is responsible for writing or reading
272 * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)}
273 * and the other xxxFileStream methods.
274 * In such cases, the user is responsible for keeping the control connection alive if necessary.
275 * <p>
276 * The implementation currently uses a {@link CopyStreamListener} which is passed to the
277 * {@link Util#copyStream(InputStream, OutputStream, int, long, CopyStreamListener, boolean)}
278 * method, so the timing is partially dependent on how long each block transfer takes.
279 * <p>
280 * <b>This keep-alive feature is optional; if it does not help or causes problems then don't use it.</b>
281 *
282 * @see #FTP_SYSTEM_TYPE
283 * @see #SYSTEM_TYPE_PROPERTIES
284 * @see FTP
285 * @see FTPConnectionClosedException
286 * @see FTPFileEntryParser
287 * @see FTPFileEntryParserFactory
288 * @see DefaultFTPFileEntryParserFactory
289 * @see FTPClientConfig
290 *
291 * @see org.apache.commons.net.MalformedServerReplyException
292 */
293public class FTPClient extends FTP
294implements Configurable
295{
296    /**
297     * The system property ({@value}) which can be used to override the system type.<br>
298     * If defined, the value will be used to create any automatically created parsers.
299     *
300     * @since 3.0
301     */
302    public static final String FTP_SYSTEM_TYPE = "org.apache.commons.net.ftp.systemType";
303
304    /**
305     * The system property ({@value}) which can be used as the default system type.<br>
306     * If defined, the value will be used if the SYST command fails.
307     *
308     * @since 3.1
309     */
310    public static final String FTP_SYSTEM_TYPE_DEFAULT = "org.apache.commons.net.ftp.systemType.default";
311
312    /**
313     * The name of an optional systemType properties file ({@value}), which is loaded
314     * using {@link Class#getResourceAsStream(String)}.<br>
315     * The entries are the systemType (as determined by {@link FTPClient#getSystemType})
316     * and the values are the replacement type or parserClass, which is passed to
317     * {@link FTPFileEntryParserFactory#createFileEntryParser(String)}.<br>
318     * For example:
319     * <pre>
320     * Plan 9=Unix
321     * OS410=org.apache.commons.net.ftp.parser.OS400FTPEntryParser
322     * </pre>
323     *
324     * @since 3.0
325     */
326    public static final String SYSTEM_TYPE_PROPERTIES = "/systemType.properties";
327
328    /**
329     * A constant indicating the FTP session is expecting all transfers
330     * to occur between the client (local) and server and that the server
331     * should connect to the client's data port to initiate a data transfer.
332     * This is the default data connection mode when and FTPClient instance
333     * is created.
334     */
335    public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0;
336    /**
337     * A constant indicating the FTP session is expecting all transfers
338     * to occur between two remote servers and that the server
339     * the client is connected to should connect to the other server's
340     * data port to initiate a data transfer.
341     */
342    public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1;
343    /**
344     * A constant indicating the FTP session is expecting all transfers
345     * to occur between the client (local) and server and that the server
346     * is in passive mode, requiring the client to connect to the
347     * server's data port to initiate a transfer.
348     */
349    public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2;
350    /**
351     * A constant indicating the FTP session is expecting all transfers
352     * to occur between two remote servers and that the server
353     * the client is connected to is in passive mode, requiring the other
354     * server to connect to the first server's data port to initiate a data
355     * transfer.
356     */
357    public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3;
358
359    private int __dataConnectionMode;
360    private int __dataTimeout;
361    private int __passivePort;
362    private String __passiveHost;
363    private final Random __random;
364    private int __activeMinPort;
365    private int __activeMaxPort;
366    private InetAddress __activeExternalHost;
367    private InetAddress __reportActiveExternalHost; // overrides __activeExternalHost in EPRT/PORT commands
368    /** The address to bind to on passive connections, if necessary. */
369    private InetAddress __passiveLocalHost;
370
371    private int __fileType;
372    @SuppressWarnings("unused") // fields are written, but currently not read
373    private int __fileFormat;
374    @SuppressWarnings("unused") // field is written, but currently not read
375    private int __fileStructure;
376    @SuppressWarnings("unused") // field is written, but currently not read
377    private int __fileTransferMode;
378    private boolean __remoteVerificationEnabled;
379    private long __restartOffset;
380    private FTPFileEntryParserFactory __parserFactory;
381    private int __bufferSize; // buffersize for buffered data streams
382    private int __sendDataSocketBufferSize;
383    private int __receiveDataSocketBufferSize;
384    private boolean __listHiddenFiles;
385    private boolean __useEPSVwithIPv4; // whether to attempt EPSV with an IPv4 connection
386
387    // __systemName is a cached value that should not be referenced directly
388    // except when assigned in getSystemName and __initDefaults.
389    private String __systemName;
390
391    // __entryParser is a cached value that should not be referenced directly
392    // except when assigned in listFiles(String, String) and __initDefaults.
393    private FTPFileEntryParser __entryParser;
394
395    // Key used to create the parser; necessary to ensure that the parser type is not ignored
396    private String __entryParserKey;
397
398    private FTPClientConfig __configuration;
399
400    // Listener used by store/retrieve methods to handle keepalive
401    private CopyStreamListener __copyStreamListener;
402
403    // How long to wait before sending another control keep-alive message
404    private long __controlKeepAliveTimeout;
405
406    // How long to wait (ms) for keepalive message replies before continuing
407    // Most FTP servers don't seem to support concurrent control and data connection usage
408    private int __controlKeepAliveReplyTimeout=1000;
409
410    // Debug counts for NOOP acks
411    private int[] __cslDebug;
412
413    /**
414     * Enable or disable replacement of internal IP in passive mode. Default enabled
415     * using {code NatServerResolverImpl}.
416     */
417    private HostnameResolver __passiveNatWorkaroundStrategy = new NatServerResolverImpl(this);
418
419    /** Pattern for PASV mode responses. Groups: (n,n,n,n),(n),(n) */
420    private static final java.util.regex.Pattern __PARMS_PAT;
421
422    static {
423        __PARMS_PAT = java.util.regex.Pattern.compile(
424                "(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})");
425    }
426
427    /** Controls the automatic server encoding detection (only UTF-8 supported). */
428    private boolean __autodetectEncoding = false;
429
430    /** Map of FEAT responses. If null, has not been initialised. */
431    private HashMap<String, Set<String>> __featuresMap;
432
433    private static class PropertiesSingleton {
434
435        static final Properties PROPERTIES;
436
437        static {
438            InputStream resourceAsStream = FTPClient.class.getResourceAsStream(SYSTEM_TYPE_PROPERTIES);
439            Properties p = null;
440            if (resourceAsStream != null) {
441                p = new Properties();
442                try {
443                    p.load(resourceAsStream);
444                } catch (IOException e) {
445                    // Ignored
446                } finally {
447                    try {
448                        resourceAsStream.close();
449                    } catch (IOException e) {
450                        // Ignored
451                    }
452                }
453            }
454            PROPERTIES = p;
455        }
456
457    }
458    private static Properties getOverrideProperties(){
459        return PropertiesSingleton.PROPERTIES;
460    }
461
462    /**
463     * Default FTPClient constructor.  Creates a new FTPClient instance
464     * with the data connection mode set to
465     * <code> ACTIVE_LOCAL_DATA_CONNECTION_MODE </code>, the file type
466     * set to <code> FTP.ASCII_FILE_TYPE </code>, the
467     * file format set to <code> FTP.NON_PRINT_TEXT_FORMAT </code>,
468     * the file structure set to <code> FTP.FILE_STRUCTURE </code>, and
469     * the transfer mode set to <code> FTP.STREAM_TRANSFER_MODE </code>.
470     * <p>
471     * The list parsing auto-detect feature can be configured to use lenient future
472     * dates (short dates may be up to one day in the future) as follows:
473     * <pre>
474     * FTPClient ftp = new FTPClient();
475     * FTPClientConfig config = new FTPClientConfig();
476     * config.setLenientFutureDates(true);
477     * ftp.configure(config );
478     * </pre>
479     */
480    public FTPClient()
481    {
482        __initDefaults();
483        __dataTimeout = -1;
484        __remoteVerificationEnabled = true;
485        __parserFactory = new DefaultFTPFileEntryParserFactory();
486        __configuration      = null;
487        __listHiddenFiles = false;
488        __useEPSVwithIPv4 = false;
489        __random = new Random();
490        __passiveLocalHost   = null;
491    }
492
493
494    private void __initDefaults()
495    {
496        __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
497        __passiveHost        = null;
498        __passivePort        = -1;
499        __activeExternalHost = null;
500        __reportActiveExternalHost = null;
501        __activeMinPort = 0;
502        __activeMaxPort = 0;
503        __fileType           = FTP.ASCII_FILE_TYPE;
504        __fileStructure      = FTP.FILE_STRUCTURE;
505        __fileFormat         = FTP.NON_PRINT_TEXT_FORMAT;
506        __fileTransferMode   = FTP.STREAM_TRANSFER_MODE;
507        __restartOffset      = 0;
508        __systemName         = null;
509        __entryParser        = null;
510        __entryParserKey    = "";
511        __featuresMap = null;
512    }
513
514    /**
515     * Parse the pathname from a CWD reply.
516     * <p>
517     * According to RFC959 (http://www.ietf.org/rfc/rfc959.txt),
518     * it should be the same as for MKD i.e.
519     * {@code 257<space>"<directory-name>"[<space>commentary]}
520     * where any double-quotes in {@code <directory-name>} are doubled.
521     * Unlike MKD, the commentary is optional.
522     * <p>
523     * However, see NET-442 for an exception.
524     *
525     * @param reply
526     * @return the pathname, without enclosing quotes,
527     * or the full string after the reply code and space if the syntax is invalid
528     * (i.e. enclosing quotes are missing or embedded quotes are not doubled)
529     */
530    // package protected for access by test cases
531    static String __parsePathname(String reply)
532    {
533        String param = reply.substring(REPLY_CODE_LEN + 1);
534        if (param.startsWith("\"")) {
535            StringBuilder sb = new StringBuilder();
536            boolean quoteSeen = false;
537            // start after initial quote
538            for(int i=1; i < param.length(); i++) {
539                char ch = param.charAt(i);
540                if (ch=='"') {
541                    if (quoteSeen) {
542                        sb.append(ch);
543                        quoteSeen=false;
544                    } else {
545                        // don't output yet, in case doubled
546                        quoteSeen=true;
547                    }
548                } else {
549                    if (quoteSeen) { // found lone trailing quote within string
550                        return sb.toString();
551                    }
552                    sb.append(ch); // just another character
553                }
554            }
555            if (quoteSeen) { // found lone trailing quote at end of string
556                return sb.toString();
557            }
558        }
559        // malformed reply, return all after reply code and space
560        return param;
561    }
562
563    /**
564     * @since 3.1
565     * @param reply the reply to parse
566     * @throws MalformedServerReplyException if the server reply does not match  (n,n,n,n),(n),(n)
567     */
568    protected void _parsePassiveModeReply(String reply)
569    throws MalformedServerReplyException
570    {
571        java.util.regex.Matcher m = __PARMS_PAT.matcher(reply);
572        if (!m.find()) {
573            throw new MalformedServerReplyException(
574                    "Could not parse passive host information.\nServer Reply: " + reply);
575        }
576
577        __passiveHost = "0,0,0,0".equals(m.group(1)) ? _socket_.getInetAddress().getHostAddress() :
578                m.group(1).replace(',', '.'); // Fix up to look like IP address
579
580        try
581        {
582            int oct1 = Integer.parseInt(m.group(2));
583            int oct2 = Integer.parseInt(m.group(3));
584            __passivePort = (oct1 << 8) | oct2;
585        }
586        catch (NumberFormatException e)
587        {
588            throw new MalformedServerReplyException(
589                    "Could not parse passive port information.\nServer Reply: " + reply);
590        }
591
592        if (__passiveNatWorkaroundStrategy != null) {
593            try {
594                String passiveHost = __passiveNatWorkaroundStrategy.resolve(__passiveHost);
595                if (!__passiveHost.equals(passiveHost)) {
596                    fireReplyReceived(0,
597                            "[Replacing PASV mode reply address "+__passiveHost+" with "+passiveHost+"]\n");
598                    __passiveHost = passiveHost;
599                }
600            } catch (UnknownHostException e) { // Should not happen as we are passing in an IP address
601                throw new MalformedServerReplyException(
602                        "Could not parse passive host information.\nServer Reply: " + reply);
603            }
604        }
605    }
606
607    protected void _parseExtendedPassiveModeReply(String reply)
608    throws MalformedServerReplyException
609    {
610        reply = reply.substring(reply.indexOf('(') + 1,
611                reply.indexOf(')')).trim();
612
613        char delim1, delim2, delim3, delim4;
614        delim1 = reply.charAt(0);
615        delim2 = reply.charAt(1);
616        delim3 = reply.charAt(2);
617        delim4 = reply.charAt(reply.length()-1);
618
619        if (!(delim1 == delim2) || !(delim2 == delim3)
620                || !(delim3 == delim4)) {
621            throw new MalformedServerReplyException(
622                    "Could not parse extended passive host information.\nServer Reply: " + reply);
623        }
624
625        int port;
626        try
627        {
628            port = Integer.parseInt(reply.substring(3, reply.length()-1));
629        }
630        catch (NumberFormatException e)
631        {
632            throw new MalformedServerReplyException(
633                    "Could not parse extended passive host information.\nServer Reply: " + reply);
634        }
635
636
637        // in EPSV mode, the passive host address is implicit
638        __passiveHost = getRemoteAddress().getHostAddress();
639        __passivePort = port;
640    }
641
642    private boolean __storeFile(FTPCmd command, String remote, InputStream local)
643    throws IOException
644    {
645        return _storeFile(command.getCommand(), remote, local);
646    }
647
648    /**
649     * @since 3.1
650     * @param command the command to send
651     * @param remote the remote file name
652     * @param local The local InputStream from which to read the data to
653     *                be written/appended to the remote file.
654     * @return true if successful
655     * @throws IOException on error
656     */
657    protected boolean _storeFile(String command, String remote, InputStream local)
658    throws IOException
659    {
660        Socket socket = _openDataConnection_(command, remote);
661
662        if (socket == null) {
663            return false;
664        }
665
666        final OutputStream output;
667
668        if (__fileType == ASCII_FILE_TYPE) {
669            output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream()));
670        } else {
671            output = getBufferedOutputStream(socket.getOutputStream());
672        }
673
674        CSL csl = null;
675        if (__controlKeepAliveTimeout > 0) {
676            csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout);
677        }
678
679        // Treat everything else as binary for now
680        try
681        {
682            Util.copyStream(local, output, getBufferSize(),
683                    CopyStreamEvent.UNKNOWN_STREAM_SIZE, __mergeListeners(csl),
684                    false);
685            output.close(); // ensure the file is fully written
686            socket.close(); // done writing the file
687
688            // Get the transfer response
689            return completePendingCommand();
690        }
691        catch (IOException e)
692        {
693            Util.closeQuietly(output); // ignore close errors here
694            Util.closeQuietly(socket); // ignore close errors here
695            throw e;
696        } finally {
697            if (csl != null) {
698                __cslDebug = csl.cleanUp(); // fetch any outstanding keepalive replies
699            }
700        }
701    }
702
703    private OutputStream __storeFileStream(FTPCmd command, String remote)
704    throws IOException
705    {
706        return _storeFileStream(command.getCommand(), remote);
707    }
708
709    /**
710     * @param command the command to send
711     * @param remote the remote file name
712     * @return the output stream to write to
713     * @throws IOException on error
714     * @since 3.1
715     */
716    protected OutputStream _storeFileStream(String command, String remote)
717    throws IOException
718    {
719        Socket socket = _openDataConnection_(command, remote);
720
721        if (socket == null) {
722            return null;
723        }
724
725        final OutputStream output;
726        if (__fileType == ASCII_FILE_TYPE) {
727            // We buffer ascii transfers because the buffering has to
728            // be interposed between ToNetASCIIOutputSream and the underlying
729            // socket output stream.  We don't buffer binary transfers
730            // because we don't want to impose a buffering policy on the
731            // programmer if possible.  Programmers can decide on their
732            // own if they want to wrap the SocketOutputStream we return
733            // for file types other than ASCII.
734            output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream()));
735        } else {
736            output = socket.getOutputStream();
737        }
738        return new org.apache.commons.net.io.SocketOutputStream(socket, output);
739    }
740
741
742    /**
743     * Establishes a data connection with the FTP server, returning
744     * a Socket for the connection if successful.  If a restart
745     * offset has been set with {@link #setRestartOffset(long)},
746     * a REST command is issued to the server with the offset as
747     * an argument before establishing the data connection.  Active
748     * mode connections also cause a local PORT command to be issued.
749     *
750     * @param command  The int representation of the FTP command to send.
751     * @param arg The arguments to the FTP command.  If this parameter is
752     *             set to null, then the command is sent with no argument.
753     * @return A Socket corresponding to the established data connection.
754     *         Null is returned if an FTP protocol error is reported at
755     *         any point during the establishment and initialization of
756     *         the connection.
757     * @throws IOException  If an I/O error occurs while either sending a
758     *      command to the server or receiving a reply from the server.
759     * @deprecated (3.3) Use {@link #_openDataConnection_(FTPCmd, String)} instead
760     */
761    @Deprecated
762    protected Socket _openDataConnection_(int command, String arg)
763    throws IOException
764    {
765        return _openDataConnection_(FTPCommand.getCommand(command), arg);
766    }
767
768    /**
769     * Establishes a data connection with the FTP server, returning
770     * a Socket for the connection if successful.  If a restart
771     * offset has been set with {@link #setRestartOffset(long)},
772     * a REST command is issued to the server with the offset as
773     * an argument before establishing the data connection.  Active
774     * mode connections also cause a local PORT command to be issued.
775     *
776     * @param command  The int representation of the FTP command to send.
777     * @param arg The arguments to the FTP command.  If this parameter is
778     *             set to null, then the command is sent with no argument.
779     * @return A Socket corresponding to the established data connection.
780     *         Null is returned if an FTP protocol error is reported at
781     *         any point during the establishment and initialization of
782     *         the connection.
783     * @throws IOException  If an I/O error occurs while either sending a
784     *      command to the server or receiving a reply from the server.
785     * @since 3.3
786     */
787    protected Socket _openDataConnection_(FTPCmd command, String arg)
788    throws IOException
789    {
790        return _openDataConnection_(command.getCommand(), arg);
791    }
792
793    /**
794     * Establishes a data connection with the FTP server, returning
795     * a Socket for the connection if successful.  If a restart
796     * offset has been set with {@link #setRestartOffset(long)},
797     * a REST command is issued to the server with the offset as
798     * an argument before establishing the data connection.  Active
799     * mode connections also cause a local PORT command to be issued.
800     *
801     * @param command  The text representation of the FTP command to send.
802     * @param arg The arguments to the FTP command.  If this parameter is
803     *             set to null, then the command is sent with no argument.
804     * @return A Socket corresponding to the established data connection.
805     *         Null is returned if an FTP protocol error is reported at
806     *         any point during the establishment and initialization of
807     *         the connection.
808     * @throws IOException  If an I/O error occurs while either sending a
809     *      command to the server or receiving a reply from the server.
810     * @since 3.1
811     */
812    protected Socket _openDataConnection_(String command, String arg)
813    throws IOException
814    {
815        if (__dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE &&
816                __dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE) {
817            return null;
818        }
819
820        final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address;
821
822        Socket socket;
823
824        if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE)
825        {
826            // if no activePortRange was set (correctly) -> getActivePort() = 0
827            // -> new ServerSocket(0) -> bind to any free local port
828            ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress());
829
830            try {
831                // Try EPRT only if remote server is over IPv6, if not use PORT,
832                // because EPRT has no advantage over PORT on IPv4.
833                // It could even have the disadvantage,
834                // that EPRT will make the data connection fail, because
835                // today's intelligent NAT Firewalls are able to
836                // substitute IP addresses in the PORT command,
837                // but might not be able to recognize the EPRT command.
838                if (isInet6Address) {
839                    if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) {
840                        return null;
841                    }
842                } else {
843                    if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) {
844                        return null;
845                    }
846                }
847
848                if ((__restartOffset > 0) && !restart(__restartOffset)) {
849                    return null;
850                }
851
852                if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
853                    return null;
854                }
855
856                // For now, let's just use the data timeout value for waiting for
857                // the data connection.  It may be desirable to let this be a
858                // separately configurable value.  In any case, we really want
859                // to allow preventing the accept from blocking indefinitely.
860                if (__dataTimeout >= 0) {
861                    server.setSoTimeout(__dataTimeout);
862                }
863                socket = server.accept();
864
865                // Ensure the timeout is set before any commands are issued on the new socket
866                if (__dataTimeout >= 0) {
867                    socket.setSoTimeout(__dataTimeout);
868                }
869                if (__receiveDataSocketBufferSize > 0) {
870                    socket.setReceiveBufferSize(__receiveDataSocketBufferSize);
871                }
872                if (__sendDataSocketBufferSize > 0) {
873                    socket.setSendBufferSize(__sendDataSocketBufferSize);
874                }
875            } finally {
876                server.close();
877            }
878        }
879        else
880        { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
881
882            // Try EPSV command first on IPv6 - and IPv4 if enabled.
883            // When using IPv4 with NAT it has the advantage
884            // to work with more rare configurations.
885            // E.g. if FTP server has a static PASV address (external network)
886            // and the client is coming from another internal network.
887            // In that case the data connection after PASV command would fail,
888            // while EPSV would make the client succeed by taking just the port.
889            boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
890            if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE)
891            {
892                _parseExtendedPassiveModeReply(_replyLines.get(0));
893            }
894            else
895            {
896                if (isInet6Address) {
897                    return null; // Must use EPSV for IPV6
898                }
899                // If EPSV failed on IPV4, revert to PASV
900                if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
901                    return null;
902                }
903                _parsePassiveModeReply(_replyLines.get(0));
904            }
905
906            socket = _socketFactory_.createSocket();
907            if (__receiveDataSocketBufferSize > 0) {
908                socket.setReceiveBufferSize(__receiveDataSocketBufferSize);
909            }
910            if (__sendDataSocketBufferSize > 0) {
911                socket.setSendBufferSize(__sendDataSocketBufferSize);
912            }
913            if (__passiveLocalHost != null) {
914                socket.bind(new InetSocketAddress(__passiveLocalHost, 0));
915            }
916
917            // For now, let's just use the data timeout value for waiting for
918            // the data connection.  It may be desirable to let this be a
919            // separately configurable value.  In any case, we really want
920            // to allow preventing the accept from blocking indefinitely.
921            if (__dataTimeout >= 0) {
922                socket.setSoTimeout(__dataTimeout);
923            }
924
925            socket.connect(new InetSocketAddress(__passiveHost, __passivePort), connectTimeout);
926            if ((__restartOffset > 0) && !restart(__restartOffset))
927            {
928                socket.close();
929                return null;
930            }
931
932            if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
933            {
934                socket.close();
935                return null;
936            }
937        }
938
939        if (__remoteVerificationEnabled && !verifyRemote(socket))
940        {
941            // Grab the host before we close the socket to avoid NET-663
942            InetAddress socketHost = socket.getInetAddress();
943
944            socket.close();
945
946            throw new IOException(
947                    "Host attempting data connection " + socketHost.getHostAddress() +
948                    " is not same as server " + getRemoteAddress().getHostAddress());
949        }
950
951        return socket;
952    }
953
954
955    @Override
956    protected void _connectAction_() throws IOException
957    {
958        _connectAction_(null);
959    }
960
961
962    /**
963     * @param socketIsReader the reader to reuse (if non-null)
964     * @throws IOException on error
965     * @since 3.4
966     */
967    @Override
968    protected void _connectAction_(Reader socketIsReader) throws IOException
969    {
970        super._connectAction_(socketIsReader); // sets up _input_ and _output_
971        __initDefaults();
972        // must be after super._connectAction_(), because otherwise we get an
973        // Exception claiming we're not connected
974        if ( __autodetectEncoding )
975        {
976            ArrayList<String> oldReplyLines = new ArrayList<String> (_replyLines);
977            int oldReplyCode = _replyCode;
978            if ( hasFeature("UTF8") || hasFeature("UTF-8")) // UTF8 appears to be the default
979            {
980                 setControlEncoding("UTF-8");
981                 _controlInput_ =
982                     new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding()));
983                 _controlOutput_ =
984                    new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding()));
985            }
986            // restore the original reply (server greeting)
987            _replyLines.clear();
988            _replyLines.addAll(oldReplyLines);
989            _replyCode = oldReplyCode;
990            _newReplyString = true;
991        }
992    }
993
994
995    /**
996     * Sets the timeout in milliseconds to use when reading from the
997     * data connection.  This timeout will be set immediately after
998     * opening the data connection, provided that the value is &ge; 0.
999     * <p>
1000     * <b>Note:</b> the timeout will also be applied when calling accept()
1001     * whilst establishing an active local data connection.
1002     * @param  timeout The default timeout in milliseconds that is used when
1003     *        opening a data connection socket. The value 0 means an infinite timeout.
1004     */
1005    public void setDataTimeout(int timeout)
1006    {
1007        __dataTimeout = timeout;
1008    }
1009
1010    /**
1011     * set the factory used for parser creation to the supplied factory object.
1012     *
1013     * @param parserFactory
1014     *               factory object used to create FTPFileEntryParsers
1015     *
1016     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
1017     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
1018     */
1019    public void setParserFactory(FTPFileEntryParserFactory parserFactory) {
1020        __parserFactory = parserFactory;
1021    }
1022
1023
1024    /**
1025     * Closes the connection to the FTP server and restores
1026     * connection parameters to the default values.
1027     *
1028     * @throws IOException If an error occurs while disconnecting.
1029     */
1030    @Override
1031    public void disconnect() throws IOException
1032    {
1033        super.disconnect();
1034        __initDefaults();
1035    }
1036
1037
1038    /**
1039     * Enable or disable verification that the remote host taking part
1040     * of a data connection is the same as the host to which the control
1041     * connection is attached.  The default is for verification to be
1042     * enabled.  You may set this value at any time, whether the
1043     * FTPClient is currently connected or not.
1044     *
1045     * @param enable True to enable verification, false to disable verification.
1046     */
1047    public void setRemoteVerificationEnabled(boolean enable)
1048    {
1049        __remoteVerificationEnabled = enable;
1050    }
1051
1052    /**
1053     * Return whether or not verification of the remote host participating
1054     * in data connections is enabled.  The default behavior is for
1055     * verification to be enabled.
1056     *
1057     * @return True if verification is enabled, false if not.
1058     */
1059    public boolean isRemoteVerificationEnabled()
1060    {
1061        return __remoteVerificationEnabled;
1062    }
1063
1064    /**
1065     * Login to the FTP server using the provided username and password.
1066     *
1067     * @param username The username to login under.
1068     * @param password The password to use.
1069     * @return True if successfully completed, false if not.
1070     * @throws FTPConnectionClosedException
1071     *      If the FTP server prematurely closes the connection as a result
1072     *      of the client being idle or some other reason causing the server
1073     *      to send FTP reply code 421.  This exception may be caught either
1074     *      as an IOException or independently as itself.
1075     * @throws IOException  If an I/O error occurs while either sending a
1076     *      command to the server or receiving a reply from the server.
1077     */
1078    public boolean login(String username, String password) throws IOException
1079    {
1080
1081        user(username);
1082
1083        if (FTPReply.isPositiveCompletion(_replyCode)) {
1084            return true;
1085        }
1086
1087        // If we get here, we either have an error code, or an intermmediate
1088        // reply requesting password.
1089        if (!FTPReply.isPositiveIntermediate(_replyCode)) {
1090            return false;
1091        }
1092
1093        return FTPReply.isPositiveCompletion(pass(password));
1094    }
1095
1096
1097    /**
1098     * Login to the FTP server using the provided username, password,
1099     * and account.  If no account is required by the server, only
1100     * the username and password, the account information is not used.
1101     *
1102     * @param username The username to login under.
1103     * @param password The password to use.
1104     * @param account  The account to use.
1105     * @return True if successfully completed, false if not.
1106     * @throws FTPConnectionClosedException
1107     *      If the FTP server prematurely closes the connection as a result
1108     *      of the client being idle or some other reason causing the server
1109     *      to send FTP reply code 421.  This exception may be caught either
1110     *      as an IOException or independently as itself.
1111     * @throws IOException  If an I/O error occurs while either sending a
1112     *      command to the server or receiving a reply from the server.
1113     */
1114    public boolean login(String username, String password, String account)
1115    throws IOException
1116    {
1117        user(username);
1118
1119        if (FTPReply.isPositiveCompletion(_replyCode)) {
1120            return true;
1121        }
1122
1123        // If we get here, we either have an error code, or an intermmediate
1124        // reply requesting password.
1125        if (!FTPReply.isPositiveIntermediate(_replyCode)) {
1126            return false;
1127        }
1128
1129        pass(password);
1130
1131        if (FTPReply.isPositiveCompletion(_replyCode)) {
1132            return true;
1133        }
1134
1135        if (!FTPReply.isPositiveIntermediate(_replyCode)) {
1136            return false;
1137        }
1138
1139        return FTPReply.isPositiveCompletion(acct(account));
1140    }
1141
1142    /**
1143     * Logout of the FTP server by sending the QUIT command.
1144     *
1145     * @return True if successfully completed, false if not.
1146     * @throws FTPConnectionClosedException
1147     *      If the FTP server prematurely closes the connection as a result
1148     *      of the client being idle or some other reason causing the server
1149     *      to send FTP reply code 421.  This exception may be caught either
1150     *      as an IOException or independently as itself.
1151     * @throws IOException  If an I/O error occurs while either sending a
1152     *      command to the server or receiving a reply from the server.
1153     */
1154    public boolean logout() throws IOException
1155    {
1156        return FTPReply.isPositiveCompletion(quit());
1157    }
1158
1159
1160    /**
1161     * Change the current working directory of the FTP session.
1162     *
1163     * @param pathname  The new current working directory.
1164     * @return True if successfully completed, false if not.
1165     * @throws FTPConnectionClosedException
1166     *      If the FTP server prematurely closes the connection as a result
1167     *      of the client being idle or some other reason causing the server
1168     *      to send FTP reply code 421.  This exception may be caught either
1169     *      as an IOException or independently as itself.
1170     * @throws IOException  If an I/O error occurs while either sending a
1171     *      command to the server or receiving a reply from the server.
1172     */
1173    public boolean changeWorkingDirectory(String pathname) throws IOException
1174    {
1175        return FTPReply.isPositiveCompletion(cwd(pathname));
1176    }
1177
1178
1179    /**
1180     * Change to the parent directory of the current working directory.
1181     *
1182     * @return True if successfully completed, false if not.
1183     * @throws FTPConnectionClosedException
1184     *      If the FTP server prematurely closes the connection as a result
1185     *      of the client being idle or some other reason causing the server
1186     *      to send FTP reply code 421.  This exception may be caught either
1187     *      as an IOException or independently as itself.
1188     * @throws IOException  If an I/O error occurs while either sending a
1189     *      command to the server or receiving a reply from the server.
1190     */
1191    public boolean changeToParentDirectory() throws IOException
1192    {
1193        return FTPReply.isPositiveCompletion(cdup());
1194    }
1195
1196
1197    /**
1198     * Issue the FTP SMNT command.
1199     *
1200     * @param pathname The pathname to mount.
1201     * @return True if successfully completed, false if not.
1202     * @throws FTPConnectionClosedException
1203     *      If the FTP server prematurely closes the connection as a result
1204     *      of the client being idle or some other reason causing the server
1205     *      to send FTP reply code 421.  This exception may be caught either
1206     *      as an IOException or independently as itself.
1207     * @throws IOException  If an I/O error occurs while either sending a
1208     *      command to the server or receiving a reply from the server.
1209     */
1210    public boolean structureMount(String pathname) throws IOException
1211    {
1212        return FTPReply.isPositiveCompletion(smnt(pathname));
1213    }
1214
1215    /**
1216     * Reinitialize the FTP session.  Not all FTP servers support this
1217     * command, which issues the FTP REIN command.
1218     *
1219     * @return True if successfully completed, false if not.
1220     * @throws FTPConnectionClosedException
1221     *      If the FTP server prematurely closes the connection as a result
1222     *      of the client being idle or some other reason causing the server
1223     *      to send FTP reply code 421.  This exception may be caught either
1224     *      as an IOException or independently as itself.
1225     * @throws IOException  If an I/O error occurs while either sending a
1226     *      command to the server or receiving a reply from the server.
1227     * @since 3.4 (made public)
1228     */
1229    public boolean reinitialize() throws IOException
1230    {
1231        rein();
1232
1233        if (FTPReply.isPositiveCompletion(_replyCode) ||
1234                (FTPReply.isPositivePreliminary(_replyCode) &&
1235                        FTPReply.isPositiveCompletion(getReply())))
1236        {
1237
1238            __initDefaults();
1239
1240            return true;
1241        }
1242
1243        return false;
1244    }
1245
1246
1247    /**
1248     * Set the current data connection mode to
1249     * <code>ACTIVE_LOCAL_DATA_CONNECTION_MODE</code>.  No communication
1250     * with the FTP server is conducted, but this causes all future data
1251     * transfers to require the FTP server to connect to the client's
1252     * data port.  Additionally, to accommodate differences between socket
1253     * implementations on different platforms, this method causes the
1254     * client to issue a PORT command before every data transfer.
1255     */
1256    public void enterLocalActiveMode()
1257    {
1258        __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
1259        __passiveHost = null;
1260        __passivePort = -1;
1261    }
1262
1263
1264    /**
1265     * Set the current data connection mode to
1266     * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code>.  Use this
1267     * method only for data transfers between the client and server.
1268     * This method causes a PASV (or EPSV) command to be issued to the server
1269     * before the opening of every data connection, telling the server to
1270     * open a data port to which the client will connect to conduct
1271     * data transfers.  The FTPClient will stay in
1272     * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code> until the
1273     * mode is changed by calling some other method such as
1274     * {@link #enterLocalActiveMode  enterLocalActiveMode() }
1275     * <p>
1276     * <b>N.B.</b> currently calling any connect method will reset the mode to
1277     * ACTIVE_LOCAL_DATA_CONNECTION_MODE.
1278     */
1279    public void enterLocalPassiveMode()
1280    {
1281        __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE;
1282        // These will be set when just before a data connection is opened
1283        // in _openDataConnection_()
1284        __passiveHost = null;
1285        __passivePort = -1;
1286    }
1287
1288
1289    /**
1290     * Set the current data connection mode to
1291     * <code> ACTIVE_REMOTE_DATA_CONNECTION </code>.  Use this method only
1292     * for server to server data transfers.  This method issues a PORT
1293     * command to the server, indicating the other server and port to which
1294     * it should connect for data transfers.  You must call this method
1295     * before EVERY server to server transfer attempt.  The FTPClient will
1296     * NOT automatically continue to issue PORT commands.  You also
1297     * must remember to call
1298     * {@link #enterLocalActiveMode  enterLocalActiveMode() } if you
1299     * wish to return to the normal data connection mode.
1300     *
1301     * @param host The passive mode server accepting connections for data
1302     *             transfers.
1303     * @param port The passive mode server's data port.
1304     * @return True if successfully completed, false if not.
1305     * @throws FTPConnectionClosedException
1306     *      If the FTP server prematurely closes the connection as a result
1307     *      of the client being idle or some other reason causing the server
1308     *      to send FTP reply code 421.  This exception may be caught either
1309     *      as an IOException or independently as itself.
1310     * @throws IOException  If an I/O error occurs while either sending a
1311     *      command to the server or receiving a reply from the server.
1312     */
1313    public boolean enterRemoteActiveMode(InetAddress host, int port)
1314    throws IOException
1315    {
1316        if (FTPReply.isPositiveCompletion(port(host, port)))
1317        {
1318            __dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE;
1319            __passiveHost = null;
1320            __passivePort = -1;
1321            return true;
1322        }
1323        return false;
1324    }
1325
1326    /**
1327     * Set the current data connection mode to
1328     * <code> PASSIVE_REMOTE_DATA_CONNECTION_MODE </code>.  Use this
1329     * method only for server to server data transfers.
1330     * This method issues a PASV command to the server, telling it to
1331     * open a data port to which the active server will connect to conduct
1332     * data transfers.  You must call this method
1333     * before EVERY server to server transfer attempt.  The FTPClient will
1334     * NOT automatically continue to issue PASV commands.  You also
1335     * must remember to call
1336     * {@link #enterLocalActiveMode  enterLocalActiveMode() } if you
1337     * wish to return to the normal data connection mode.
1338     *
1339     * @return True if successfully completed, false if not.
1340     * @throws FTPConnectionClosedException
1341     *      If the FTP server prematurely closes the connection as a result
1342     *      of the client being idle or some other reason causing the server
1343     *      to send FTP reply code 421.  This exception may be caught either
1344     *      as an IOException or independently as itself.
1345     * @throws IOException  If an I/O error occurs while either sending a
1346     *      command to the server or receiving a reply from the server.
1347     */
1348    public boolean enterRemotePassiveMode() throws IOException
1349    {
1350        if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
1351            return false;
1352        }
1353
1354        __dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE;
1355        _parsePassiveModeReply(_replyLines.get(0));
1356
1357        return true;
1358    }
1359
1360    /**
1361     * Returns the hostname or IP address (in the form of a string) returned
1362     * by the server when entering passive mode.  If not in passive mode,
1363     * returns null.  This method only returns a valid value AFTER a
1364     * data connection has been opened after a call to
1365     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1366     * This is because FTPClient sends a PASV command to the server only
1367     * just before opening a data connection, and not when you call
1368     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1369     *
1370     * @return The passive host name if in passive mode, otherwise null.
1371     */
1372    public String getPassiveHost()
1373    {
1374        return __passiveHost;
1375    }
1376
1377    /**
1378     * If in passive mode, returns the data port of the passive host.
1379     * This method only returns a valid value AFTER a
1380     * data connection has been opened after a call to
1381     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1382     * This is because FTPClient sends a PASV command to the server only
1383     * just before opening a data connection, and not when you call
1384     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1385     *
1386     * @return The data port of the passive server.  If not in passive
1387     *         mode, undefined.
1388     */
1389    public int getPassivePort()
1390    {
1391        return __passivePort;
1392    }
1393
1394
1395    /**
1396     * Returns the current data connection mode (one of the
1397     * <code> _DATA_CONNECTION_MODE </code> constants.
1398     *
1399     * @return The current data connection mode (one of the
1400     * <code> _DATA_CONNECTION_MODE </code> constants.
1401     */
1402    public int getDataConnectionMode()
1403    {
1404        return __dataConnectionMode;
1405    }
1406
1407    /**
1408     * Get the client port for active mode.
1409     *
1410     * @return The client port for active mode.
1411     */
1412    private int getActivePort()
1413    {
1414        if (__activeMinPort > 0 && __activeMaxPort >= __activeMinPort)
1415        {
1416            if (__activeMaxPort == __activeMinPort) {
1417                return __activeMaxPort;
1418            }
1419            // Get a random port between the min and max port range
1420            return __random.nextInt(__activeMaxPort - __activeMinPort + 1) + __activeMinPort;
1421        }
1422        else
1423        {
1424            // default port
1425            return 0;
1426        }
1427    }
1428
1429    /**
1430     * Get the host address for active mode; allows the local address to be overridden.
1431     *
1432     * @return __activeExternalHost if non-null, else getLocalAddress()
1433     * @see #setActiveExternalIPAddress(String)
1434     */
1435    private InetAddress getHostAddress()
1436    {
1437        if (__activeExternalHost != null)
1438        {
1439            return __activeExternalHost;
1440        }
1441        else
1442        {
1443            // default local address
1444            return getLocalAddress();
1445        }
1446    }
1447
1448    /**
1449     * Get the reported host address for active mode EPRT/PORT commands;
1450     * allows override of {@link #getHostAddress()}.
1451     *
1452     * Useful for FTP Client behind Firewall NAT.
1453     *
1454     * @return __reportActiveExternalHost if non-null, else getHostAddress();
1455     */
1456    private InetAddress getReportHostAddress() {
1457        if (__reportActiveExternalHost != null) {
1458            return __reportActiveExternalHost ;
1459        } else {
1460            return getHostAddress();
1461        }
1462    }
1463
1464    /**
1465     * Set the client side port range in active mode.
1466     *
1467     * @param minPort The lowest available port (inclusive).
1468     * @param maxPort The highest available port (inclusive).
1469     * @since 2.2
1470     */
1471    public void setActivePortRange(int minPort, int maxPort)
1472    {
1473        this.__activeMinPort = minPort;
1474        this.__activeMaxPort = maxPort;
1475    }
1476
1477    /**
1478     * Set the external IP address in active mode.
1479     * Useful when there are multiple network cards.
1480     *
1481     * @param ipAddress The external IP address of this machine.
1482     * @throws UnknownHostException if the ipAddress cannot be resolved
1483     * @since 2.2
1484     */
1485    public void setActiveExternalIPAddress(String ipAddress) throws UnknownHostException
1486    {
1487        this.__activeExternalHost = InetAddress.getByName(ipAddress);
1488    }
1489
1490    /**
1491     * Set the local IP address to use in passive mode.
1492     * Useful when there are multiple network cards.
1493     *
1494     * @param ipAddress The local IP address of this machine.
1495     * @throws UnknownHostException if the ipAddress cannot be resolved
1496     */
1497    public void setPassiveLocalIPAddress(String ipAddress) throws UnknownHostException
1498    {
1499        this.__passiveLocalHost = InetAddress.getByName(ipAddress);
1500    }
1501
1502    /**
1503     * Set the local IP address to use in passive mode.
1504     * Useful when there are multiple network cards.
1505     *
1506     * @param inetAddress The local IP address of this machine.
1507     */
1508    public void setPassiveLocalIPAddress(InetAddress inetAddress)
1509    {
1510        this.__passiveLocalHost = inetAddress;
1511    }
1512
1513    /**
1514     * Set the local IP address in passive mode.
1515     * Useful when there are multiple network cards.
1516     *
1517     * @return The local IP address in passive mode.
1518     */
1519    public InetAddress getPassiveLocalIPAddress()
1520    {
1521        return this.__passiveLocalHost;
1522    }
1523
1524    /**
1525     * Set the external IP address to report in EPRT/PORT commands in active mode.
1526     * Useful when there are multiple network cards.
1527     *
1528     * @param ipAddress The external IP address of this machine.
1529     * @throws UnknownHostException if the ipAddress cannot be resolved
1530     * @since 3.1
1531     * @see #getReportHostAddress()
1532     */
1533    public void setReportActiveExternalIPAddress(String ipAddress) throws UnknownHostException
1534    {
1535        this.__reportActiveExternalHost = InetAddress.getByName(ipAddress);
1536    }
1537
1538
1539    /**
1540     * Sets the file type to be transferred.  This should be one of
1541     * <code> FTP.ASCII_FILE_TYPE </code>, <code> FTP.BINARY_FILE_TYPE</code>,
1542     * etc.  The file type only needs to be set when you want to change the
1543     * type.  After changing it, the new type stays in effect until you change
1544     * it again.  The default file type is <code> FTP.ASCII_FILE_TYPE </code>
1545     * if this method is never called.
1546     * <br>
1547     * The server default is supposed to be ASCII (see RFC 959), however many
1548     * ftp servers default to BINARY. <b>To ensure correct operation with all servers,
1549     * always specify the appropriate file type after connecting to the server.</b>
1550     * <br>
1551     * <p>
1552     * <b>N.B.</b> currently calling any connect method will reset the type to
1553     * FTP.ASCII_FILE_TYPE.
1554     * @param fileType The <code> _FILE_TYPE </code> constant indicating the
1555     *                 type of file.
1556     * @return True if successfully completed, false if not.
1557     * @throws FTPConnectionClosedException
1558     *      If the FTP server prematurely closes the connection as a result
1559     *      of the client being idle or some other reason causing the server
1560     *      to send FTP reply code 421.  This exception may be caught either
1561     *      as an IOException or independently as itself.
1562     * @throws IOException  If an I/O error occurs while either sending a
1563     *      command to the server or receiving a reply from the server.
1564     */
1565    public boolean setFileType(int fileType) throws IOException
1566    {
1567        if (FTPReply.isPositiveCompletion(type(fileType)))
1568        {
1569            __fileType = fileType;
1570            __fileFormat = FTP.NON_PRINT_TEXT_FORMAT;
1571            return true;
1572        }
1573        return false;
1574    }
1575
1576
1577    /**
1578     * Sets the file type to be transferred and the format.  The type should be
1579     * one of  <code> FTP.ASCII_FILE_TYPE </code>,
1580     * <code> FTP.BINARY_FILE_TYPE </code>, etc.  The file type only needs to
1581     * be set when you want to change the type.  After changing it, the new
1582     * type stays in effect until you change it again.  The default file type
1583     * is <code> FTP.ASCII_FILE_TYPE </code> if this method is never called.
1584     * <br>
1585     * The server default is supposed to be ASCII (see RFC 959), however many
1586     * ftp servers default to BINARY. <b>To ensure correct operation with all servers,
1587     * always specify the appropriate file type after connecting to the server.</b>
1588     * <br>
1589     * The format should be one of the FTP class <code> TEXT_FORMAT </code>
1590     * constants, or if the type is <code> FTP.LOCAL_FILE_TYPE </code>, the
1591     * format should be the byte size for that type.  The default format
1592     * is <code> FTP.NON_PRINT_TEXT_FORMAT </code> if this method is never
1593     * called.
1594     * <p>
1595     * <b>N.B.</b> currently calling any connect method will reset the type to
1596     * FTP.ASCII_FILE_TYPE and the formatOrByteSize to FTP.NON_PRINT_TEXT_FORMAT.
1597     *
1598     * @param fileType The <code> _FILE_TYPE </code> constant indicating the
1599     *                 type of file.
1600     * @param formatOrByteSize  The format of the file (one of the
1601     *              <code>_FORMAT</code> constants.  In the case of
1602     *              <code>LOCAL_FILE_TYPE</code>, the byte size.
1603     *
1604     * @return True if successfully completed, false if not.
1605     * @throws FTPConnectionClosedException
1606     *      If the FTP server prematurely closes the connection as a result
1607     *      of the client being idle or some other reason causing the server
1608     *      to send FTP reply code 421.  This exception may be caught either
1609     *      as an IOException or independently as itself.
1610     * @throws IOException  If an I/O error occurs while either sending a
1611     *      command to the server or receiving a reply from the server.
1612     */
1613    public boolean setFileType(int fileType, int formatOrByteSize)
1614    throws IOException
1615    {
1616        if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize)))
1617        {
1618            __fileType = fileType;
1619            __fileFormat = formatOrByteSize;
1620            return true;
1621        }
1622        return false;
1623    }
1624
1625
1626    /**
1627     * Sets the file structure.  The default structure is
1628     * <code> FTP.FILE_STRUCTURE </code> if this method is never called
1629     * or if a connect method is called.
1630     *
1631     * @param structure  The structure of the file (one of the FTP class
1632     *         <code>_STRUCTURE</code> constants).
1633     * @return True if successfully completed, false if not.
1634     * @throws FTPConnectionClosedException
1635     *      If the FTP server prematurely closes the connection as a result
1636     *      of the client being idle or some other reason causing the server
1637     *      to send FTP reply code 421.  This exception may be caught either
1638     *      as an IOException or independently as itself.
1639     * @throws IOException  If an I/O error occurs while either sending a
1640     *      command to the server or receiving a reply from the server.
1641     */
1642    public boolean setFileStructure(int structure) throws IOException
1643    {
1644        if (FTPReply.isPositiveCompletion(stru(structure)))
1645        {
1646            __fileStructure = structure;
1647            return true;
1648        }
1649        return false;
1650    }
1651
1652
1653    /**
1654     * Sets the transfer mode.  The default transfer mode
1655     * <code> FTP.STREAM_TRANSFER_MODE </code> if this method is never called
1656     * or if a connect method is called.
1657     *
1658     * @param mode  The new transfer mode to use (one of the FTP class
1659     *         <code>_TRANSFER_MODE</code> constants).
1660     * @return True if successfully completed, false if not.
1661     * @throws FTPConnectionClosedException
1662     *      If the FTP server prematurely closes the connection as a result
1663     *      of the client being idle or some other reason causing the server
1664     *      to send FTP reply code 421.  This exception may be caught either
1665     *      as an IOException or independently as itself.
1666     * @throws IOException  If an I/O error occurs while either sending a
1667     *      command to the server or receiving a reply from the server.
1668     */
1669    public boolean setFileTransferMode(int mode) throws IOException
1670    {
1671        if (FTPReply.isPositiveCompletion(mode(mode)))
1672        {
1673            __fileTransferMode = mode;
1674            return true;
1675        }
1676        return false;
1677    }
1678
1679
1680    /**
1681     * Initiate a server to server file transfer.  This method tells the
1682     * server to which the client is connected to retrieve a given file from
1683     * the other server.
1684     *
1685     * @param fileName  The name of the file to retrieve.
1686     * @return True if successfully completed, false if not.
1687     * @throws FTPConnectionClosedException
1688     *      If the FTP server prematurely closes the connection as a result
1689     *      of the client being idle or some other reason causing the server
1690     *      to send FTP reply code 421.  This exception may be caught either
1691     *      as an IOException or independently as itself.
1692     * @throws IOException  If an I/O error occurs while either sending a
1693     *      command to the server or receiving a reply from the server.
1694     */
1695    public boolean remoteRetrieve(String fileName) throws IOException
1696    {
1697        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1698                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1699            return FTPReply.isPositivePreliminary(retr(fileName));
1700        }
1701        return false;
1702    }
1703
1704
1705    /**
1706     * Initiate a server to server file transfer.  This method tells the
1707     * server to which the client is connected to store a file on
1708     * the other server using the given file name.  The other server must
1709     * have had a <code> remoteRetrieve </code> issued to it by another
1710     * FTPClient.
1711     *
1712     * @param fileName  The name to call the file that is to be stored.
1713     * @return True if successfully completed, false if not.
1714     * @throws FTPConnectionClosedException
1715     *      If the FTP server prematurely closes the connection as a result
1716     *      of the client being idle or some other reason causing the server
1717     *      to send FTP reply code 421.  This exception may be caught either
1718     *      as an IOException or independently as itself.
1719     * @throws IOException  If an I/O error occurs while either sending a
1720     *      command to the server or receiving a reply from the server.
1721     */
1722    public boolean remoteStore(String fileName) throws IOException
1723    {
1724        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1725                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1726            return FTPReply.isPositivePreliminary(stor(fileName));
1727        }
1728        return false;
1729    }
1730
1731
1732    /**
1733     * Initiate a server to server file transfer.  This method tells the
1734     * server to which the client is connected to store a file on
1735     * the other server using a unique file name based on the given file name.
1736     * The other server must have had a <code> remoteRetrieve </code> issued
1737     * to it by another FTPClient.
1738     *
1739     * @param fileName  The name on which to base the file name of the file
1740     *                  that is to be stored.
1741     * @return True if successfully completed, false if not.
1742     * @throws FTPConnectionClosedException
1743     *      If the FTP server prematurely closes the connection as a result
1744     *      of the client being idle or some other reason causing the server
1745     *      to send FTP reply code 421.  This exception may be caught either
1746     *      as an IOException or independently as itself.
1747     * @throws IOException  If an I/O error occurs while either sending a
1748     *      command to the server or receiving a reply from the server.
1749     */
1750    public boolean remoteStoreUnique(String fileName) throws IOException
1751    {
1752        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1753                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1754            return FTPReply.isPositivePreliminary(stou(fileName));
1755        }
1756        return false;
1757    }
1758
1759
1760    /**
1761     * Initiate a server to server file transfer.  This method tells the
1762     * server to which the client is connected to store a file on
1763     * the other server using a unique file name.
1764     * The other server must have had a <code> remoteRetrieve </code> issued
1765     * to it by another FTPClient.  Many FTP servers require that a base
1766     * file name be given from which the unique file name can be derived.  For
1767     * those servers use the other version of <code> remoteStoreUnique</code>
1768     *
1769     * @return True if successfully completed, false if not.
1770     * @throws FTPConnectionClosedException
1771     *      If the FTP server prematurely closes the connection as a result
1772     *      of the client being idle or some other reason causing the server
1773     *      to send FTP reply code 421.  This exception may be caught either
1774     *      as an IOException or independently as itself.
1775     * @throws IOException  If an I/O error occurs while either sending a
1776     *      command to the server or receiving a reply from the server.
1777     */
1778    public boolean remoteStoreUnique() throws IOException
1779    {
1780        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1781                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1782            return FTPReply.isPositivePreliminary(stou());
1783        }
1784        return false;
1785    }
1786
1787    // For server to server transfers
1788    /**
1789     * Initiate a server to server file transfer.  This method tells the
1790     * server to which the client is connected to append to a given file on
1791     * the other server.  The other server must have had a
1792     * <code> remoteRetrieve </code> issued to it by another FTPClient.
1793     *
1794     * @param fileName  The name of the file to be appended to, or if the
1795     *        file does not exist, the name to call the file being stored.
1796     *
1797     * @return True if successfully completed, false if not.
1798     * @throws FTPConnectionClosedException
1799     *      If the FTP server prematurely closes the connection as a result
1800     *      of the client being idle or some other reason causing the server
1801     *      to send FTP reply code 421.  This exception may be caught either
1802     *      as an IOException or independently as itself.
1803     * @throws IOException  If an I/O error occurs while either sending a
1804     *      command to the server or receiving a reply from the server.
1805     */
1806    public boolean remoteAppend(String fileName) throws IOException
1807    {
1808        if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1809                __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1810            return FTPReply.isPositivePreliminary(appe(fileName));
1811        }
1812        return false;
1813    }
1814
1815    /**
1816     * There are a few FTPClient methods that do not complete the
1817     * entire sequence of FTP commands to complete a transaction.  These
1818     * commands require some action by the programmer after the reception
1819     * of a positive intermediate command.  After the programmer's code
1820     * completes its actions, it must call this method to receive
1821     * the completion reply from the server and verify the success of the
1822     * entire transaction.
1823     * <p>
1824     * For example,
1825     * <pre>
1826     * InputStream input;
1827     * OutputStream output;
1828     * input  = new FileInputStream("foobaz.txt");
1829     * output = ftp.storeFileStream("foobar.txt")
1830     * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
1831     *     input.close();
1832     *     output.close();
1833     *     ftp.logout();
1834     *     ftp.disconnect();
1835     *     System.err.println("File transfer failed.");
1836     *     System.exit(1);
1837     * }
1838     * Util.copyStream(input, output);
1839     * input.close();
1840     * output.close();
1841     * // Must call completePendingCommand() to finish command.
1842     * if(!ftp.completePendingCommand()) {
1843     *     ftp.logout();
1844     *     ftp.disconnect();
1845     *     System.err.println("File transfer failed.");
1846     *     System.exit(1);
1847     * }
1848     * </pre>
1849     *
1850     * @return True if successfully completed, false if not.
1851     * @throws FTPConnectionClosedException
1852     *      If the FTP server prematurely closes the connection as a result
1853     *      of the client being idle or some other reason causing the server
1854     *      to send FTP reply code 421.  This exception may be caught either
1855     *      as an IOException or independently as itself.
1856     * @throws IOException  If an I/O error occurs while either sending a
1857     *      command to the server or receiving a reply from the server.
1858     */
1859    public boolean completePendingCommand() throws IOException
1860    {
1861        return FTPReply.isPositiveCompletion(getReply());
1862    }
1863
1864
1865    /**
1866     * Retrieves a named file from the server and writes it to the given
1867     * OutputStream.  This method does NOT close the given OutputStream.
1868     * If the current file type is ASCII, line separators in the file are
1869     * converted to the local representation.
1870     * <p>
1871     * Note: if you have used {@link #setRestartOffset(long)},
1872     * the file data will start from the selected offset.
1873     * @param remote  The name of the remote file.
1874     * @param local   The local OutputStream to which to write the file.
1875     * @return True if successfully completed, false if not.
1876     * @throws FTPConnectionClosedException
1877     *      If the FTP server prematurely closes the connection as a result
1878     *      of the client being idle or some other reason causing the server
1879     *      to send FTP reply code 421.  This exception may be caught either
1880     *      as an IOException or independently as itself.
1881     * @throws org.apache.commons.net.io.CopyStreamException
1882     *      If an I/O error occurs while actually
1883     *      transferring the file.  The CopyStreamException allows you to
1884     *      determine the number of bytes transferred and the IOException
1885     *      causing the error.  This exception may be caught either
1886     *      as an IOException or independently as itself.
1887     * @throws IOException  If an I/O error occurs while either sending a
1888     *      command to the server or receiving a reply from the server.
1889     */
1890    public boolean retrieveFile(String remote, OutputStream local)
1891    throws IOException
1892    {
1893        return _retrieveFile(FTPCmd.RETR.getCommand(), remote, local);
1894    }
1895
1896    /**
1897     * @param command the command to get
1898     * @param remote the remote file name
1899     * @param local The local OutputStream to which to write the file.
1900     * @return true if successful
1901     * @throws IOException on error
1902     * @since 3.1
1903     */
1904    protected boolean _retrieveFile(String command, String remote, OutputStream local)
1905    throws IOException
1906    {
1907        Socket socket = _openDataConnection_(command, remote);
1908
1909        if (socket == null) {
1910            return false;
1911        }
1912
1913        final InputStream input;
1914        if (__fileType == ASCII_FILE_TYPE) {
1915            input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream()));
1916        } else {
1917            input = getBufferedInputStream(socket.getInputStream());
1918        }
1919
1920        CSL csl = null;
1921        if (__controlKeepAliveTimeout > 0) {
1922            csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout);
1923        }
1924
1925        // Treat everything else as binary for now
1926        try
1927        {
1928            Util.copyStream(input, local, getBufferSize(),
1929                    CopyStreamEvent.UNKNOWN_STREAM_SIZE, __mergeListeners(csl),
1930                    false);
1931
1932            // Get the transfer response
1933            return completePendingCommand();
1934        } finally {
1935            Util.closeQuietly(input);
1936            Util.closeQuietly(socket);
1937            if (csl != null) {
1938                __cslDebug = csl.cleanUp(); // fetch any outstanding keepalive replies
1939            }
1940        }
1941    }
1942
1943    /**
1944     * Returns an InputStream from which a named file from the server
1945     * can be read.  If the current file type is ASCII, the returned
1946     * InputStream will convert line separators in the file to
1947     * the local representation.  You must close the InputStream when you
1948     * finish reading from it.  The InputStream itself will take care of
1949     * closing the parent data connection socket upon being closed.
1950     * <p>
1951     * <b>To finalize the file transfer you must call
1952     * {@link #completePendingCommand  completePendingCommand } and
1953     * check its return value to verify success.</b>
1954     * If this is not done, subsequent commands may behave unexpectedly.
1955     * <p>
1956     * Note: if you have used {@link #setRestartOffset(long)},
1957     * the file data will start from the selected offset.
1958     *
1959     * @param remote  The name of the remote file.
1960     * @return An InputStream from which the remote file can be read.  If
1961     *      the data connection cannot be opened (e.g., the file does not
1962     *      exist), null is returned (in which case you may check the reply
1963     *      code to determine the exact reason for failure).
1964     * @throws FTPConnectionClosedException
1965     *      If the FTP server prematurely closes the connection as a result
1966     *      of the client being idle or some other reason causing the server
1967     *      to send FTP reply code 421.  This exception may be caught either
1968     *      as an IOException or independently as itself.
1969     * @throws IOException  If an I/O error occurs while either sending a
1970     *      command to the server or receiving a reply from the server.
1971     */
1972    public InputStream retrieveFileStream(String remote) throws IOException
1973    {
1974        return _retrieveFileStream(FTPCmd.RETR.getCommand(), remote);
1975    }
1976
1977    /**
1978     * @param command the command to send
1979     * @param remote the remote file name
1980     * @return the stream from which to read the file
1981     * @throws IOException on error
1982     * @since 3.1
1983     */
1984    protected InputStream _retrieveFileStream(String command, String remote)
1985    throws IOException
1986    {
1987        Socket socket = _openDataConnection_(command, remote);
1988
1989        if (socket == null) {
1990            return null;
1991        }
1992
1993        final InputStream input;
1994        if (__fileType == ASCII_FILE_TYPE) {
1995            // We buffer ascii transfers because the buffering has to
1996            // be interposed between FromNetASCIIOutputSream and the underlying
1997            // socket input stream.  We don't buffer binary transfers
1998            // because we don't want to impose a buffering policy on the
1999            // programmer if possible.  Programmers can decide on their
2000            // own if they want to wrap the SocketInputStream we return
2001            // for file types other than ASCII.
2002            input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream()));
2003        } else {
2004            input = socket.getInputStream();
2005        }
2006        return new org.apache.commons.net.io.SocketInputStream(socket, input);
2007    }
2008
2009
2010    /**
2011     * Stores a file on the server using the given name and taking input
2012     * from the given InputStream.  This method does NOT close the given
2013     * InputStream.  If the current file type is ASCII, line separators in
2014     * the file are transparently converted to the NETASCII format (i.e.,
2015     * you should not attempt to create a special InputStream to do this).
2016     *
2017     * @param remote  The name to give the remote file.
2018     * @param local   The local InputStream from which to read the file.
2019     * @return True if successfully completed, false if not.
2020     * @throws FTPConnectionClosedException
2021     *      If the FTP server prematurely closes the connection as a result
2022     *      of the client being idle or some other reason causing the server
2023     *      to send FTP reply code 421.  This exception may be caught either
2024     *      as an IOException or independently as itself.
2025     * @throws org.apache.commons.net.io.CopyStreamException
2026     *      If an I/O error occurs while actually
2027     *      transferring the file.  The CopyStreamException allows you to
2028     *      determine the number of bytes transferred and the IOException
2029     *      causing the error.  This exception may be caught either
2030     *      as an IOException or independently as itself.
2031     * @throws IOException  If an I/O error occurs while either sending a
2032     *      command to the server or receiving a reply from the server.
2033     */
2034    public boolean storeFile(String remote, InputStream local)
2035    throws IOException
2036    {
2037        return __storeFile(FTPCmd.STOR, remote, local);
2038    }
2039
2040
2041    /**
2042     * Returns an OutputStream through which data can be written to store
2043     * a file on the server using the given name.  If the current file type
2044     * is ASCII, the returned OutputStream will convert line separators in
2045     * the file to the NETASCII format  (i.e., you should not attempt to
2046     * create a special OutputStream to do this).  You must close the
2047     * OutputStream when you finish writing to it.  The OutputStream itself
2048     * will take care of closing the parent data connection socket upon being
2049     * closed.
2050     * <p>
2051     * <b>To finalize the file transfer you must call
2052     * {@link #completePendingCommand  completePendingCommand } and
2053     * check its return value to verify success.</b>
2054     * If this is not done, subsequent commands may behave unexpectedly.
2055     *
2056     * @param remote  The name to give the remote file.
2057     * @return An OutputStream through which the remote file can be written.  If
2058     *      the data connection cannot be opened (e.g., the file does not
2059     *      exist), null is returned (in which case you may check the reply
2060     *      code to determine the exact reason for failure).
2061     * @throws FTPConnectionClosedException
2062     *      If the FTP server prematurely closes the connection as a result
2063     *      of the client being idle or some other reason causing the server
2064     *      to send FTP reply code 421.  This exception may be caught either
2065     *      as an IOException or independently as itself.
2066     * @throws IOException  If an I/O error occurs while either sending a
2067     *      command to the server or receiving a reply from the server.
2068     */
2069    public OutputStream storeFileStream(String remote) throws IOException
2070    {
2071        return __storeFileStream(FTPCmd.STOR, remote);
2072    }
2073
2074    /**
2075     * Appends to a file on the server with the given name, taking input
2076     * from the given InputStream.  This method does NOT close the given
2077     * InputStream.  If the current file type is ASCII, line separators in
2078     * the file are transparently converted to the NETASCII format (i.e.,
2079     * you should not attempt to create a special InputStream to do this).
2080     *
2081     * @param remote  The name of the remote file.
2082     * @param local   The local InputStream from which to read the data to
2083     *                be appended to the remote file.
2084     * @return True if successfully completed, false if not.
2085     * @throws FTPConnectionClosedException
2086     *      If the FTP server prematurely closes the connection as a result
2087     *      of the client being idle or some other reason causing the server
2088     *      to send FTP reply code 421.  This exception may be caught either
2089     *      as an IOException or independently as itself.
2090     * @throws org.apache.commons.net.io.CopyStreamException
2091     *      If an I/O error occurs while actually
2092     *      transferring the file.  The CopyStreamException allows you to
2093     *      determine the number of bytes transferred and the IOException
2094     *      causing the error.  This exception may be caught either
2095     *      as an IOException or independently as itself.
2096     * @throws IOException  If an I/O error occurs while either sending a
2097     *      command to the server or receiving a reply from the server.
2098     */
2099    public boolean appendFile(String remote, InputStream local)
2100    throws IOException
2101    {
2102        return __storeFile(FTPCmd.APPE, remote, local);
2103    }
2104
2105    /**
2106     * Returns an OutputStream through which data can be written to append
2107     * to a file on the server with the given name.  If the current file type
2108     * is ASCII, the returned OutputStream will convert line separators in
2109     * the file to the NETASCII format  (i.e., you should not attempt to
2110     * create a special OutputStream to do this).  You must close the
2111     * OutputStream when you finish writing to it.  The OutputStream itself
2112     * will take care of closing the parent data connection socket upon being
2113     * closed.
2114     * <p>
2115     * <b>To finalize the file transfer you must call
2116     * {@link #completePendingCommand  completePendingCommand } and
2117     * check its return value to verify success.</b>
2118     * If this is not done, subsequent commands may behave unexpectedly.
2119     *
2120     * @param remote  The name of the remote file.
2121     * @return An OutputStream through which the remote file can be appended.
2122     *      If the data connection cannot be opened (e.g., the file does not
2123     *      exist), null is returned (in which case you may check the reply
2124     *      code to determine the exact reason for failure).
2125     * @throws FTPConnectionClosedException
2126     *      If the FTP server prematurely closes the connection as a result
2127     *      of the client being idle or some other reason causing the server
2128     *      to send FTP reply code 421.  This exception may be caught either
2129     *      as an IOException or independently as itself.
2130     * @throws IOException  If an I/O error occurs while either sending a
2131     *      command to the server or receiving a reply from the server.
2132     */
2133    public OutputStream appendFileStream(String remote) throws IOException
2134    {
2135        return __storeFileStream(FTPCmd.APPE, remote);
2136    }
2137
2138    /**
2139     * Stores a file on the server using a unique name derived from the
2140     * given name and taking input
2141     * from the given InputStream.  This method does NOT close the given
2142     * InputStream.  If the current file type is ASCII, line separators in
2143     * the file are transparently converted to the NETASCII format (i.e.,
2144     * you should not attempt to create a special InputStream to do this).
2145     *
2146     * @param remote  The name on which to base the unique name given to
2147     *                the remote file.
2148     * @param local   The local InputStream from which to read the file.
2149     * @return True if successfully completed, false if not.
2150     * @throws FTPConnectionClosedException
2151     *      If the FTP server prematurely closes the connection as a result
2152     *      of the client being idle or some other reason causing the server
2153     *      to send FTP reply code 421.  This exception may be caught either
2154     *      as an IOException or independently as itself.
2155     * @throws org.apache.commons.net.io.CopyStreamException
2156     *      If an I/O error occurs while actually
2157     *      transferring the file.  The CopyStreamException allows you to
2158     *      determine the number of bytes transferred and the IOException
2159     *      causing the error.  This exception may be caught either
2160     *      as an IOException or independently as itself.
2161     * @throws IOException  If an I/O error occurs while either sending a
2162     *      command to the server or receiving a reply from the server.
2163     */
2164    public boolean storeUniqueFile(String remote, InputStream local)
2165    throws IOException
2166    {
2167        return __storeFile(FTPCmd.STOU, remote, local);
2168    }
2169
2170
2171    /**
2172     * Returns an OutputStream through which data can be written to store
2173     * a file on the server using a unique name derived from the given name.
2174     * If the current file type
2175     * is ASCII, the returned OutputStream will convert line separators in
2176     * the file to the NETASCII format  (i.e., you should not attempt to
2177     * create a special OutputStream to do this).  You must close the
2178     * OutputStream when you finish writing to it.  The OutputStream itself
2179     * will take care of closing the parent data connection socket upon being
2180     * closed.
2181     * <p>
2182     * <b>To finalize the file transfer you must call
2183     * {@link #completePendingCommand  completePendingCommand } and
2184     * check its return value to verify success.</b>
2185     * If this is not done, subsequent commands may behave unexpectedly.
2186     *
2187     * @param remote  The name on which to base the unique name given to
2188     *                the remote file.
2189     * @return An OutputStream through which the remote file can be written.  If
2190     *      the data connection cannot be opened (e.g., the file does not
2191     *      exist), null is returned (in which case you may check the reply
2192     *      code to determine the exact reason for failure).
2193     * @throws FTPConnectionClosedException
2194     *      If the FTP server prematurely closes the connection as a result
2195     *      of the client being idle or some other reason causing the server
2196     *      to send FTP reply code 421.  This exception may be caught either
2197     *      as an IOException or independently as itself.
2198     * @throws IOException  If an I/O error occurs while either sending a
2199     *      command to the server or receiving a reply from the server.
2200     */
2201    public OutputStream storeUniqueFileStream(String remote) throws IOException
2202    {
2203        return __storeFileStream(FTPCmd.STOU, remote);
2204    }
2205
2206    /**
2207     * Stores a file on the server using a unique name assigned by the
2208     * server and taking input from the given InputStream.  This method does
2209     * NOT close the given
2210     * InputStream.  If the current file type is ASCII, line separators in
2211     * the file are transparently converted to the NETASCII format (i.e.,
2212     * you should not attempt to create a special InputStream to do this).
2213     *
2214     * @param local   The local InputStream from which to read the file.
2215     * @return True if successfully completed, false if not.
2216     * @throws FTPConnectionClosedException
2217     *      If the FTP server prematurely closes the connection as a result
2218     *      of the client being idle or some other reason causing the server
2219     *      to send FTP reply code 421.  This exception may be caught either
2220     *      as an IOException or independently as itself.
2221     * @throws org.apache.commons.net.io.CopyStreamException
2222     *      If an I/O error occurs while actually
2223     *      transferring the file.  The CopyStreamException allows you to
2224     *      determine the number of bytes transferred and the IOException
2225     *      causing the error.  This exception may be caught either
2226     *      as an IOException or independently as itself.
2227     * @throws IOException  If an I/O error occurs while either sending a
2228     *      command to the server or receiving a reply from the server.
2229     */
2230    public boolean storeUniqueFile(InputStream local) throws IOException
2231    {
2232        return __storeFile(FTPCmd.STOU, null, local);
2233    }
2234
2235    /**
2236     * Returns an OutputStream through which data can be written to store
2237     * a file on the server using a unique name assigned by the server.
2238     * If the current file type
2239     * is ASCII, the returned OutputStream will convert line separators in
2240     * the file to the NETASCII format  (i.e., you should not attempt to
2241     * create a special OutputStream to do this).  You must close the
2242     * OutputStream when you finish writing to it.  The OutputStream itself
2243     * will take care of closing the parent data connection socket upon being
2244     * closed.
2245     * <p>
2246     * <b>To finalize the file transfer you must call
2247     * {@link #completePendingCommand  completePendingCommand } and
2248     * check its return value to verify success.</b>
2249     * If this is not done, subsequent commands may behave unexpectedly.
2250     *
2251     * @return An OutputStream through which the remote file can be written.  If
2252     *      the data connection cannot be opened (e.g., the file does not
2253     *      exist), null is returned (in which case you may check the reply
2254     *      code to determine the exact reason for failure).
2255     * @throws FTPConnectionClosedException
2256     *      If the FTP server prematurely closes the connection as a result
2257     *      of the client being idle or some other reason causing the server
2258     *      to send FTP reply code 421.  This exception may be caught either
2259     *      as an IOException or independently as itself.
2260     * @throws IOException  If an I/O error occurs while either sending a
2261     *      command to the server or receiving a reply from the server.
2262     */
2263    public OutputStream storeUniqueFileStream() throws IOException
2264    {
2265        return __storeFileStream(FTPCmd.STOU, null);
2266    }
2267
2268    /**
2269     * Reserve a number of bytes on the server for the next file transfer.
2270     *
2271     * @param bytes  The number of bytes which the server should allocate.
2272     * @return True if successfully completed, false if not.
2273     * @throws FTPConnectionClosedException
2274     *      If the FTP server prematurely closes the connection as a result
2275     *      of the client being idle or some other reason causing the server
2276     *      to send FTP reply code 421.  This exception may be caught either
2277     *      as an IOException or independently as itself.
2278     * @throws IOException  If an I/O error occurs while either sending a
2279     *      command to the server or receiving a reply from the server.
2280     */
2281    public boolean allocate(int bytes) throws IOException
2282    {
2283        return FTPReply.isPositiveCompletion(allo(bytes));
2284    }
2285
2286    /**
2287     * Reserve a number of bytes on the server for the next file transfer.
2288     *
2289     * @param bytes  The number of bytes which the server should allocate.
2290     * @return True if successfully completed, false if not.
2291     * @throws FTPConnectionClosedException
2292     *      If the FTP server prematurely closes the connection as a result
2293     *      of the client being idle or some other reason causing the server
2294     *      to send FTP reply code 421.  This exception may be caught either
2295     *      as an IOException or independently as itself.
2296     * @throws IOException  If an I/O error occurs while either sending a
2297     *      command to the server or receiving a reply from the server.
2298     */
2299    public boolean allocate(long bytes) throws IOException
2300    {
2301        return FTPReply.isPositiveCompletion(allo(bytes));
2302    }
2303
2304    /**
2305     * Query the server for supported features. The server may reply with a list of server-supported exensions.
2306     * For example, a typical client-server interaction might be (from RFC 2389):
2307     * <pre>
2308        C&gt; feat
2309        S&gt; 211-Extensions supported:
2310        S&gt;  MLST size*;create;modify*;perm;media-type
2311        S&gt;  SIZE
2312        S&gt;  COMPRESSION
2313        S&gt;  MDTM
2314        S&gt; 211 END
2315     * </pre>
2316     * @see <a href="http://www.faqs.org/rfcs/rfc2389.html">http://www.faqs.org/rfcs/rfc2389.html</a>
2317     * @return True if successfully completed, false if not.
2318     * @throws IOException on error
2319     * @since 2.2
2320     */
2321    public boolean features() throws IOException {
2322        return FTPReply.isPositiveCompletion(feat());
2323    }
2324
2325    /**
2326     * Query the server for a supported feature, and returns its values (if any).
2327     * Caches the parsed response to avoid resending the command repeatedly.
2328     * @param feature the feature to check
2329     *
2330     * @return if the feature is present, returns the feature values (empty array if none)
2331     * Returns {@code null} if the feature is not found or the command failed.
2332     * Check {@link #getReplyCode()} or {@link #getReplyString()} if so.
2333     * @throws IOException on error
2334     * @since 3.0
2335     */
2336    public String[] featureValues(String feature) throws IOException {
2337        if (!initFeatureMap()) {
2338            return null;
2339        }
2340        Set<String> entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH));
2341        if (entries != null) {
2342            return entries.toArray(new String[entries.size()]);
2343        }
2344        return null;
2345    }
2346
2347    /**
2348     * Query the server for a supported feature, and returns the its value (if any).
2349     * Caches the parsed response to avoid resending the command repeatedly.
2350     * @param feature the feature to check
2351     *
2352     * @return if the feature is present, returns the feature value or the empty string
2353     * if the feature exists but has no value.
2354     * Returns {@code null} if the feature is not found or the command failed.
2355     * Check {@link #getReplyCode()} or {@link #getReplyString()} if so.
2356     * @throws IOException on error
2357     * @since 3.0
2358     */
2359    public String featureValue(String feature) throws IOException {
2360        String [] values = featureValues(feature);
2361        if (values != null) {
2362            return values[0];
2363        }
2364        return null;
2365    }
2366
2367    /**
2368     * Query the server for a supported feature.
2369     * Caches the parsed response to avoid resending the command repeatedly.
2370     *
2371     * @param feature the name of the feature; it is converted to upper case.
2372     * @return {@code true} if the feature is present, {@code false} if the feature is not present
2373     * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link #getReplyString()}
2374     * if it is necessary to distinguish these cases.
2375     *
2376     * @throws IOException on error
2377     * @since 3.0
2378     */
2379    public boolean hasFeature(String feature) throws IOException {
2380        if (!initFeatureMap()) {
2381            return false;
2382        }
2383        return __featuresMap.containsKey(feature.toUpperCase(Locale.ENGLISH));
2384    }
2385
2386    /**
2387     * Query the server for a supported feature with particular value,
2388     * for example "AUTH SSL" or "AUTH TLS".
2389     * Caches the parsed response to avoid resending the command repeatedly.
2390     *
2391     * @param feature the name of the feature; it is converted to upper case.
2392     * @param value the value to find.
2393     *
2394     * @return {@code true} if the feature is present, {@code false} if the feature is not present
2395     * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link #getReplyString()}
2396     * if it is necessary to distinguish these cases.
2397     *
2398     * @throws IOException on error
2399     * @since 3.0
2400     */
2401    public boolean hasFeature(String feature, String value) throws IOException {
2402        if (!initFeatureMap()) {
2403            return false;
2404        }
2405        Set<String> entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH));
2406        if (entries != null) {
2407            return entries.contains(value);
2408        }
2409        return false;
2410    }
2411
2412    /*
2413     * Create the feature map if not already created.
2414     */
2415    private boolean initFeatureMap() throws IOException {
2416        if (__featuresMap == null) {
2417            // Don't create map here, because next line may throw exception
2418            final int replyCode = feat();
2419            if (replyCode == FTPReply.NOT_LOGGED_IN) { // 503
2420                return false; // NET-518; don't create empy map
2421            }
2422            boolean success = FTPReply.isPositiveCompletion(replyCode);
2423            // we init the map here, so we don't keep trying if we know the command will fail
2424            __featuresMap = new HashMap<String, Set<String>>();
2425            if (!success) {
2426                return false;
2427            }
2428            for (String l : getReplyStrings()) {
2429                if (l.startsWith(" ")) { // it's a FEAT entry
2430                    String key;
2431                    String value="";
2432                    int varsep = l.indexOf(' ', 1);
2433                    if (varsep > 0) {
2434                        key = l.substring(1, varsep);
2435                        value = l.substring(varsep+1);
2436                    } else {
2437                        key = l.substring(1);
2438                    }
2439                    key = key.toUpperCase(Locale.ENGLISH);
2440                    Set<String> entries = __featuresMap.get(key);
2441                    if (entries == null) {
2442                        entries = new HashSet<String>();
2443                        __featuresMap.put(key, entries);
2444                    }
2445                    entries.add(value);
2446                }
2447            }
2448        }
2449        return true;
2450    }
2451
2452    /**
2453     * Reserve space on the server for the next file transfer.
2454     *
2455     * @param bytes  The number of bytes which the server should allocate.
2456     * @param recordSize  The size of a file record.
2457     * @return True if successfully completed, false if not.
2458     * @throws FTPConnectionClosedException
2459     *      If the FTP server prematurely closes the connection as a result
2460     *      of the client being idle or some other reason causing the server
2461     *      to send FTP reply code 421.  This exception may be caught either
2462     *      as an IOException or independently as itself.
2463     * @throws IOException  If an I/O error occurs while either sending a
2464     *      command to the server or receiving a reply from the server.
2465     */
2466    public boolean allocate(int bytes, int recordSize) throws IOException
2467    {
2468        return FTPReply.isPositiveCompletion(allo(bytes, recordSize));
2469    }
2470
2471    /**
2472     * Reserve space on the server for the next file transfer.
2473     *
2474     * @param bytes  The number of bytes which the server should allocate.
2475     * @param recordSize  The size of a file record.
2476     * @return True if successfully completed, false if not.
2477     * @throws FTPConnectionClosedException
2478     *      If the FTP server prematurely closes the connection as a result
2479     *      of the client being idle or some other reason causing the server
2480     *      to send FTP reply code 421.  This exception may be caught either
2481     *      as an IOException or independently as itself.
2482     * @throws IOException  If an I/O error occurs while either sending a
2483     *      command to the server or receiving a reply from the server.
2484     */
2485    public boolean allocate(long bytes, int recordSize) throws IOException
2486    {
2487        return FTPReply.isPositiveCompletion(allo(bytes, recordSize));
2488    }
2489
2490
2491    /**
2492     * Issue a command and wait for the reply.
2493     * <p>
2494     * Should only be used with commands that return replies on the
2495     * command channel - do not use for LIST, NLST, MLSD etc.
2496     *
2497     * @param command  The command to invoke
2498     * @param params  The parameters string, may be {@code null}
2499     * @return True if successfully completed, false if not, in which case
2500     * call {@link #getReplyCode()} or {@link #getReplyString()}
2501     * to get the reason.
2502     *
2503     * @throws IOException  If an I/O error occurs while either sending a
2504     *      command to the server or receiving a reply from the server.
2505     * @since 3.0
2506     */
2507    public boolean doCommand(String command, String params) throws IOException
2508    {
2509        return FTPReply.isPositiveCompletion(sendCommand(command, params));
2510    }
2511
2512    /**
2513     * Issue a command and wait for the reply, returning it as an array of strings.
2514     * <p>
2515     * Should only be used with commands that return replies on the
2516     * command channel - do not use for LIST, NLST, MLSD etc.
2517     *
2518     * @param command  The command to invoke
2519     * @param params  The parameters string, may be {@code null}
2520     * @return The array of replies, or {@code null} if the command failed, in which case
2521     * call {@link #getReplyCode()} or {@link #getReplyString()}
2522     * to get the reason.
2523     *
2524     * @throws IOException  If an I/O error occurs while either sending a
2525     *      command to the server or receiving a reply from the server.
2526     * @since 3.0
2527     */
2528    public String[] doCommandAsStrings(String command, String params) throws IOException
2529    {
2530        boolean success = FTPReply.isPositiveCompletion(sendCommand(command, params));
2531        if (success){
2532            return getReplyStrings();
2533        } else {
2534            return null;
2535        }
2536    }
2537
2538    /**
2539     * Get file details using the MLST command
2540     *
2541     * @param pathname the file or directory to list, may be {@code null}
2542     * @return the file details, may be {@code null}
2543     * @throws IOException on error
2544     * @since 3.0
2545     */
2546    public FTPFile mlistFile(String pathname) throws IOException
2547    {
2548        boolean success = FTPReply.isPositiveCompletion(sendCommand(FTPCmd.MLST, pathname));
2549        if (success){
2550            String reply = getReplyStrings()[1];
2551            // some FTP server reply not contains space before fact(s)
2552            if(reply.charAt(0) != ' ') { reply = " " + reply; }
2553            /* check the response makes sense.
2554             * Must have space before fact(s) and between fact(s) and file name
2555             * Fact(s) can be absent, so at least 3 chars are needed.
2556             */
2557            if (reply.length() < 3) {
2558                throw new MalformedServerReplyException("Invalid server reply (MLST): '" + reply + "'");
2559            }
2560            // some FTP server reply contains more than one space before fact(s)
2561            String entry = reply.replaceAll("^\\s+", ""); // skip leading space for parser
2562            return MLSxEntryParser.parseEntry(entry);
2563        } else {
2564            return null;
2565        }
2566    }
2567
2568    /**
2569     * Generate a directory listing for the current directory using the MLSD command.
2570     *
2571     * @return the array of file entries
2572     * @throws IOException on error
2573     * @since 3.0
2574     */
2575    public FTPFile[] mlistDir() throws IOException
2576    {
2577        return mlistDir(null);
2578    }
2579
2580    /**
2581     * Generate a directory listing using the MLSD command.
2582     *
2583     * @param pathname the directory name, may be {@code null}
2584     * @return the array of file entries
2585     * @throws IOException on error
2586     * @since 3.0
2587     */
2588    public FTPFile[] mlistDir(String pathname) throws IOException
2589    {
2590        FTPListParseEngine engine = initiateMListParsing( pathname);
2591        return engine.getFiles();
2592    }
2593
2594    /**
2595     * Generate a directory listing using the MLSD command.
2596     *
2597     * @param pathname the directory name, may be {@code null}
2598     * @param filter the filter to apply to the responses
2599     * @return the array of file entries
2600     * @throws IOException on error
2601     * @since 3.0
2602     */
2603    public FTPFile[] mlistDir(String pathname, FTPFileFilter filter) throws IOException
2604    {
2605        FTPListParseEngine engine = initiateMListParsing( pathname);
2606        return engine.getFiles(filter);
2607    }
2608
2609    /**
2610     * Restart a <code>STREAM_TRANSFER_MODE</code> file transfer starting
2611     * from the given offset.  This will only work on FTP servers supporting
2612     * the REST comand for the stream transfer mode.  However, most FTP
2613     * servers support this.  Any subsequent file transfer will start
2614     * reading or writing the remote file from the indicated offset.
2615     *
2616     * @param offset  The offset into the remote file at which to start the
2617     *           next file transfer.
2618     * @return True if successfully completed, false if not.
2619     * @throws FTPConnectionClosedException
2620     *      If the FTP server prematurely closes the connection as a result
2621     *      of the client being idle or some other reason causing the server
2622     *      to send FTP reply code 421.  This exception may be caught either
2623     *      as an IOException or independently as itself.
2624     * @throws IOException  If an I/O error occurs while either sending a
2625     *      command to the server or receiving a reply from the server.
2626     * @since 3.1 (changed from private to protected)
2627     */
2628    protected boolean restart(long offset) throws IOException
2629    {
2630        __restartOffset = 0;
2631        return FTPReply.isPositiveIntermediate(rest(Long.toString(offset)));
2632    }
2633
2634    /**
2635     * Sets the restart offset for file transfers.
2636     * <p>
2637     * The restart command is not sent to the server immediately.
2638     * It is sent when a data connection is created as part of a
2639     * subsequent command.
2640     * The restart marker is reset to zero after use.
2641     * </p>
2642     * <p>
2643     * <b>Note: This method should only be invoked immediately prior to
2644     * the transfer to which it applies.</b>
2645     *
2646     * @param offset  The offset into the remote file at which to start the
2647     *           next file transfer.  This must be a value greater than or
2648     *           equal to zero.
2649     */
2650    public void setRestartOffset(long offset)
2651    {
2652        if (offset >= 0) {
2653            __restartOffset = offset;
2654        }
2655    }
2656
2657    /**
2658     * Fetches the restart offset.
2659     *
2660     * @return offset  The offset into the remote file at which to start the
2661     *           next file transfer.
2662     */
2663    public long getRestartOffset()
2664    {
2665        return __restartOffset;
2666    }
2667
2668
2669
2670    /**
2671     * Renames a remote file.
2672     *
2673     * @param from  The name of the remote file to rename.
2674     * @param to    The new name of the remote file.
2675     * @return True if successfully completed, false if not.
2676     * @throws FTPConnectionClosedException
2677     *      If the FTP server prematurely closes the connection as a result
2678     *      of the client being idle or some other reason causing the server
2679     *      to send FTP reply code 421.  This exception may be caught either
2680     *      as an IOException or independently as itself.
2681     * @throws IOException  If an I/O error occurs while either sending a
2682     *      command to the server or receiving a reply from the server.
2683     */
2684    public boolean rename(String from, String to) throws IOException
2685    {
2686        if (!FTPReply.isPositiveIntermediate(rnfr(from))) {
2687            return false;
2688        }
2689
2690        return FTPReply.isPositiveCompletion(rnto(to));
2691    }
2692
2693
2694    /**
2695     * Abort a transfer in progress.
2696     *
2697     * @return True if successfully completed, false if not.
2698     * @throws FTPConnectionClosedException
2699     *      If the FTP server prematurely closes the connection as a result
2700     *      of the client being idle or some other reason causing the server
2701     *      to send FTP reply code 421.  This exception may be caught either
2702     *      as an IOException or independently as itself.
2703     * @throws IOException  If an I/O error occurs while either sending a
2704     *      command to the server or receiving a reply from the server.
2705     */
2706    public boolean abort() throws IOException
2707    {
2708        return FTPReply.isPositiveCompletion(abor());
2709    }
2710
2711    /**
2712     * Deletes a file on the FTP server.
2713     *
2714     * @param pathname   The pathname of the file to be deleted.
2715     * @return True if successfully completed, false if not.
2716     * @throws FTPConnectionClosedException
2717     *      If the FTP server prematurely closes the connection as a result
2718     *      of the client being idle or some other reason causing the server
2719     *      to send FTP reply code 421.  This exception may be caught either
2720     *      as an IOException or independently as itself.
2721     * @throws IOException  If an I/O error occurs while either sending a
2722     *      command to the server or receiving a reply from the server.
2723     */
2724    public boolean deleteFile(String pathname) throws IOException
2725    {
2726        return FTPReply.isPositiveCompletion(dele(pathname));
2727    }
2728
2729
2730    /**
2731     * Removes a directory on the FTP server (if empty).
2732     *
2733     * @param pathname  The pathname of the directory to remove.
2734     * @return True if successfully completed, false if not.
2735     * @throws FTPConnectionClosedException
2736     *      If the FTP server prematurely closes the connection as a result
2737     *      of the client being idle or some other reason causing the server
2738     *      to send FTP reply code 421.  This exception may be caught either
2739     *      as an IOException or independently as itself.
2740     * @throws IOException  If an I/O error occurs while either sending a
2741     *      command to the server or receiving a reply from the server.
2742     */
2743    public boolean removeDirectory(String pathname) throws IOException
2744    {
2745        return FTPReply.isPositiveCompletion(rmd(pathname));
2746    }
2747
2748
2749    /**
2750     * Creates a new subdirectory on the FTP server in the current directory
2751     * (if a relative pathname is given) or where specified (if an absolute
2752     * pathname is given).
2753     *
2754     * @param pathname The pathname of the directory to create.
2755     * @return True if successfully completed, false if not.
2756     * @throws FTPConnectionClosedException
2757     *      If the FTP server prematurely closes the connection as a result
2758     *      of the client being idle or some other reason causing the server
2759     *      to send FTP reply code 421.  This exception may be caught either
2760     *      as an IOException or independently as itself.
2761     * @throws IOException  If an I/O error occurs while either sending a
2762     *      command to the server or receiving a reply from the server.
2763     */
2764    public boolean makeDirectory(String pathname) throws IOException
2765    {
2766        return FTPReply.isPositiveCompletion(mkd(pathname));
2767    }
2768
2769
2770    /**
2771     * Returns the pathname of the current working directory.
2772     *
2773     * @return The pathname of the current working directory.  If it cannot
2774     *         be obtained, returns null.
2775     * @throws FTPConnectionClosedException
2776     *      If the FTP server prematurely closes the connection as a result
2777     *      of the client being idle or some other reason causing the server
2778     *      to send FTP reply code 421.  This exception may be caught either
2779     *      as an IOException or independently as itself.
2780     * @throws IOException  If an I/O error occurs while either sending a
2781     *      command to the server or receiving a reply from the server.
2782     */
2783    public String printWorkingDirectory() throws IOException
2784    {
2785        if (pwd() != FTPReply.PATHNAME_CREATED) {
2786            return null;
2787        }
2788
2789        return __parsePathname(_replyLines.get( _replyLines.size() - 1));
2790    }
2791
2792
2793    /**
2794     * Send a site specific command.
2795     * @param arguments The site specific command and arguments.
2796     * @return True if successfully completed, false if not.
2797     * @throws FTPConnectionClosedException
2798     *      If the FTP server prematurely closes the connection as a result
2799     *      of the client being idle or some other reason causing the server
2800     *      to send FTP reply code 421.  This exception may be caught either
2801     *      as an IOException or independently as itself.
2802     * @throws IOException  If an I/O error occurs while either sending a
2803     *      command to the server or receiving a reply from the server.
2804     */
2805    public boolean sendSiteCommand(String arguments) throws IOException
2806    {
2807        return FTPReply.isPositiveCompletion(site(arguments));
2808    }
2809
2810
2811    /**
2812     * Fetches the system type from the server and returns the string.
2813     * This value is cached for the duration of the connection after the
2814     * first call to this method.  In other words, only the first time
2815     * that you invoke this method will it issue a SYST command to the
2816     * FTP server.  FTPClient will remember the value and return the
2817     * cached value until a call to disconnect.
2818     * <p>
2819     * If the SYST command fails, and the system property
2820     * {@link #FTP_SYSTEM_TYPE_DEFAULT} is defined, then this is used instead.
2821     * @return The system type obtained from the server. Never null.
2822     * @throws FTPConnectionClosedException
2823     *      If the FTP server prematurely closes the connection as a result
2824     *      of the client being idle or some other reason causing the server
2825     *      to send FTP reply code 421.  This exception may be caught either
2826     *      as an IOException or independently as itself.
2827     * @throws IOException  If an I/O error occurs while either sending a
2828     *  command to the server or receiving a reply from the server (and the default
2829     *  system type property is not defined)
2830     *  @since 2.2
2831     */
2832    public String getSystemType() throws IOException
2833    {
2834        //if (syst() == FTPReply.NAME_SYSTEM_TYPE)
2835        // Technically, we should expect a NAME_SYSTEM_TYPE response, but
2836        // in practice FTP servers deviate, so we soften the condition to
2837        // a positive completion.
2838        if (__systemName == null){
2839            if (FTPReply.isPositiveCompletion(syst())) {
2840                // Assume that response is not empty here (cannot be null)
2841                __systemName = _replyLines.get(_replyLines.size() - 1).substring(4);
2842            } else {
2843                // Check if the user has provided a default for when the SYST command fails
2844                String systDefault = System.getProperty(FTP_SYSTEM_TYPE_DEFAULT);
2845                if (systDefault != null) {
2846                    __systemName = systDefault;
2847                } else {
2848                    throw new IOException("Unable to determine system type - response: " + getReplyString());
2849                }
2850            }
2851        }
2852        return __systemName;
2853    }
2854
2855
2856    /**
2857     * Fetches the system help information from the server and returns the
2858     * full string.
2859     *
2860     * @return The system help string obtained from the server.  null if the
2861     *       information could not be obtained.
2862     * @throws FTPConnectionClosedException
2863     *      If the FTP server prematurely closes the connection as a result
2864     *      of the client being idle or some other reason causing the server
2865     *      to send FTP reply code 421.  This exception may be caught either
2866     *      as an IOException or independently as itself.
2867     * @throws IOException  If an I/O error occurs while either sending a
2868     *  command to the server or receiving a reply from the server.
2869     */
2870    public String listHelp() throws IOException
2871    {
2872        if (FTPReply.isPositiveCompletion(help())) {
2873            return getReplyString();
2874        }
2875        return null;
2876    }
2877
2878
2879    /**
2880     * Fetches the help information for a given command from the server and
2881     * returns the full string.
2882     * @param command The command on which to ask for help.
2883     * @return The command help string obtained from the server.  null if the
2884     *       information could not be obtained.
2885     * @throws FTPConnectionClosedException
2886     *      If the FTP server prematurely closes the connection as a result
2887     *      of the client being idle or some other reason causing the server
2888     *      to send FTP reply code 421.  This exception may be caught either
2889     *      as an IOException or independently as itself.
2890     * @throws IOException  If an I/O error occurs while either sending a
2891     *  command to the server or receiving a reply from the server.
2892     */
2893    public String listHelp(String command) throws IOException
2894    {
2895        if (FTPReply.isPositiveCompletion(help(command))) {
2896            return getReplyString();
2897        }
2898        return null;
2899    }
2900
2901
2902    /**
2903     * Sends a NOOP command to the FTP server.  This is useful for preventing
2904     * server timeouts.
2905     *
2906     * @return True if successfully completed, false if not.
2907     * @throws FTPConnectionClosedException
2908     *      If the FTP server prematurely closes the connection as a result
2909     *      of the client being idle or some other reason causing the server
2910     *      to send FTP reply code 421.  This exception may be caught either
2911     *      as an IOException or independently as itself.
2912     * @throws IOException  If an I/O error occurs while either sending a
2913     *      command to the server or receiving a reply from the server.
2914     */
2915    public boolean sendNoOp() throws IOException
2916    {
2917        return FTPReply.isPositiveCompletion(noop());
2918    }
2919
2920
2921    /**
2922     * Obtain a list of file names in a directory (or just the name of a given
2923     * file, which is not particularly useful).  This information is obtained
2924     * through the NLST command.  If the given pathname is a directory and
2925     * contains no files,  a zero length array is returned only
2926     * if the FTP server returned a positive completion code, otherwise
2927     * null is returned (the FTP server returned a 550 error No files found.).
2928     * If the directory is not empty, an array of file names in the directory is
2929     * returned. If the pathname corresponds
2930     * to a file, only that file will be listed.  The server may or may not
2931     * expand glob expressions.
2932     *
2933     * @param pathname  The file or directory to list.
2934     *                  Warning: the server may treat a leading '-' as an
2935     *                  option introducer. If so, try using an absolute path,
2936     *                  or prefix the path with ./ (unix style servers).
2937     *                  Some servers may support "--" as meaning end of options,
2938     *                  in which case "-- -xyz" should work.
2939     * @return The list of file names contained in the given path.  null if
2940     *     the list could not be obtained.  If there are no file names in
2941     *     the directory, a zero-length array is returned.
2942     * @throws FTPConnectionClosedException
2943     *      If the FTP server prematurely closes the connection as a result
2944     *      of the client being idle or some other reason causing the server
2945     *      to send FTP reply code 421.  This exception may be caught either
2946     *      as an IOException or independently as itself.
2947     * @throws IOException  If an I/O error occurs while either sending a
2948     *      command to the server or receiving a reply from the server.
2949     */
2950    public String[] listNames(String pathname) throws IOException
2951    {
2952        Socket socket = _openDataConnection_(FTPCmd.NLST, getListArguments(pathname));
2953
2954        if (socket == null) {
2955            return null;
2956        }
2957
2958        BufferedReader reader =
2959            new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding()));
2960
2961        ArrayList<String> results = new ArrayList<String>();
2962        String line;
2963        while ((line = reader.readLine()) != null) {
2964            results.add(line);
2965        }
2966
2967        reader.close();
2968        socket.close();
2969
2970        if (completePendingCommand())
2971        {
2972            String[] names = new String[ results.size() ];
2973            return results.toArray(names);
2974        }
2975
2976        return null;
2977    }
2978
2979
2980    /**
2981     * Obtain a list of file names in the current working directory
2982     * This information is obtained through the NLST command.  If the current
2983     * directory contains no files, a zero length array is returned only
2984     * if the FTP server returned a positive completion code, otherwise,
2985     * null is returned (the FTP server returned a 550 error No files found.).
2986     * If the directory is not empty, an array of file names in the directory is
2987     * returned.
2988     *
2989     * @return The list of file names contained in the current working
2990     *     directory.  null if the list could not be obtained.
2991     *     If there are no file names in the directory, a zero-length array
2992     *     is returned.
2993     * @throws FTPConnectionClosedException
2994     *      If the FTP server prematurely closes the connection as a result
2995     *      of the client being idle or some other reason causing the server
2996     *      to send FTP reply code 421.  This exception may be caught either
2997     *      as an IOException or independently as itself.
2998     * @throws IOException  If an I/O error occurs while either sending a
2999     *      command to the server or receiving a reply from the server.
3000     */
3001    public String[] listNames() throws IOException
3002    {
3003        return listNames(null);
3004    }
3005
3006
3007
3008    /**
3009     * Using the default system autodetect mechanism, obtain a
3010     * list of file information for the current working directory
3011     * or for just a single file.
3012     * <p>
3013     * This information is obtained through the LIST command.  The contents of
3014     * the returned array is determined by the<code> FTPFileEntryParser </code>
3015     * used.
3016     * <p>
3017     * N.B. the LIST command does not generally return very precise timestamps.
3018     * For recent files, the response usually contains hours and minutes (not seconds).
3019     * For older files, the output may only contain a date.
3020     * If the server supports it, the MLSD command returns timestamps with a precision
3021     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3022     *
3023     * @param pathname  The file or directory to list.  Since the server may
3024     *                  or may not expand glob expressions, using them here
3025     *                  is not recommended and may well cause this method to
3026     *                  fail.
3027     *                  Also, some servers treat a leading '-' as being an option.
3028     *                  To avoid this interpretation, use an absolute pathname
3029     *                  or prefix the pathname with ./ (unix style servers).
3030     *                  Some servers may support "--" as meaning end of options,
3031     *                  in which case "-- -xyz" should work.
3032     *
3033     * @return The list of file information contained in the given path in
3034     *         the format determined by the autodetection mechanism
3035     * @throws FTPConnectionClosedException
3036     *                   If the FTP server prematurely closes the connection
3037     *                   as a result of the client being idle or some other
3038     *                   reason causing the server to send FTP reply code 421.
3039     *                   This exception may be caught either as an IOException
3040     *                   or independently as itself.
3041     * @throws IOException
3042     *                   If an I/O error occurs while either sending a
3043     *                   command to the server or receiving a reply
3044     *                   from the server.
3045     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3046     *                   Thrown if the parserKey parameter cannot be
3047     *                   resolved by the selected parser factory.
3048     *                   In the DefaultFTPEntryParserFactory, this will
3049     *                   happen when parserKey is neither
3050     *                   the fully qualified class name of a class
3051     *                   implementing the interface
3052     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3053     *                   nor a string containing one of the recognized keys
3054     *                   mapping to such a parser or if class loader
3055     *                   security issues prevent its being loaded.
3056     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3057     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3058     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3059     */
3060    public FTPFile[] listFiles(String pathname)
3061    throws IOException
3062    {
3063        FTPListParseEngine engine = initiateListParsing((String) null, pathname);
3064        return engine.getFiles();
3065
3066    }
3067
3068    /**
3069     * Using the default system autodetect mechanism, obtain a
3070     * list of file information for the current working directory.
3071     * <p>
3072     * This information is obtained through the LIST command.  The contents of
3073     * the returned array is determined by the<code> FTPFileEntryParser </code>
3074     * used.
3075     * <p>
3076     * N.B. the LIST command does not generally return very precise timestamps.
3077     * For recent files, the response usually contains hours and minutes (not seconds).
3078     * For older files, the output may only contain a date.
3079     * If the server supports it, the MLSD command returns timestamps with a precision
3080     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3081     *
3082     * @return The list of file information contained in the current directory
3083     *         in the format determined by the autodetection mechanism.
3084     *         <p><b>
3085     *         NOTE:</b> This array may contain null members if any of the
3086     *         individual file listings failed to parse.  The caller should
3087     *         check each entry for null before referencing it.
3088     * @throws FTPConnectionClosedException
3089     *                   If the FTP server prematurely closes the connection
3090     *                   as a result of the client being idle or some other
3091     *                   reason causing the server to send FTP reply code 421.
3092     *                   This exception may be caught either as an IOException
3093     *                   or independently as itself.
3094     * @throws IOException
3095     *                   If an I/O error occurs while either sending a
3096     *                   command to the server or receiving a reply
3097     *                   from the server.
3098     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3099     *                   Thrown if the parserKey parameter cannot be
3100     *                   resolved by the selected parser factory.
3101     *                   In the DefaultFTPEntryParserFactory, this will
3102     *                   happen when parserKey is neither
3103     *                   the fully qualified class name of a class
3104     *                   implementing the interface
3105     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3106     *                   nor a string containing one of the recognized keys
3107     *                   mapping to such a parser or if class loader
3108     *                   security issues prevent its being loaded.
3109     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3110     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3111     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3112     */
3113    public FTPFile[] listFiles()
3114    throws IOException
3115    {
3116        return listFiles((String) null);
3117    }
3118
3119    /**
3120     * Version of {@link #listFiles(String)} which allows a filter to be provided.
3121     * For example: <code>listFiles("site", FTPFileFilters.DIRECTORY);</code>
3122     * @param pathname the initial path, may be null
3123     * @param filter the filter, non-null
3124     * @return the list of FTPFile entries.
3125     * @throws IOException on error
3126     * @since 2.2
3127     */
3128    public FTPFile[] listFiles(String pathname, FTPFileFilter filter)
3129    throws IOException
3130    {
3131        FTPListParseEngine engine = initiateListParsing((String) null, pathname);
3132        return engine.getFiles(filter);
3133
3134    }
3135
3136    /**
3137     * Using the default system autodetect mechanism, obtain a
3138     * list of directories contained in the current working directory.
3139     * <p>
3140     * This information is obtained through the LIST command.  The contents of
3141     * the returned array is determined by the<code> FTPFileEntryParser </code>
3142     * used.
3143     * <p>
3144     * N.B. the LIST command does not generally return very precise timestamps.
3145     * For recent files, the response usually contains hours and minutes (not seconds).
3146     * For older files, the output may only contain a date.
3147     * If the server supports it, the MLSD command returns timestamps with a precision
3148     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3149     *
3150     * @return The list of directories contained in the current directory
3151     *         in the format determined by the autodetection mechanism.
3152     *
3153     * @throws FTPConnectionClosedException
3154     *                   If the FTP server prematurely closes the connection
3155     *                   as a result of the client being idle or some other
3156     *                   reason causing the server to send FTP reply code 421.
3157     *                   This exception may be caught either as an IOException
3158     *                   or independently as itself.
3159     * @throws IOException
3160     *                   If an I/O error occurs while either sending a
3161     *                   command to the server or receiving a reply
3162     *                   from the server.
3163     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3164     *                   Thrown if the parserKey parameter cannot be
3165     *                   resolved by the selected parser factory.
3166     *                   In the DefaultFTPEntryParserFactory, this will
3167     *                   happen when parserKey is neither
3168     *                   the fully qualified class name of a class
3169     *                   implementing the interface
3170     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3171     *                   nor a string containing one of the recognized keys
3172     *                   mapping to such a parser or if class loader
3173     *                   security issues prevent its being loaded.
3174     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3175     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3176     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3177     * @since 3.0
3178     */
3179    public FTPFile[] listDirectories() throws IOException {
3180        return listDirectories((String) null);
3181    }
3182
3183    /**
3184     * Using the default system autodetect mechanism, obtain a
3185     * list of directories contained in the specified directory.
3186     * <p>
3187     * This information is obtained through the LIST command.  The contents of
3188     * the returned array is determined by the<code> FTPFileEntryParser </code>
3189     * used.
3190     * <p>
3191     * N.B. the LIST command does not generally return very precise timestamps.
3192     * For recent files, the response usually contains hours and minutes (not seconds).
3193     * For older files, the output may only contain a date.
3194     * If the server supports it, the MLSD command returns timestamps with a precision
3195     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3196     * @param parent the starting directory
3197     *
3198     * @return The list of directories contained in the specified directory
3199     *         in the format determined by the autodetection mechanism.
3200     *
3201     * @throws FTPConnectionClosedException
3202     *                   If the FTP server prematurely closes the connection
3203     *                   as a result of the client being idle or some other
3204     *                   reason causing the server to send FTP reply code 421.
3205     *                   This exception may be caught either as an IOException
3206     *                   or independently as itself.
3207     * @throws IOException
3208     *                   If an I/O error occurs while either sending a
3209     *                   command to the server or receiving a reply
3210     *                   from the server.
3211     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3212     *                   Thrown if the parserKey parameter cannot be
3213     *                   resolved by the selected parser factory.
3214     *                   In the DefaultFTPEntryParserFactory, this will
3215     *                   happen when parserKey is neither
3216     *                   the fully qualified class name of a class
3217     *                   implementing the interface
3218     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3219     *                   nor a string containing one of the recognized keys
3220     *                   mapping to such a parser or if class loader
3221     *                   security issues prevent its being loaded.
3222     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3223     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3224     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3225     * @since 3.0
3226     */
3227    public FTPFile[] listDirectories(String parent) throws IOException {
3228        return listFiles(parent, FTPFileFilters.DIRECTORIES);
3229    }
3230
3231    /**
3232     * Using the default autodetect mechanism, initialize an FTPListParseEngine
3233     * object containing a raw file information for the current working
3234     * directory on the server
3235     * This information is obtained through the LIST command.  This object
3236     * is then capable of being iterated to return a sequence of FTPFile
3237     * objects with information filled in by the
3238     * <code> FTPFileEntryParser </code> used.
3239     * <p>
3240     * This method differs from using the listFiles() methods in that
3241     * expensive FTPFile objects are not created until needed which may be
3242     * an advantage on large lists.
3243     *
3244     * @return A FTPListParseEngine object that holds the raw information and
3245     * is capable of providing parsed FTPFile objects, one for each file
3246     * containing information contained in the given path in the format
3247     * determined by the <code> parser </code> parameter.   Null will be
3248     * returned if a data connection cannot be opened.  If the current working
3249     * directory contains no files, an empty array will be the return.
3250     *
3251     * @throws FTPConnectionClosedException
3252     *                   If the FTP server prematurely closes the connection as a result
3253     *                   of the client being idle or some other reason causing the server
3254     *                   to send FTP reply code 421.  This exception may be caught either
3255     *                   as an IOException or independently as itself.
3256     * @throws IOException
3257     *                   If an I/O error occurs while either sending a
3258     *                   command to the server or receiving a reply from the server.
3259     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3260     *                   Thrown if the autodetect mechanism cannot
3261     *                   resolve the type of system we are connected with.
3262     * @see FTPListParseEngine
3263     */
3264    public FTPListParseEngine initiateListParsing()
3265    throws IOException
3266    {
3267        return initiateListParsing((String) null);
3268    }
3269
3270    /**
3271     * Using the default autodetect mechanism, initialize an FTPListParseEngine
3272     * object containing a raw file information for the supplied directory.
3273     * This information is obtained through the LIST command.  This object
3274     * is then capable of being iterated to return a sequence of FTPFile
3275     * objects with information filled in by the
3276     * <code> FTPFileEntryParser </code> used.
3277     * <p>
3278     * The server may or may not expand glob expressions.  You should avoid
3279     * using glob expressions because the return format for glob listings
3280     * differs from server to server and will likely cause this method to fail.
3281     * <p>
3282     * This method differs from using the listFiles() methods in that
3283     * expensive FTPFile objects are not created until needed which may be
3284     * an advantage on large lists.
3285     *
3286     * <pre>
3287     *    FTPClient f=FTPClient();
3288     *    f.connect(server);
3289     *    f.login(username, password);
3290     *    FTPListParseEngine engine = f.initiateListParsing(directory);
3291     *
3292     *    while (engine.hasNext()) {
3293     *       FTPFile[] files = engine.getNext(25);  // "page size" you want
3294     *       //do whatever you want with these files, display them, etc.
3295     *       //expensive FTPFile objects not created until needed.
3296     *    }
3297     * </pre>
3298     * @param pathname the starting directory
3299     *
3300     * @return A FTPListParseEngine object that holds the raw information and
3301     * is capable of providing parsed FTPFile objects, one for each file
3302     * containing information contained in the given path in the format
3303     * determined by the <code> parser </code> parameter.   Null will be
3304     * returned if a data connection cannot be opened.  If the current working
3305     * directory contains no files, an empty array will be the return.
3306     *
3307     * @throws FTPConnectionClosedException
3308     *                   If the FTP server prematurely closes the connection as a result
3309     *                   of the client being idle or some other reason causing the server
3310     *                   to send FTP reply code 421.  This exception may be caught either
3311     *                   as an IOException or independently as itself.
3312     * @throws IOException
3313     *                   If an I/O error occurs while either sending a
3314     *                   command to the server or receiving a reply from the server.
3315     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3316     *                   Thrown if the autodetect mechanism cannot
3317     *                   resolve the type of system we are connected with.
3318     * @see FTPListParseEngine
3319     */
3320    public FTPListParseEngine initiateListParsing(String pathname)
3321    throws IOException
3322    {
3323        return initiateListParsing((String) null, pathname);
3324    }
3325
3326    /**
3327     * Using the supplied parser key, initialize an FTPListParseEngine
3328     * object containing a raw file information for the supplied directory.
3329     * This information is obtained through the LIST command.  This object
3330     * is then capable of being iterated to return a sequence of FTPFile
3331     * objects with information filled in by the
3332     * <code> FTPFileEntryParser </code> used.
3333     * <p>
3334     * The server may or may not expand glob expressions.  You should avoid
3335     * using glob expressions because the return format for glob listings
3336     * differs from server to server and will likely cause this method to fail.
3337     * <p>
3338     * This method differs from using the listFiles() methods in that
3339     * expensive FTPFile objects are not created until needed which may be
3340     * an advantage on large lists.
3341     *
3342     * @param parserKey A string representing a designated code or fully-qualified
3343     * class name of an  <code> FTPFileEntryParser </code> that should be
3344     *               used to parse each server file listing.
3345     *               May be {@code null}, in which case the code checks first
3346     *               the system property {@link #FTP_SYSTEM_TYPE}, and if that is
3347     *               not defined the SYST command is used to provide the value.
3348     *               To allow for arbitrary system types, the return from the
3349     *               SYST command is used to look up an alias for the type in the
3350     *               {@link #SYSTEM_TYPE_PROPERTIES} properties file if it is available.
3351     * @param pathname the starting directory
3352     *
3353     * @return A FTPListParseEngine object that holds the raw information and
3354     * is capable of providing parsed FTPFile objects, one for each file
3355     * containing information contained in the given path in the format
3356     * determined by the <code> parser </code> parameter.   Null will be
3357     * returned if a data connection cannot be opened.  If the current working
3358     * directory contains no files, an empty array will be the return.
3359     *
3360     * @throws FTPConnectionClosedException
3361     *                   If the FTP server prematurely closes the connection as a result
3362     *                   of the client being idle or some other reason causing the server
3363     *                   to send FTP reply code 421.  This exception may be caught either
3364     *                   as an IOException or independently as itself.
3365     * @throws IOException
3366     *                   If an I/O error occurs while either sending a
3367     *                   command to the server or receiving a reply from the server.
3368     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3369     *                   Thrown if the parserKey parameter cannot be
3370     *                   resolved by the selected parser factory.
3371     *                   In the DefaultFTPEntryParserFactory, this will
3372     *                   happen when parserKey is neither
3373     *                   the fully qualified class name of a class
3374     *                   implementing the interface
3375     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3376     *                   nor a string containing one of the recognized keys
3377     *                   mapping to such a parser or if class loader
3378     *                   security issues prevent its being loaded.
3379     * @see FTPListParseEngine
3380     */
3381    public FTPListParseEngine initiateListParsing(
3382            String parserKey, String pathname)
3383    throws IOException
3384    {
3385        __createParser(parserKey); // create and cache parser
3386        return initiateListParsing(__entryParser, pathname);
3387    }
3388
3389    // package access for test purposes
3390    void __createParser(String parserKey) throws IOException {
3391        // We cache the value to avoid creation of a new object every
3392        // time a file listing is generated.
3393        // Note: we don't check against a null parserKey (NET-544)
3394        if(__entryParser == null ||  (parserKey != null && ! __entryParserKey.equals(parserKey))) {
3395            if (null != parserKey) {
3396                // if a parser key was supplied in the parameters,
3397                // use that to create the parser
3398                __entryParser =
3399                    __parserFactory.createFileEntryParser(parserKey);
3400                __entryParserKey = parserKey;
3401
3402            } else {
3403                // if no parserKey was supplied, check for a configuration
3404                // in the params, and if it has a non-empty system type, use that.
3405                if (null != __configuration && __configuration.getServerSystemKey().length() > 0) {
3406                    __entryParser =
3407                        __parserFactory.createFileEntryParser(__configuration);
3408                    __entryParserKey = __configuration.getServerSystemKey();
3409                } else {
3410                    // if a parserKey hasn't been supplied, and a configuration
3411                    // hasn't been supplied, and the override property is not set
3412                    // then autodetect by calling
3413                    // the SYST command and use that to choose the parser.
3414                    String systemType = System.getProperty(FTP_SYSTEM_TYPE);
3415                    if (systemType == null) {
3416                        systemType = getSystemType(); // cannot be null
3417                        Properties override = getOverrideProperties();
3418                        if (override != null) {
3419                            String newType = override.getProperty(systemType);
3420                            if (newType != null) {
3421                                systemType = newType;
3422                            }
3423                        }
3424                    }
3425                    if (null != __configuration) { // system type must have been empty above
3426                        __entryParser = __parserFactory.createFileEntryParser(new FTPClientConfig(systemType, __configuration));
3427                    } else {
3428                        __entryParser = __parserFactory.createFileEntryParser(systemType);
3429                    }
3430                    __entryParserKey = systemType;
3431                }
3432            }
3433        }
3434
3435
3436    }
3437
3438    /**
3439     * private method through which all listFiles() and
3440     * initiateListParsing methods pass once a parser is determined.
3441     *
3442     * @throws FTPConnectionClosedException
3443     *                   If the FTP server prematurely closes the connection as a result
3444     *                   of the client being idle or some other reason causing the server
3445     *                   to send FTP reply code 421.  This exception may be caught either
3446     *                   as an IOException or independently as itself.
3447     * @throws IOException
3448     *                   If an I/O error occurs while either sending a
3449     *                   command to the server or receiving a reply from the server.
3450     * @see FTPListParseEngine
3451     */
3452    private FTPListParseEngine initiateListParsing(
3453            FTPFileEntryParser parser, String pathname)
3454    throws IOException
3455    {
3456        Socket socket = _openDataConnection_(FTPCmd.LIST, getListArguments(pathname));
3457
3458        FTPListParseEngine engine = new FTPListParseEngine(parser, __configuration);
3459        if (socket == null)
3460        {
3461            return engine;
3462        }
3463
3464        try {
3465            engine.readServerList(socket.getInputStream(), getControlEncoding());
3466        }
3467        finally {
3468            Util.closeQuietly(socket);
3469        }
3470
3471        completePendingCommand();
3472        return engine;
3473    }
3474
3475    /**
3476     * Initiate list parsing for MLSD listings in the current working directory.
3477     *
3478     * @return the engine
3479     * @throws IOException on error
3480     */
3481    public FTPListParseEngine initiateMListParsing() throws IOException
3482    {
3483        return initiateMListParsing(null);
3484    }
3485
3486    /**
3487     * Initiate list parsing for MLSD listings.
3488     *
3489     * @param pathname the path from where to MLSD.
3490     * @return the engine.
3491     * @throws IOException on error
3492     */
3493    public FTPListParseEngine initiateMListParsing(String pathname) throws IOException
3494    {
3495        Socket socket = _openDataConnection_(FTPCmd.MLSD, pathname);
3496        FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance(), __configuration);
3497        if (socket == null)
3498        {
3499            return engine;
3500        }
3501
3502        try {
3503            engine.readServerList(socket.getInputStream(), getControlEncoding());
3504        }
3505        finally {
3506            Util.closeQuietly(socket);
3507            completePendingCommand();
3508        }
3509        return engine;
3510    }
3511
3512    /**
3513     * @param pathname the initial pathname
3514     * @return the adjusted string with "-a" added if necessary
3515     * @since 2.0
3516     */
3517    protected String getListArguments(String pathname) {
3518        if (getListHiddenFiles())
3519        {
3520            if (pathname != null)
3521            {
3522                StringBuilder sb = new StringBuilder(pathname.length() + 3);
3523                sb.append("-a ");
3524                sb.append(pathname);
3525                return sb.toString();
3526            }
3527            else
3528            {
3529                return "-a";
3530            }
3531        }
3532
3533        return pathname;
3534    }
3535
3536
3537    /**
3538     * Issue the FTP STAT command to the server.
3539     *
3540     * @return The status information returned by the server.
3541     * @throws FTPConnectionClosedException
3542     *      If the FTP server prematurely closes the connection as a result
3543     *      of the client being idle or some other reason causing the server
3544     *      to send FTP reply code 421.  This exception may be caught either
3545     *      as an IOException or independently as itself.
3546     * @throws IOException  If an I/O error occurs while either sending a
3547     *      command to the server or receiving a reply from the server.
3548     */
3549    public String getStatus() throws IOException
3550    {
3551        if (FTPReply.isPositiveCompletion(stat())) {
3552            return getReplyString();
3553        }
3554        return null;
3555    }
3556
3557
3558    /**
3559     * Issue the FTP STAT command to the server for a given pathname.  This
3560     * should produce a listing of the file or directory.
3561     * @param pathname the file name
3562     *
3563     * @return The status information returned by the server.
3564     * @throws FTPConnectionClosedException
3565     *      If the FTP server prematurely closes the connection as a result
3566     *      of the client being idle or some other reason causing the server
3567     *      to send FTP reply code 421.  This exception may be caught either
3568     *      as an IOException or independently as itself.
3569     * @throws IOException  If an I/O error occurs while either sending a
3570     *      command to the server or receiving a reply from the server.
3571     */
3572    public String getStatus(String pathname) throws IOException
3573    {
3574        if (FTPReply.isPositiveCompletion(stat(pathname))) {
3575            return getReplyString();
3576        }
3577        return null;
3578    }
3579
3580
3581    /**
3582     * Issue the FTP SIZE command to the server for a given pathname.
3583     * This should produce the size of the file.
3584     *
3585     * @param pathname the file name
3586     *
3587     * @return The size information returned by the server; {@code null} if there was an error
3588     * @throws FTPConnectionClosedException
3589     *      If the FTP server prematurely closes the connection as a result
3590     *      of the client being idle or some other reason causing the server
3591     *      to send FTP reply code 421.  This exception may be caught either
3592     *      as an IOException or independently as itself.
3593     * @throws IOException  If an I/O error occurs while either sending a
3594     *      command to the server or receiving a reply from the server.
3595     * @since 3.7
3596     */
3597    public String getSize(String pathname) throws IOException
3598    {
3599        if (FTPReply.isPositiveCompletion(size(pathname))) {
3600            return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space
3601        }
3602        return null;
3603    }
3604
3605
3606    /**
3607     * Issue the FTP MDTM command (not supported by all servers) to retrieve the last
3608     * modification time of a file. The modification string should be in the
3609     * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in
3610     * GMT, but not all FTP servers honour this.
3611     *
3612     * @param pathname The file path to query.
3613     * @return A string representing the last file modification time in <code>YYYYMMDDhhmmss</code> format.
3614     * @throws IOException if an I/O error occurs.
3615     * @since 2.0
3616     */
3617    public String getModificationTime(String pathname) throws IOException {
3618        if (FTPReply.isPositiveCompletion(mdtm(pathname))) {
3619            return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space
3620        }
3621        return null;
3622    }
3623
3624
3625    /**
3626     * Issue the FTP MDTM command (not supported by all servers) to retrieve the last
3627     * modification time of a file. The modification string should be in the
3628     * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in
3629     * GMT, but not all FTP servers honour this.
3630     *
3631     * @param pathname The file path to query.
3632     * @return A FTPFile representing the last file modification time, may be {@code null}.
3633     * The FTPFile timestamp will be null if a parse error occurs.
3634     * @throws IOException if an I/O error occurs.
3635     * @since 3.4
3636     */
3637    public FTPFile mdtmFile(String pathname) throws IOException {
3638        if (FTPReply.isPositiveCompletion(mdtm(pathname))) {
3639            String reply = getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space
3640            FTPFile file = new FTPFile();
3641            file.setName(pathname);
3642            file.setRawListing(reply);
3643            file.setTimestamp(MLSxEntryParser.parseGMTdateTime(reply));
3644            return file;
3645        }
3646        return null;
3647    }
3648
3649
3650    /**
3651     * Issue the FTP MFMT command (not supported by all servers) which sets the last
3652     * modified time of a file.
3653     *
3654     * The timestamp should be in the form <code>YYYYMMDDhhmmss</code>. It should also
3655     * be in GMT, but not all servers honour this.
3656     *
3657     * An FTP server would indicate its support of this feature by including "MFMT"
3658     * in its response to the FEAT command, which may be retrieved by FTPClient.features()
3659     *
3660     * @param pathname The file path for which last modified time is to be changed.
3661     * @param timeval The timestamp to set to, in <code>YYYYMMDDhhmmss</code> format.
3662     * @return true if successfully set, false if not
3663     * @throws IOException if an I/O error occurs.
3664     * @since 2.2
3665     * @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a>
3666     */
3667    public boolean setModificationTime(String pathname, String timeval) throws IOException {
3668        return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval)));
3669    }
3670
3671
3672    /**
3673     * Set the internal buffer size for buffered data streams.
3674     *
3675     * @param bufSize The size of the buffer. Use a non-positive value to use the default.
3676     */
3677    public void setBufferSize(int bufSize) {
3678        __bufferSize = bufSize;
3679    }
3680
3681    /**
3682     * Retrieve the current internal buffer size for buffered data streams.
3683     * @return The current buffer size.
3684     */
3685    public int getBufferSize() {
3686        return __bufferSize;
3687    }
3688
3689    /**
3690     * Sets the value to be used for the data socket SO_SNDBUF option.
3691     * If the value is positive, the option will be set when the data socket has been created.
3692     *
3693     * @param bufSize The size of the buffer, zero or negative means the value is ignored.
3694      * @since 3.3
3695    */
3696    public void setSendDataSocketBufferSize(int bufSize) {
3697        __sendDataSocketBufferSize = bufSize;
3698    }
3699
3700    /**
3701     * Retrieve the value to be used for the data socket SO_SNDBUF option.
3702     * @return The current buffer size.
3703     * @since 3.3
3704     */
3705    public int getSendDataSocketBufferSize() {
3706        return __sendDataSocketBufferSize;
3707    }
3708
3709    /**
3710     * Sets the value to be used for the data socket SO_RCVBUF option.
3711     * If the value is positive, the option will be set when the data socket has been created.
3712     *
3713     * @param bufSize The size of the buffer, zero or negative means the value is ignored.
3714     * @since 3.3
3715     */
3716    public void setReceieveDataSocketBufferSize(int bufSize) {
3717        __receiveDataSocketBufferSize = bufSize;
3718    }
3719
3720    /**
3721     * Retrieve the value to be used for the data socket SO_RCVBUF option.
3722     * @return The current buffer size.
3723     * @since 3.3
3724     */
3725    public int getReceiveDataSocketBufferSize() {
3726        return __receiveDataSocketBufferSize;
3727    }
3728
3729    /**
3730     * Implementation of the {@link Configurable Configurable} interface.
3731     * In the case of this class, configuring merely makes the config object available for the
3732     * factory methods that construct parsers.
3733     * @param config {@link FTPClientConfig FTPClientConfig} object used to
3734     * provide non-standard configurations to the parser.
3735     * @since 1.4
3736     */
3737    @Override
3738    public void configure(FTPClientConfig config) {
3739        this.__configuration = config;
3740    }
3741
3742    /**
3743     * You can set this to true if you would like to get hidden files when {@link #listFiles} too.
3744     * A <code>LIST -a</code> will be issued to the ftp server.
3745     * It depends on your ftp server if you need to call this method, also dont expect to get rid
3746     * of hidden files if you call this method with "false".
3747     *
3748     * @param listHiddenFiles true if hidden files should be listed
3749     * @since 2.0
3750     */
3751    public void setListHiddenFiles(boolean listHiddenFiles) {
3752        this.__listHiddenFiles = listHiddenFiles;
3753    }
3754
3755    /**
3756     * @see #setListHiddenFiles(boolean)
3757     * @return the current state
3758     * @since 2.0
3759     */
3760    public boolean getListHiddenFiles() {
3761        return this.__listHiddenFiles;
3762    }
3763
3764    /**
3765     * Whether should attempt to use EPSV with IPv4.
3766     * Default (if not set) is <code>false</code>
3767     * @return true if should attempt EPSV
3768     * @since 2.2
3769     */
3770    public boolean isUseEPSVwithIPv4() {
3771        return __useEPSVwithIPv4;
3772    }
3773
3774
3775    /**
3776     * Set whether to use EPSV with IPv4.
3777     * Might be worth enabling in some circumstances.
3778     *
3779     * For example, when using IPv4 with NAT it
3780     * may work with some rare configurations.
3781     * E.g. if FTP server has a static PASV address (external network)
3782     * and the client is coming from another internal network.
3783     * In that case the data connection after PASV command would fail,
3784     * while EPSV would make the client succeed by taking just the port.
3785     *
3786     * @param selected value to set.
3787     * @since 2.2
3788     */
3789    public void setUseEPSVwithIPv4(boolean selected) {
3790        this.__useEPSVwithIPv4 = selected;
3791    }
3792
3793    /**
3794     * Set the listener to be used when performing store/retrieve operations.
3795     * The default value (if not set) is {@code null}.
3796     *
3797     * @param listener to be used, may be {@code null} to disable
3798     * @since 3.0
3799     */
3800    public void setCopyStreamListener(CopyStreamListener listener){
3801        __copyStreamListener = listener;
3802    }
3803
3804    /**
3805     * Obtain the currently active listener.
3806     *
3807     * @return the listener, may be {@code null}
3808     * @since 3.0
3809     */
3810    public CopyStreamListener getCopyStreamListener(){
3811        return __copyStreamListener;
3812    }
3813
3814    /**
3815     * Set the time to wait between sending control connection keepalive messages
3816     * when processing file upload or download.
3817     * <p>
3818     * See the class Javadoc section "Control channel keep-alive feature:"
3819     *
3820     * @param controlIdle the wait (in secs) between keepalive messages. Zero (or less) disables.
3821     * @since 3.0
3822     * @see #setControlKeepAliveReplyTimeout(int)
3823     */
3824    public void setControlKeepAliveTimeout(long controlIdle){
3825        __controlKeepAliveTimeout = controlIdle * 1000;
3826    }
3827
3828    /**
3829     * Get the time to wait between sending control connection keepalive messages
3830     * when processing file upload or download.
3831     * <p>
3832     * See the class Javadoc section "Control channel keep-alive feature:"
3833     *
3834     * @return the number of seconds between keepalive messages.
3835     * @since 3.0
3836     */
3837    public long getControlKeepAliveTimeout() {
3838        return __controlKeepAliveTimeout / 1000;
3839    }
3840
3841    /**
3842     * Get the CSL debug array.
3843     * <p>
3844     * <b>For debug use only</b>
3845     * <p>
3846     * Currently contains:
3847     * <ul>
3848     * <li>successfully acked NOOPs at end of transfer</li>
3849     * <li>unanswered NOOPs at end of transfer</li>
3850     * <li>unanswered NOOPs after fetching additional replies</li>
3851     * <li>Number of IOErrors ignored</li>
3852     * </ul>
3853     * @return the debug array
3854     * @deprecated 3.7 For testing only; may be dropped or changed at any time
3855     */
3856    @Deprecated // only for use in testing
3857    public int[] getCslDebug() {
3858        return __cslDebug;
3859    }
3860    /**
3861     * Set how long to wait for control keep-alive message replies.
3862     *
3863     * @param timeout number of milliseconds to wait (defaults to 1000)
3864     * @since 3.0
3865     * @see #setControlKeepAliveTimeout(long)
3866     */
3867    public void setControlKeepAliveReplyTimeout(int timeout) {
3868        __controlKeepAliveReplyTimeout = timeout;
3869    }
3870
3871    /**
3872     * Get how long to wait for control keep-alive message replies.
3873     * @return wait time in msec
3874     * @since 3.0
3875     */
3876    public int getControlKeepAliveReplyTimeout() {
3877        return __controlKeepAliveReplyTimeout;
3878    }
3879
3880    /**
3881     * Enable or disable passive mode NAT workaround.
3882     * If enabled, a site-local PASV mode reply address will be replaced with the
3883     * remote host address to which the PASV mode request was sent
3884     * (unless that is also a site local address).
3885     * This gets around the problem that some NAT boxes may change the
3886     * reply.
3887     *
3888     * The default is true, i.e. site-local replies are replaced.
3889     * @param enabled true to enable replacing internal IP's in passive
3890     * mode.
3891     * @deprecated (3.6) use {@link #setPassiveNatWorkaroundStrategy(HostnameResolver)} instead
3892     */
3893    @Deprecated
3894    public void setPassiveNatWorkaround(boolean enabled) {
3895        if (enabled) {
3896            this.__passiveNatWorkaroundStrategy = new NatServerResolverImpl(this);
3897        } else {
3898            this.__passiveNatWorkaroundStrategy = null;
3899        }
3900    }
3901
3902    /**
3903     * Set the workaround strategy to replace the PASV mode reply addresses.
3904     * This gets around the problem that some NAT boxes may change the reply.
3905     *
3906     * The default implementation is {@code NatServerResolverImpl}, i.e. site-local
3907     * replies are replaced.
3908     * @param resolver strategy to replace internal IP's in passive mode
3909     * or null to disable the workaround (i.e. use PASV mode reply address.)
3910     * @since 3.6
3911     */
3912    public void setPassiveNatWorkaroundStrategy(HostnameResolver resolver) {
3913        this.__passiveNatWorkaroundStrategy = resolver;
3914    }
3915
3916    /**
3917     * Strategy interface for updating host names received from FTP server
3918     * for passive NAT workaround.
3919     *
3920     * @since 3.6
3921     */
3922    public static interface HostnameResolver {
3923        String resolve(String hostname) throws UnknownHostException;
3924    }
3925
3926    /**
3927     * Default strategy for passive NAT workaround (site-local
3928     * replies are replaced.)
3929     * @since 3.6
3930     */
3931    public static class NatServerResolverImpl implements HostnameResolver {
3932        private FTPClient client;
3933
3934        public NatServerResolverImpl(FTPClient client) {
3935            this.client = client;
3936        }
3937
3938        @Override
3939        public String resolve(String hostname) throws UnknownHostException {
3940            String newHostname = hostname;
3941            InetAddress host = InetAddress.getByName(newHostname);
3942            // reply is a local address, but target is not - assume NAT box changed the PASV reply
3943            if (host.isSiteLocalAddress()) {
3944                InetAddress remote = this.client.getRemoteAddress();
3945                if (!remote.isSiteLocalAddress()){
3946                    newHostname = remote.getHostAddress();
3947                }
3948            }
3949            return newHostname;
3950        }
3951    }
3952
3953    private OutputStream getBufferedOutputStream(OutputStream outputStream) {
3954        if (__bufferSize > 0) {
3955            return new BufferedOutputStream(outputStream, __bufferSize);
3956        }
3957        return new BufferedOutputStream(outputStream);
3958    }
3959
3960    private InputStream getBufferedInputStream(InputStream inputStream) {
3961        if (__bufferSize > 0) {
3962            return new BufferedInputStream(inputStream, __bufferSize);
3963        }
3964        return new BufferedInputStream(inputStream);
3965    }
3966
3967    // @since 3.0
3968    private static class CSL implements CopyStreamListener {
3969
3970        private final FTPClient parent;
3971        private final long idle;
3972        private final int currentSoTimeout;
3973
3974        private long time = System.currentTimeMillis();
3975        private int notAcked;
3976        private int acksAcked;
3977        private int ioErrors;
3978
3979        CSL(FTPClient parent, long idleTime, int maxWait) throws SocketException {
3980            this.idle = idleTime;
3981            this.parent = parent;
3982            this.currentSoTimeout = parent.getSoTimeout();
3983            parent.setSoTimeout(maxWait);
3984        }
3985
3986        @Override
3987        public void bytesTransferred(CopyStreamEvent event) {
3988            bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
3989        }
3990
3991        @Override
3992        public void bytesTransferred(long totalBytesTransferred,
3993                int bytesTransferred, long streamSize) {
3994            long now = System.currentTimeMillis();
3995            if (now - time > idle) {
3996                try {
3997                    parent.__noop();
3998                    acksAcked++;
3999                } catch (SocketTimeoutException e) {
4000                    notAcked++;
4001                } catch (IOException e) {
4002                    ioErrors++;
4003                    // Ignored
4004                }
4005                time = now;
4006            }
4007        }
4008
4009        int[] cleanUp() throws IOException {
4010            int remain = notAcked;
4011            try {
4012                while(notAcked > 0) {
4013                    parent.getReply(); // we do want to see these
4014                    notAcked--; // only decrement if actually received
4015                }
4016            } catch (SocketTimeoutException e) { // NET-584
4017                // ignored
4018            } finally {
4019                parent.setSoTimeout(currentSoTimeout);
4020            }
4021            return new int [] {acksAcked, remain, notAcked, ioErrors}; // debug counts
4022        }
4023
4024    }
4025
4026    /**
4027     * Merge two copystream listeners, either or both of which may be null.
4028     *
4029     * @param local the listener used by this class, may be null
4030     * @return a merged listener or a single listener or null
4031     * @since 3.0
4032     */
4033    private CopyStreamListener __mergeListeners(CopyStreamListener local) {
4034        if (local == null) {
4035            return __copyStreamListener;
4036        }
4037        if (__copyStreamListener == null) {
4038            return local;
4039        }
4040        // Both are non-null
4041        CopyStreamAdapter merged = new CopyStreamAdapter();
4042        merged.addCopyStreamListener(local);
4043        merged.addCopyStreamListener(__copyStreamListener);
4044        return merged;
4045    }
4046
4047    /**
4048     * Enables or disables automatic server encoding detection (only UTF-8 supported).
4049     * <p>
4050     * Does not affect existing connections; must be invoked before a connection is established.
4051     *
4052     * @param autodetect If true, automatic server encoding detection will be enabled.
4053     */
4054    public void setAutodetectUTF8(boolean autodetect)
4055    {
4056        __autodetectEncoding = autodetect;
4057    }
4058
4059    /**
4060     * Tells if automatic server encoding detection is enabled or disabled.
4061     * @return true, if automatic server encoding detection is enabled.
4062     */
4063    public boolean getAutodetectUTF8()
4064    {
4065        return __autodetectEncoding;
4066    }
4067
4068    // Method for use by unit test code only
4069    FTPFileEntryParser getEntryParser() {
4070        return __entryParser;
4071    }
4072
4073    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
4074
4075    /**
4076     * @return the name
4077     * @throws IOException on error
4078     * @deprecated use {@link #getSystemType()} instead
4079     */
4080    @Deprecated
4081    public String getSystemName() throws IOException
4082    {
4083        if (__systemName == null && FTPReply.isPositiveCompletion(syst())) {
4084            __systemName = _replyLines.get(_replyLines.size() - 1).substring(4);
4085        }
4086        return __systemName;
4087    }
4088}
4089
4090/* Emacs configuration
4091 * Local variables:        **
4092 * mode:             java  **
4093 * c-basic-offset:   4     **
4094 * indent-tabs-mode: nil   **
4095 * End:                    **
4096 */
4097/* kate: indent-width 4; replace-tabs on; */