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            final 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 (final IOException e) {
445                    // Ignored
446                } finally {
447                    try {
448                        resourceAsStream.close();
449                    } catch (final 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(final String reply)
532    {
533        final String param = reply.substring(REPLY_CODE_LEN + 1);
534        if (param.startsWith("\"")) {
535            final StringBuilder sb = new StringBuilder();
536            boolean quoteSeen = false;
537            // start after initial quote
538            for(int i=1; i < param.length(); i++) {
539                final 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(final String reply)
569    throws MalformedServerReplyException
570    {
571        final 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        this.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            final int oct1 = Integer.parseInt(m.group(2));
583            final int oct2 = Integer.parseInt(m.group(3));
584            passivePort = (oct1 << 8) | oct2;
585        }
586        catch (final 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                final String newPassiveHost = passiveNatWorkaroundStrategy.resolve(this.passiveHost);
595                if (!this.passiveHost.equals(newPassiveHost)) {
596                    fireReplyReceived(0,
597                            "[Replacing PASV mode reply address "+this.passiveHost+" with "+newPassiveHost+"]\n");
598                    this.passiveHost = newPassiveHost;
599                }
600            } catch (final 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 (final 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        this.passiveHost = getRemoteAddress().getHostAddress();
639        this.passivePort = port;
640    }
641
642    private boolean storeFile(final FTPCmd command, final String remote, final 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(final String command, final String remote, final InputStream local)
658    throws IOException
659    {
660        final 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 (final 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(final FTPCmd command, final 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(final String command, final String remote)
717    throws IOException
718    {
719        final 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_(final int command, final 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_(final FTPCmd command, final 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_(final String command, final 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            try (final ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress())) {
829                // Try EPRT only if remote server is over IPv6, if not use PORT,
830                // because EPRT has no advantage over PORT on IPv4.
831                // It could even have the disadvantage,
832                // that EPRT will make the data connection fail, because
833                // today's intelligent NAT Firewalls are able to
834                // substitute IP addresses in the PORT command,
835                // but might not be able to recognize the EPRT command.
836                if (isInet6Address) {
837                    if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) {
838                        return null;
839                    }
840                } else {
841                    if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) {
842                        return null;
843                    }
844                }
845
846                if ((restartOffset > 0) && !restart(restartOffset)) {
847                    return null;
848                }
849
850                if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
851                    return null;
852                }
853
854                // For now, let's just use the data timeout value for waiting for
855                // the data connection.  It may be desirable to let this be a
856                // separately configurable value.  In any case, we really want
857                // to allow preventing the accept from blocking indefinitely.
858                if (dataTimeout >= 0) {
859                    server.setSoTimeout(dataTimeout);
860                }
861                socket = server.accept();
862
863                // Ensure the timeout is set before any commands are issued on the new socket
864                if (dataTimeout >= 0) {
865                    socket.setSoTimeout(dataTimeout);
866                }
867                if (receiveDataSocketBufferSize > 0) {
868                    socket.setReceiveBufferSize(receiveDataSocketBufferSize);
869                }
870                if (sendDataSocketBufferSize > 0) {
871                    socket.setSendBufferSize(sendDataSocketBufferSize);
872                }
873            }
874        }
875        else
876        { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE
877
878            // Try EPSV command first on IPv6 - and IPv4 if enabled.
879            // When using IPv4 with NAT it has the advantage
880            // to work with more rare configurations.
881            // E.g. if FTP server has a static PASV address (external network)
882            // and the client is coming from another internal network.
883            // In that case the data connection after PASV command would fail,
884            // while EPSV would make the client succeed by taking just the port.
885            final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
886            if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE)
887            {
888                _parseExtendedPassiveModeReply(_replyLines.get(0));
889            }
890            else
891            {
892                if (isInet6Address) {
893                    return null; // Must use EPSV for IPV6
894                }
895                // If EPSV failed on IPV4, revert to PASV
896                if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
897                    return null;
898                }
899                _parsePassiveModeReply(_replyLines.get(0));
900            }
901
902            socket = _socketFactory_.createSocket();
903            if (receiveDataSocketBufferSize > 0) {
904                socket.setReceiveBufferSize(receiveDataSocketBufferSize);
905            }
906            if (sendDataSocketBufferSize > 0) {
907                socket.setSendBufferSize(sendDataSocketBufferSize);
908            }
909            if (passiveLocalHost != null) {
910                socket.bind(new InetSocketAddress(passiveLocalHost, 0));
911            }
912
913            // For now, let's just use the data timeout value for waiting for
914            // the data connection.  It may be desirable to let this be a
915            // separately configurable value.  In any case, we really want
916            // to allow preventing the accept from blocking indefinitely.
917            if (dataTimeout >= 0) {
918                socket.setSoTimeout(dataTimeout);
919            }
920
921            socket.connect(new InetSocketAddress(passiveHost, passivePort), connectTimeout);
922            if ((restartOffset > 0) && !restart(restartOffset))
923            {
924                socket.close();
925                return null;
926            }
927
928            if (!FTPReply.isPositivePreliminary(sendCommand(command, arg)))
929            {
930                socket.close();
931                return null;
932            }
933        }
934
935        if (remoteVerificationEnabled && !verifyRemote(socket))
936        {
937            // Grab the host before we close the socket to avoid NET-663
938            final InetAddress socketHost = socket.getInetAddress();
939
940            socket.close();
941
942            throw new IOException(
943                    "Host attempting data connection " + socketHost.getHostAddress() +
944                    " is not same as server " + getRemoteAddress().getHostAddress());
945        }
946
947        return socket;
948    }
949
950
951    @Override
952    protected void _connectAction_() throws IOException
953    {
954        _connectAction_(null);
955    }
956
957
958    /**
959     * @param socketIsReader the reader to reuse (if non-null)
960     * @throws IOException on error
961     * @since 3.4
962     */
963    @Override
964    protected void _connectAction_(final Reader socketIsReader) throws IOException
965    {
966        super._connectAction_(socketIsReader); // sets up _input_ and _output_
967        initDefaults();
968        // must be after super._connectAction_(), because otherwise we get an
969        // Exception claiming we're not connected
970        if ( autodetectEncoding )
971        {
972            final ArrayList<String> oldReplyLines = new ArrayList<> (_replyLines);
973            final int oldReplyCode = _replyCode;
974            if ( hasFeature("UTF8") || hasFeature("UTF-8")) // UTF8 appears to be the default
975            {
976                 setControlEncoding("UTF-8");
977                 _controlInput_ =
978                     new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding()));
979                 _controlOutput_ =
980                    new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding()));
981            }
982            // restore the original reply (server greeting)
983            _replyLines.clear();
984            _replyLines.addAll(oldReplyLines);
985            _replyCode = oldReplyCode;
986            _newReplyString = true;
987        }
988    }
989
990
991    /**
992     * Sets the timeout in milliseconds to use when reading from the
993     * data connection.  This timeout will be set immediately after
994     * opening the data connection, provided that the value is &ge; 0.
995     * <p>
996     * <b>Note:</b> the timeout will also be applied when calling accept()
997     * whilst establishing an active local data connection.
998     * @param  timeout The default timeout in milliseconds that is used when
999     *        opening a data connection socket. The value 0 means an infinite timeout.
1000     */
1001    public void setDataTimeout(final int timeout)
1002    {
1003        dataTimeout = timeout;
1004    }
1005
1006    /**
1007     * set the factory used for parser creation to the supplied factory object.
1008     *
1009     * @param parserFactory
1010     *               factory object used to create FTPFileEntryParsers
1011     *
1012     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
1013     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
1014     */
1015    public void setParserFactory(final FTPFileEntryParserFactory parserFactory) {
1016        this.parserFactory = parserFactory;
1017    }
1018
1019
1020    /**
1021     * Closes the connection to the FTP server and restores
1022     * connection parameters to the default values.
1023     *
1024     * @throws IOException If an error occurs while disconnecting.
1025     */
1026    @Override
1027    public void disconnect() throws IOException
1028    {
1029        super.disconnect();
1030        initDefaults();
1031    }
1032
1033
1034    /**
1035     * Enable or disable verification that the remote host taking part
1036     * of a data connection is the same as the host to which the control
1037     * connection is attached.  The default is for verification to be
1038     * enabled.  You may set this value at any time, whether the
1039     * FTPClient is currently connected or not.
1040     *
1041     * @param enable True to enable verification, false to disable verification.
1042     */
1043    public void setRemoteVerificationEnabled(final boolean enable)
1044    {
1045        remoteVerificationEnabled = enable;
1046    }
1047
1048    /**
1049     * Return whether or not verification of the remote host participating
1050     * in data connections is enabled.  The default behavior is for
1051     * verification to be enabled.
1052     *
1053     * @return True if verification is enabled, false if not.
1054     */
1055    public boolean isRemoteVerificationEnabled()
1056    {
1057        return remoteVerificationEnabled;
1058    }
1059
1060    /**
1061     * Login to the FTP server using the provided username and password.
1062     *
1063     * @param username The username to login under.
1064     * @param password The password to use.
1065     * @return True if successfully completed, false if not.
1066     * @throws FTPConnectionClosedException
1067     *      If the FTP server prematurely closes the connection as a result
1068     *      of the client being idle or some other reason causing the server
1069     *      to send FTP reply code 421.  This exception may be caught either
1070     *      as an IOException or independently as itself.
1071     * @throws IOException  If an I/O error occurs while either sending a
1072     *      command to the server or receiving a reply from the server.
1073     */
1074    public boolean login(final String username, final String password) throws IOException
1075    {
1076
1077        user(username);
1078
1079        if (FTPReply.isPositiveCompletion(_replyCode)) {
1080            return true;
1081        }
1082
1083        // If we get here, we either have an error code, or an intermmediate
1084        // reply requesting password.
1085        if (!FTPReply.isPositiveIntermediate(_replyCode)) {
1086            return false;
1087        }
1088
1089        return FTPReply.isPositiveCompletion(pass(password));
1090    }
1091
1092
1093    /**
1094     * Login to the FTP server using the provided username, password,
1095     * and account.  If no account is required by the server, only
1096     * the username and password, the account information is not used.
1097     *
1098     * @param username The username to login under.
1099     * @param password The password to use.
1100     * @param account  The account to use.
1101     * @return True if successfully completed, false if not.
1102     * @throws FTPConnectionClosedException
1103     *      If the FTP server prematurely closes the connection as a result
1104     *      of the client being idle or some other reason causing the server
1105     *      to send FTP reply code 421.  This exception may be caught either
1106     *      as an IOException or independently as itself.
1107     * @throws IOException  If an I/O error occurs while either sending a
1108     *      command to the server or receiving a reply from the server.
1109     */
1110    public boolean login(final String username, final String password, final String account)
1111    throws IOException
1112    {
1113        user(username);
1114
1115        if (FTPReply.isPositiveCompletion(_replyCode)) {
1116            return true;
1117        }
1118
1119        // If we get here, we either have an error code, or an intermmediate
1120        // reply requesting password.
1121        if (!FTPReply.isPositiveIntermediate(_replyCode)) {
1122            return false;
1123        }
1124
1125        pass(password);
1126
1127        if (FTPReply.isPositiveCompletion(_replyCode)) {
1128            return true;
1129        }
1130
1131        if (!FTPReply.isPositiveIntermediate(_replyCode)) {
1132            return false;
1133        }
1134
1135        return FTPReply.isPositiveCompletion(acct(account));
1136    }
1137
1138    /**
1139     * Logout of the FTP server by sending the QUIT command.
1140     *
1141     * @return True if successfully completed, false if not.
1142     * @throws FTPConnectionClosedException
1143     *      If the FTP server prematurely closes the connection as a result
1144     *      of the client being idle or some other reason causing the server
1145     *      to send FTP reply code 421.  This exception may be caught either
1146     *      as an IOException or independently as itself.
1147     * @throws IOException  If an I/O error occurs while either sending a
1148     *      command to the server or receiving a reply from the server.
1149     */
1150    public boolean logout() throws IOException
1151    {
1152        return FTPReply.isPositiveCompletion(quit());
1153    }
1154
1155
1156    /**
1157     * Change the current working directory of the FTP session.
1158     *
1159     * @param pathname  The new current working directory.
1160     * @return True if successfully completed, false if not.
1161     * @throws FTPConnectionClosedException
1162     *      If the FTP server prematurely closes the connection as a result
1163     *      of the client being idle or some other reason causing the server
1164     *      to send FTP reply code 421.  This exception may be caught either
1165     *      as an IOException or independently as itself.
1166     * @throws IOException  If an I/O error occurs while either sending a
1167     *      command to the server or receiving a reply from the server.
1168     */
1169    public boolean changeWorkingDirectory(final String pathname) throws IOException
1170    {
1171        return FTPReply.isPositiveCompletion(cwd(pathname));
1172    }
1173
1174
1175    /**
1176     * Change to the parent directory of the current working directory.
1177     *
1178     * @return True if successfully completed, false if not.
1179     * @throws FTPConnectionClosedException
1180     *      If the FTP server prematurely closes the connection as a result
1181     *      of the client being idle or some other reason causing the server
1182     *      to send FTP reply code 421.  This exception may be caught either
1183     *      as an IOException or independently as itself.
1184     * @throws IOException  If an I/O error occurs while either sending a
1185     *      command to the server or receiving a reply from the server.
1186     */
1187    public boolean changeToParentDirectory() throws IOException
1188    {
1189        return FTPReply.isPositiveCompletion(cdup());
1190    }
1191
1192
1193    /**
1194     * Issue the FTP SMNT command.
1195     *
1196     * @param pathname The pathname to mount.
1197     * @return True if successfully completed, false if not.
1198     * @throws FTPConnectionClosedException
1199     *      If the FTP server prematurely closes the connection as a result
1200     *      of the client being idle or some other reason causing the server
1201     *      to send FTP reply code 421.  This exception may be caught either
1202     *      as an IOException or independently as itself.
1203     * @throws IOException  If an I/O error occurs while either sending a
1204     *      command to the server or receiving a reply from the server.
1205     */
1206    public boolean structureMount(final String pathname) throws IOException
1207    {
1208        return FTPReply.isPositiveCompletion(smnt(pathname));
1209    }
1210
1211    /**
1212     * Reinitialize the FTP session.  Not all FTP servers support this
1213     * command, which issues the FTP REIN command.
1214     *
1215     * @return True if successfully completed, false if not.
1216     * @throws FTPConnectionClosedException
1217     *      If the FTP server prematurely closes the connection as a result
1218     *      of the client being idle or some other reason causing the server
1219     *      to send FTP reply code 421.  This exception may be caught either
1220     *      as an IOException or independently as itself.
1221     * @throws IOException  If an I/O error occurs while either sending a
1222     *      command to the server or receiving a reply from the server.
1223     * @since 3.4 (made public)
1224     */
1225    public boolean reinitialize() throws IOException
1226    {
1227        rein();
1228
1229        if (FTPReply.isPositiveCompletion(_replyCode) ||
1230                (FTPReply.isPositivePreliminary(_replyCode) &&
1231                        FTPReply.isPositiveCompletion(getReply())))
1232        {
1233
1234            initDefaults();
1235
1236            return true;
1237        }
1238
1239        return false;
1240    }
1241
1242
1243    /**
1244     * Set the current data connection mode to
1245     * <code>ACTIVE_LOCAL_DATA_CONNECTION_MODE</code>.  No communication
1246     * with the FTP server is conducted, but this causes all future data
1247     * transfers to require the FTP server to connect to the client's
1248     * data port.  Additionally, to accommodate differences between socket
1249     * implementations on different platforms, this method causes the
1250     * client to issue a PORT command before every data transfer.
1251     */
1252    public void enterLocalActiveMode()
1253    {
1254        dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE;
1255        passiveHost = null;
1256        passivePort = -1;
1257    }
1258
1259
1260    /**
1261     * Set the current data connection mode to
1262     * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code>.  Use this
1263     * method only for data transfers between the client and server.
1264     * This method causes a PASV (or EPSV) command to be issued to the server
1265     * before the opening of every data connection, telling the server to
1266     * open a data port to which the client will connect to conduct
1267     * data transfers.  The FTPClient will stay in
1268     * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code> until the
1269     * mode is changed by calling some other method such as
1270     * {@link #enterLocalActiveMode  enterLocalActiveMode() }
1271     * <p>
1272     * <b>N.B.</b> currently calling any connect method will reset the mode to
1273     * ACTIVE_LOCAL_DATA_CONNECTION_MODE.
1274     */
1275    public void enterLocalPassiveMode()
1276    {
1277        dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE;
1278        // These will be set when just before a data connection is opened
1279        // in _openDataConnection_()
1280        passiveHost = null;
1281        passivePort = -1;
1282    }
1283
1284
1285    /**
1286     * Set the current data connection mode to
1287     * <code> ACTIVE_REMOTE_DATA_CONNECTION </code>.  Use this method only
1288     * for server to server data transfers.  This method issues a PORT
1289     * command to the server, indicating the other server and port to which
1290     * it should connect for data transfers.  You must call this method
1291     * before EVERY server to server transfer attempt.  The FTPClient will
1292     * NOT automatically continue to issue PORT commands.  You also
1293     * must remember to call
1294     * {@link #enterLocalActiveMode  enterLocalActiveMode() } if you
1295     * wish to return to the normal data connection mode.
1296     *
1297     * @param host The passive mode server accepting connections for data
1298     *             transfers.
1299     * @param port The passive mode server's data port.
1300     * @return True if successfully completed, false if not.
1301     * @throws FTPConnectionClosedException
1302     *      If the FTP server prematurely closes the connection as a result
1303     *      of the client being idle or some other reason causing the server
1304     *      to send FTP reply code 421.  This exception may be caught either
1305     *      as an IOException or independently as itself.
1306     * @throws IOException  If an I/O error occurs while either sending a
1307     *      command to the server or receiving a reply from the server.
1308     */
1309    public boolean enterRemoteActiveMode(final InetAddress host, final int port)
1310    throws IOException
1311    {
1312        if (FTPReply.isPositiveCompletion(port(host, port)))
1313        {
1314            dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE;
1315            passiveHost = null;
1316            passivePort = -1;
1317            return true;
1318        }
1319        return false;
1320    }
1321
1322    /**
1323     * Set the current data connection mode to
1324     * <code> PASSIVE_REMOTE_DATA_CONNECTION_MODE </code>.  Use this
1325     * method only for server to server data transfers.
1326     * This method issues a PASV command to the server, telling it to
1327     * open a data port to which the active server will connect to conduct
1328     * data transfers.  You must call this method
1329     * before EVERY server to server transfer attempt.  The FTPClient will
1330     * NOT automatically continue to issue PASV commands.  You also
1331     * must remember to call
1332     * {@link #enterLocalActiveMode  enterLocalActiveMode() } if you
1333     * wish to return to the normal data connection mode.
1334     *
1335     * @return True if successfully completed, false if not.
1336     * @throws FTPConnectionClosedException
1337     *      If the FTP server prematurely closes the connection as a result
1338     *      of the client being idle or some other reason causing the server
1339     *      to send FTP reply code 421.  This exception may be caught either
1340     *      as an IOException or independently as itself.
1341     * @throws IOException  If an I/O error occurs while either sending a
1342     *      command to the server or receiving a reply from the server.
1343     */
1344    public boolean enterRemotePassiveMode() throws IOException
1345    {
1346        if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
1347            return false;
1348        }
1349
1350        dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE;
1351        _parsePassiveModeReply(_replyLines.get(0));
1352
1353        return true;
1354    }
1355
1356    /**
1357     * Returns the hostname or IP address (in the form of a string) returned
1358     * by the server when entering passive mode.  If not in passive mode,
1359     * returns null.  This method only returns a valid value AFTER a
1360     * data connection has been opened after a call to
1361     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1362     * This is because FTPClient sends a PASV command to the server only
1363     * just before opening a data connection, and not when you call
1364     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1365     *
1366     * @return The passive host name if in passive mode, otherwise null.
1367     */
1368    public String getPassiveHost()
1369    {
1370        return passiveHost;
1371    }
1372
1373    /**
1374     * If in passive mode, returns the data port of the passive host.
1375     * This method only returns a valid value AFTER a
1376     * data connection has been opened after a call to
1377     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1378     * This is because FTPClient sends a PASV command to the server only
1379     * just before opening a data connection, and not when you call
1380     * {@link #enterLocalPassiveMode enterLocalPassiveMode()}.
1381     *
1382     * @return The data port of the passive server.  If not in passive
1383     *         mode, undefined.
1384     */
1385    public int getPassivePort()
1386    {
1387        return passivePort;
1388    }
1389
1390
1391    /**
1392     * Returns the current data connection mode (one of the
1393     * <code> _DATA_CONNECTION_MODE </code> constants.
1394     *
1395     * @return The current data connection mode (one of the
1396     * <code> _DATA_CONNECTION_MODE </code> constants.
1397     */
1398    public int getDataConnectionMode()
1399    {
1400        return dataConnectionMode;
1401    }
1402
1403    /**
1404     * Get the client port for active mode.
1405     *
1406     * @return The client port for active mode.
1407     */
1408    private int getActivePort()
1409    {
1410        if (activeMinPort > 0 && activeMaxPort >= activeMinPort)
1411        {
1412            if (activeMaxPort == activeMinPort) {
1413                return activeMaxPort;
1414            }
1415            // Get a random port between the min and max port range
1416            return random.nextInt(activeMaxPort - activeMinPort + 1) + activeMinPort;
1417        }
1418        // default port
1419        return 0;
1420    }
1421
1422    /**
1423     * Get the host address for active mode; allows the local address to be overridden.
1424     *
1425     * @return __activeExternalHost if non-null, else getLocalAddress()
1426     * @see #setActiveExternalIPAddress(String)
1427     */
1428    private InetAddress getHostAddress()
1429    {
1430        if (activeExternalHost != null)
1431        {
1432            return activeExternalHost;
1433        }
1434        // default local address
1435        return getLocalAddress();
1436    }
1437
1438    /**
1439     * Get the reported host address for active mode EPRT/PORT commands;
1440     * allows override of {@link #getHostAddress()}.
1441     *
1442     * Useful for FTP Client behind Firewall NAT.
1443     *
1444     * @return __reportActiveExternalHost if non-null, else getHostAddress();
1445     */
1446    private InetAddress getReportHostAddress() {
1447        if (reportActiveExternalHost != null) {
1448            return reportActiveExternalHost ;
1449        }
1450        return getHostAddress();
1451    }
1452
1453    /**
1454     * Set the client side port range in active mode.
1455     *
1456     * @param minPort The lowest available port (inclusive).
1457     * @param maxPort The highest available port (inclusive).
1458     * @since 2.2
1459     */
1460    public void setActivePortRange(final int minPort, final int maxPort)
1461    {
1462        this.activeMinPort = minPort;
1463        this.activeMaxPort = maxPort;
1464    }
1465
1466    /**
1467     * Set the external IP address in active mode.
1468     * Useful when there are multiple network cards.
1469     *
1470     * @param ipAddress The external IP address of this machine.
1471     * @throws UnknownHostException if the ipAddress cannot be resolved
1472     * @since 2.2
1473     */
1474    public void setActiveExternalIPAddress(final String ipAddress) throws UnknownHostException
1475    {
1476        this.activeExternalHost = InetAddress.getByName(ipAddress);
1477    }
1478
1479    /**
1480     * Set the local IP address to use in passive mode.
1481     * Useful when there are multiple network cards.
1482     *
1483     * @param ipAddress The local IP address of this machine.
1484     * @throws UnknownHostException if the ipAddress cannot be resolved
1485     */
1486    public void setPassiveLocalIPAddress(final String ipAddress) throws UnknownHostException
1487    {
1488        this.passiveLocalHost = InetAddress.getByName(ipAddress);
1489    }
1490
1491    /**
1492     * Set the local IP address to use in passive mode.
1493     * Useful when there are multiple network cards.
1494     *
1495     * @param inetAddress The local IP address of this machine.
1496     */
1497    public void setPassiveLocalIPAddress(final InetAddress inetAddress)
1498    {
1499        this.passiveLocalHost = inetAddress;
1500    }
1501
1502    /**
1503     * Set the local IP address in passive mode.
1504     * Useful when there are multiple network cards.
1505     *
1506     * @return The local IP address in passive mode.
1507     */
1508    public InetAddress getPassiveLocalIPAddress()
1509    {
1510        return this.passiveLocalHost;
1511    }
1512
1513    /**
1514     * Set the external IP address to report in EPRT/PORT commands in active mode.
1515     * Useful when there are multiple network cards.
1516     *
1517     * @param ipAddress The external IP address of this machine.
1518     * @throws UnknownHostException if the ipAddress cannot be resolved
1519     * @since 3.1
1520     * @see #getReportHostAddress()
1521     */
1522    public void setReportActiveExternalIPAddress(final String ipAddress) throws UnknownHostException
1523    {
1524        this.reportActiveExternalHost = InetAddress.getByName(ipAddress);
1525    }
1526
1527
1528    /**
1529     * Sets the file type to be transferred.  This should be one of
1530     * <code> FTP.ASCII_FILE_TYPE </code>, <code> FTP.BINARY_FILE_TYPE</code>,
1531     * etc.  The file type only needs to be set when you want to change the
1532     * type.  After changing it, the new type stays in effect until you change
1533     * it again.  The default file type is <code> FTP.ASCII_FILE_TYPE </code>
1534     * if this method is never called.
1535     * <br>
1536     * The server default is supposed to be ASCII (see RFC 959), however many
1537     * ftp servers default to BINARY. <b>To ensure correct operation with all servers,
1538     * always specify the appropriate file type after connecting to the server.</b>
1539     * <br>
1540     * <p>
1541     * <b>N.B.</b> currently calling any connect method will reset the type to
1542     * FTP.ASCII_FILE_TYPE.
1543     * @param fileType The <code> _FILE_TYPE </code> constant indicating the
1544     *                 type of file.
1545     * @return True if successfully completed, false if not.
1546     * @throws FTPConnectionClosedException
1547     *      If the FTP server prematurely closes the connection as a result
1548     *      of the client being idle or some other reason causing the server
1549     *      to send FTP reply code 421.  This exception may be caught either
1550     *      as an IOException or independently as itself.
1551     * @throws IOException  If an I/O error occurs while either sending a
1552     *      command to the server or receiving a reply from the server.
1553     */
1554    public boolean setFileType(final int fileType) throws IOException
1555    {
1556        if (FTPReply.isPositiveCompletion(type(fileType)))
1557        {
1558            this.fileType = fileType;
1559            this.fileFormat = FTP.NON_PRINT_TEXT_FORMAT;
1560            return true;
1561        }
1562        return false;
1563    }
1564
1565
1566    /**
1567     * Sets the file type to be transferred and the format.  The type should be
1568     * one of  <code> FTP.ASCII_FILE_TYPE </code>,
1569     * <code> FTP.BINARY_FILE_TYPE </code>, etc.  The file type only needs to
1570     * be set when you want to change the type.  After changing it, the new
1571     * type stays in effect until you change it again.  The default file type
1572     * is <code> FTP.ASCII_FILE_TYPE </code> if this method is never called.
1573     * <br>
1574     * The server default is supposed to be ASCII (see RFC 959), however many
1575     * ftp servers default to BINARY. <b>To ensure correct operation with all servers,
1576     * always specify the appropriate file type after connecting to the server.</b>
1577     * <br>
1578     * The format should be one of the FTP class <code> TEXT_FORMAT </code>
1579     * constants, or if the type is <code> FTP.LOCAL_FILE_TYPE </code>, the
1580     * format should be the byte size for that type.  The default format
1581     * is <code> FTP.NON_PRINT_TEXT_FORMAT </code> if this method is never
1582     * called.
1583     * <p>
1584     * <b>N.B.</b> currently calling any connect method will reset the type to
1585     * FTP.ASCII_FILE_TYPE and the formatOrByteSize to FTP.NON_PRINT_TEXT_FORMAT.
1586     *
1587     * @param fileType The <code> _FILE_TYPE </code> constant indicating the
1588     *                 type of file.
1589     * @param formatOrByteSize  The format of the file (one of the
1590     *              <code>_FORMAT</code> constants.  In the case of
1591     *              <code>LOCAL_FILE_TYPE</code>, the byte size.
1592     *
1593     * @return True if successfully completed, false if not.
1594     * @throws FTPConnectionClosedException
1595     *      If the FTP server prematurely closes the connection as a result
1596     *      of the client being idle or some other reason causing the server
1597     *      to send FTP reply code 421.  This exception may be caught either
1598     *      as an IOException or independently as itself.
1599     * @throws IOException  If an I/O error occurs while either sending a
1600     *      command to the server or receiving a reply from the server.
1601     */
1602    public boolean setFileType(final int fileType, final int formatOrByteSize)
1603    throws IOException
1604    {
1605        if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize)))
1606        {
1607            this.fileType = fileType;
1608            this.fileFormat = formatOrByteSize;
1609            return true;
1610        }
1611        return false;
1612    }
1613
1614
1615    /**
1616     * Sets the file structure.  The default structure is
1617     * <code> FTP.FILE_STRUCTURE </code> if this method is never called
1618     * or if a connect method is called.
1619     *
1620     * @param structure  The structure of the file (one of the FTP class
1621     *         <code>_STRUCTURE</code> constants).
1622     * @return True if successfully completed, false if not.
1623     * @throws FTPConnectionClosedException
1624     *      If the FTP server prematurely closes the connection as a result
1625     *      of the client being idle or some other reason causing the server
1626     *      to send FTP reply code 421.  This exception may be caught either
1627     *      as an IOException or independently as itself.
1628     * @throws IOException  If an I/O error occurs while either sending a
1629     *      command to the server or receiving a reply from the server.
1630     */
1631    public boolean setFileStructure(final int structure) throws IOException
1632    {
1633        if (FTPReply.isPositiveCompletion(stru(structure)))
1634        {
1635            fileStructure = structure;
1636            return true;
1637        }
1638        return false;
1639    }
1640
1641
1642    /**
1643     * Sets the transfer mode.  The default transfer mode
1644     * <code> FTP.STREAM_TRANSFER_MODE </code> if this method is never called
1645     * or if a connect method is called.
1646     *
1647     * @param mode  The new transfer mode to use (one of the FTP class
1648     *         <code>_TRANSFER_MODE</code> constants).
1649     * @return True if successfully completed, false if not.
1650     * @throws FTPConnectionClosedException
1651     *      If the FTP server prematurely closes the connection as a result
1652     *      of the client being idle or some other reason causing the server
1653     *      to send FTP reply code 421.  This exception may be caught either
1654     *      as an IOException or independently as itself.
1655     * @throws IOException  If an I/O error occurs while either sending a
1656     *      command to the server or receiving a reply from the server.
1657     */
1658    public boolean setFileTransferMode(final int mode) throws IOException
1659    {
1660        if (FTPReply.isPositiveCompletion(mode(mode)))
1661        {
1662            fileTransferMode = mode;
1663            return true;
1664        }
1665        return false;
1666    }
1667
1668
1669    /**
1670     * Initiate a server to server file transfer.  This method tells the
1671     * server to which the client is connected to retrieve a given file from
1672     * the other server.
1673     *
1674     * @param fileName  The name of the file to retrieve.
1675     * @return True if successfully completed, false if not.
1676     * @throws FTPConnectionClosedException
1677     *      If the FTP server prematurely closes the connection as a result
1678     *      of the client being idle or some other reason causing the server
1679     *      to send FTP reply code 421.  This exception may be caught either
1680     *      as an IOException or independently as itself.
1681     * @throws IOException  If an I/O error occurs while either sending a
1682     *      command to the server or receiving a reply from the server.
1683     */
1684    public boolean remoteRetrieve(final String fileName) throws IOException
1685    {
1686        if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1687                dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1688            return FTPReply.isPositivePreliminary(retr(fileName));
1689        }
1690        return false;
1691    }
1692
1693
1694    /**
1695     * Initiate a server to server file transfer.  This method tells the
1696     * server to which the client is connected to store a file on
1697     * the other server using the given file name.  The other server must
1698     * have had a <code> remoteRetrieve </code> issued to it by another
1699     * FTPClient.
1700     *
1701     * @param fileName  The name to call the file that is to be stored.
1702     * @return True if successfully completed, false if not.
1703     * @throws FTPConnectionClosedException
1704     *      If the FTP server prematurely closes the connection as a result
1705     *      of the client being idle or some other reason causing the server
1706     *      to send FTP reply code 421.  This exception may be caught either
1707     *      as an IOException or independently as itself.
1708     * @throws IOException  If an I/O error occurs while either sending a
1709     *      command to the server or receiving a reply from the server.
1710     */
1711    public boolean remoteStore(final String fileName) throws IOException
1712    {
1713        if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1714                dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1715            return FTPReply.isPositivePreliminary(stor(fileName));
1716        }
1717        return false;
1718    }
1719
1720
1721    /**
1722     * Initiate a server to server file transfer.  This method tells the
1723     * server to which the client is connected to store a file on
1724     * the other server using a unique file name based on the given file name.
1725     * The other server must have had a <code> remoteRetrieve </code> issued
1726     * to it by another FTPClient.
1727     *
1728     * @param fileName  The name on which to base the file name of the file
1729     *                  that is to be stored.
1730     * @return True if successfully completed, false if not.
1731     * @throws FTPConnectionClosedException
1732     *      If the FTP server prematurely closes the connection as a result
1733     *      of the client being idle or some other reason causing the server
1734     *      to send FTP reply code 421.  This exception may be caught either
1735     *      as an IOException or independently as itself.
1736     * @throws IOException  If an I/O error occurs while either sending a
1737     *      command to the server or receiving a reply from the server.
1738     */
1739    public boolean remoteStoreUnique(final String fileName) throws IOException
1740    {
1741        if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1742                dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1743            return FTPReply.isPositivePreliminary(stou(fileName));
1744        }
1745        return false;
1746    }
1747
1748
1749    /**
1750     * Initiate a server to server file transfer.  This method tells the
1751     * server to which the client is connected to store a file on
1752     * the other server using a unique file name.
1753     * The other server must have had a <code> remoteRetrieve </code> issued
1754     * to it by another FTPClient.  Many FTP servers require that a base
1755     * file name be given from which the unique file name can be derived.  For
1756     * those servers use the other version of <code> remoteStoreUnique</code>
1757     *
1758     * @return True if successfully completed, false if not.
1759     * @throws FTPConnectionClosedException
1760     *      If the FTP server prematurely closes the connection as a result
1761     *      of the client being idle or some other reason causing the server
1762     *      to send FTP reply code 421.  This exception may be caught either
1763     *      as an IOException or independently as itself.
1764     * @throws IOException  If an I/O error occurs while either sending a
1765     *      command to the server or receiving a reply from the server.
1766     */
1767    public boolean remoteStoreUnique() throws IOException
1768    {
1769        if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1770                dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1771            return FTPReply.isPositivePreliminary(stou());
1772        }
1773        return false;
1774    }
1775
1776    // For server to server transfers
1777    /**
1778     * Initiate a server to server file transfer.  This method tells the
1779     * server to which the client is connected to append to a given file on
1780     * the other server.  The other server must have had a
1781     * <code> remoteRetrieve </code> issued to it by another FTPClient.
1782     *
1783     * @param fileName  The name of the file to be appended to, or if the
1784     *        file does not exist, the name to call the file being stored.
1785     *
1786     * @return True if successfully completed, false if not.
1787     * @throws FTPConnectionClosedException
1788     *      If the FTP server prematurely closes the connection as a result
1789     *      of the client being idle or some other reason causing the server
1790     *      to send FTP reply code 421.  This exception may be caught either
1791     *      as an IOException or independently as itself.
1792     * @throws IOException  If an I/O error occurs while either sending a
1793     *      command to the server or receiving a reply from the server.
1794     */
1795    public boolean remoteAppend(final String fileName) throws IOException
1796    {
1797        if (dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE ||
1798                dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) {
1799            return FTPReply.isPositivePreliminary(appe(fileName));
1800        }
1801        return false;
1802    }
1803
1804    /**
1805     * There are a few FTPClient methods that do not complete the
1806     * entire sequence of FTP commands to complete a transaction.  These
1807     * commands require some action by the programmer after the reception
1808     * of a positive intermediate command.  After the programmer's code
1809     * completes its actions, it must call this method to receive
1810     * the completion reply from the server and verify the success of the
1811     * entire transaction.
1812     * <p>
1813     * For example,
1814     * <pre>
1815     * InputStream input;
1816     * OutputStream output;
1817     * input  = new FileInputStream("foobaz.txt");
1818     * output = ftp.storeFileStream("foobar.txt")
1819     * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
1820     *     input.close();
1821     *     output.close();
1822     *     ftp.logout();
1823     *     ftp.disconnect();
1824     *     System.err.println("File transfer failed.");
1825     *     System.exit(1);
1826     * }
1827     * Util.copyStream(input, output);
1828     * input.close();
1829     * output.close();
1830     * // Must call completePendingCommand() to finish command.
1831     * if(!ftp.completePendingCommand()) {
1832     *     ftp.logout();
1833     *     ftp.disconnect();
1834     *     System.err.println("File transfer failed.");
1835     *     System.exit(1);
1836     * }
1837     * </pre>
1838     *
1839     * @return True if successfully completed, false if not.
1840     * @throws FTPConnectionClosedException
1841     *      If the FTP server prematurely closes the connection as a result
1842     *      of the client being idle or some other reason causing the server
1843     *      to send FTP reply code 421.  This exception may be caught either
1844     *      as an IOException or independently as itself.
1845     * @throws IOException  If an I/O error occurs while either sending a
1846     *      command to the server or receiving a reply from the server.
1847     */
1848    public boolean completePendingCommand() throws IOException
1849    {
1850        return FTPReply.isPositiveCompletion(getReply());
1851    }
1852
1853
1854    /**
1855     * Retrieves a named file from the server and writes it to the given
1856     * OutputStream.  This method does NOT close the given OutputStream.
1857     * If the current file type is ASCII, line separators in the file are
1858     * converted to the local representation.
1859     * <p>
1860     * Note: if you have used {@link #setRestartOffset(long)},
1861     * the file data will start from the selected offset.
1862     * @param remote  The name of the remote file.
1863     * @param local   The local OutputStream to which to write the file.
1864     * @return True if successfully completed, false if not.
1865     * @throws FTPConnectionClosedException
1866     *      If the FTP server prematurely closes the connection as a result
1867     *      of the client being idle or some other reason causing the server
1868     *      to send FTP reply code 421.  This exception may be caught either
1869     *      as an IOException or independently as itself.
1870     * @throws org.apache.commons.net.io.CopyStreamException
1871     *      If an I/O error occurs while actually
1872     *      transferring the file.  The CopyStreamException allows you to
1873     *      determine the number of bytes transferred and the IOException
1874     *      causing the error.  This exception may be caught either
1875     *      as an IOException or independently as itself.
1876     * @throws IOException  If an I/O error occurs while either sending a
1877     *      command to the server or receiving a reply from the server.
1878     */
1879    public boolean retrieveFile(final String remote, final OutputStream local)
1880    throws IOException
1881    {
1882        return _retrieveFile(FTPCmd.RETR.getCommand(), remote, local);
1883    }
1884
1885    /**
1886     * @param command the command to get
1887     * @param remote the remote file name
1888     * @param local The local OutputStream to which to write the file.
1889     * @return true if successful
1890     * @throws IOException on error
1891     * @since 3.1
1892     */
1893    protected boolean _retrieveFile(final String command, final String remote, final OutputStream local)
1894    throws IOException
1895    {
1896        final Socket socket = _openDataConnection_(command, remote);
1897
1898        if (socket == null) {
1899            return false;
1900        }
1901
1902        final InputStream input;
1903        if (fileType == ASCII_FILE_TYPE) {
1904            input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream()));
1905        } else {
1906            input = getBufferedInputStream(socket.getInputStream());
1907        }
1908
1909        CSL csl = null;
1910        if (controlKeepAliveTimeout > 0) {
1911            csl = new CSL(this, controlKeepAliveTimeout, controlKeepAliveReplyTimeout);
1912        }
1913
1914        // Treat everything else as binary for now
1915        try
1916        {
1917            Util.copyStream(input, local, getBufferSize(),
1918                    CopyStreamEvent.UNKNOWN_STREAM_SIZE, mergeListeners(csl),
1919                    false);
1920
1921            // Get the transfer response
1922            return completePendingCommand();
1923        } finally {
1924            Util.closeQuietly(input);
1925            Util.closeQuietly(socket);
1926            if (csl != null) {
1927                cslDebug = csl.cleanUp(); // fetch any outstanding keepalive replies
1928            }
1929        }
1930    }
1931
1932    /**
1933     * Returns an InputStream from which a named file from the server
1934     * can be read.  If the current file type is ASCII, the returned
1935     * InputStream will convert line separators in the file to
1936     * the local representation.  You must close the InputStream when you
1937     * finish reading from it.  The InputStream itself will take care of
1938     * closing the parent data connection socket upon being closed.
1939     * <p>
1940     * <b>To finalize the file transfer you must call
1941     * {@link #completePendingCommand  completePendingCommand } and
1942     * check its return value to verify success.</b>
1943     * If this is not done, subsequent commands may behave unexpectedly.
1944     * <p>
1945     * Note: if you have used {@link #setRestartOffset(long)},
1946     * the file data will start from the selected offset.
1947     *
1948     * @param remote  The name of the remote file.
1949     * @return An InputStream from which the remote file can be read.  If
1950     *      the data connection cannot be opened (e.g., the file does not
1951     *      exist), null is returned (in which case you may check the reply
1952     *      code to determine the exact reason for failure).
1953     * @throws FTPConnectionClosedException
1954     *      If the FTP server prematurely closes the connection as a result
1955     *      of the client being idle or some other reason causing the server
1956     *      to send FTP reply code 421.  This exception may be caught either
1957     *      as an IOException or independently as itself.
1958     * @throws IOException  If an I/O error occurs while either sending a
1959     *      command to the server or receiving a reply from the server.
1960     */
1961    public InputStream retrieveFileStream(final String remote) throws IOException
1962    {
1963        return _retrieveFileStream(FTPCmd.RETR.getCommand(), remote);
1964    }
1965
1966    /**
1967     * @param command the command to send
1968     * @param remote the remote file name
1969     * @return the stream from which to read the file
1970     * @throws IOException on error
1971     * @since 3.1
1972     */
1973    protected InputStream _retrieveFileStream(final String command, final String remote)
1974    throws IOException
1975    {
1976        final Socket socket = _openDataConnection_(command, remote);
1977
1978        if (socket == null) {
1979            return null;
1980        }
1981
1982        final InputStream input;
1983        if (fileType == ASCII_FILE_TYPE) {
1984            // We buffer ascii transfers because the buffering has to
1985            // be interposed between FromNetASCIIOutputSream and the underlying
1986            // socket input stream.  We don't buffer binary transfers
1987            // because we don't want to impose a buffering policy on the
1988            // programmer if possible.  Programmers can decide on their
1989            // own if they want to wrap the SocketInputStream we return
1990            // for file types other than ASCII.
1991            input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream()));
1992        } else {
1993            input = socket.getInputStream();
1994        }
1995        return new org.apache.commons.net.io.SocketInputStream(socket, input);
1996    }
1997
1998
1999    /**
2000     * Stores a file on the server using the given name and taking input
2001     * from the given InputStream.  This method does NOT close the given
2002     * InputStream.  If the current file type is ASCII, line separators in
2003     * the file are transparently converted to the NETASCII format (i.e.,
2004     * you should not attempt to create a special InputStream to do this).
2005     *
2006     * @param remote  The name to give the remote file.
2007     * @param local   The local InputStream from which to read the file.
2008     * @return True if successfully completed, false if not.
2009     * @throws FTPConnectionClosedException
2010     *      If the FTP server prematurely closes the connection as a result
2011     *      of the client being idle or some other reason causing the server
2012     *      to send FTP reply code 421.  This exception may be caught either
2013     *      as an IOException or independently as itself.
2014     * @throws org.apache.commons.net.io.CopyStreamException
2015     *      If an I/O error occurs while actually
2016     *      transferring the file.  The CopyStreamException allows you to
2017     *      determine the number of bytes transferred and the IOException
2018     *      causing the error.  This exception may be caught either
2019     *      as an IOException or independently as itself.
2020     * @throws IOException  If an I/O error occurs while either sending a
2021     *      command to the server or receiving a reply from the server.
2022     */
2023    public boolean storeFile(final String remote, final InputStream local)
2024    throws IOException
2025    {
2026        return storeFile(FTPCmd.STOR, remote, local);
2027    }
2028
2029
2030    /**
2031     * Returns an OutputStream through which data can be written to store
2032     * a file on the server using the given name.  If the current file type
2033     * is ASCII, the returned OutputStream will convert line separators in
2034     * the file to the NETASCII format  (i.e., you should not attempt to
2035     * create a special OutputStream to do this).  You must close the
2036     * OutputStream when you finish writing to it.  The OutputStream itself
2037     * will take care of closing the parent data connection socket upon being
2038     * closed.
2039     * <p>
2040     * <b>To finalize the file transfer you must call
2041     * {@link #completePendingCommand  completePendingCommand } and
2042     * check its return value to verify success.</b>
2043     * If this is not done, subsequent commands may behave unexpectedly.
2044     *
2045     * @param remote  The name to give the remote file.
2046     * @return An OutputStream through which the remote file can be written.  If
2047     *      the data connection cannot be opened (e.g., the file does not
2048     *      exist), null is returned (in which case you may check the reply
2049     *      code to determine the exact reason for failure).
2050     * @throws FTPConnectionClosedException
2051     *      If the FTP server prematurely closes the connection as a result
2052     *      of the client being idle or some other reason causing the server
2053     *      to send FTP reply code 421.  This exception may be caught either
2054     *      as an IOException or independently as itself.
2055     * @throws IOException  If an I/O error occurs while either sending a
2056     *      command to the server or receiving a reply from the server.
2057     */
2058    public OutputStream storeFileStream(final String remote) throws IOException
2059    {
2060        return storeFileStream(FTPCmd.STOR, remote);
2061    }
2062
2063    /**
2064     * Appends to a file on the server with the given name, taking input
2065     * from the given InputStream.  This method does NOT close the given
2066     * InputStream.  If the current file type is ASCII, line separators in
2067     * the file are transparently converted to the NETASCII format (i.e.,
2068     * you should not attempt to create a special InputStream to do this).
2069     *
2070     * @param remote  The name of the remote file.
2071     * @param local   The local InputStream from which to read the data to
2072     *                be appended to the remote file.
2073     * @return True if successfully completed, false if not.
2074     * @throws FTPConnectionClosedException
2075     *      If the FTP server prematurely closes the connection as a result
2076     *      of the client being idle or some other reason causing the server
2077     *      to send FTP reply code 421.  This exception may be caught either
2078     *      as an IOException or independently as itself.
2079     * @throws org.apache.commons.net.io.CopyStreamException
2080     *      If an I/O error occurs while actually
2081     *      transferring the file.  The CopyStreamException allows you to
2082     *      determine the number of bytes transferred and the IOException
2083     *      causing the error.  This exception may be caught either
2084     *      as an IOException or independently as itself.
2085     * @throws IOException  If an I/O error occurs while either sending a
2086     *      command to the server or receiving a reply from the server.
2087     */
2088    public boolean appendFile(final String remote, final InputStream local)
2089    throws IOException
2090    {
2091        return storeFile(FTPCmd.APPE, remote, local);
2092    }
2093
2094    /**
2095     * Returns an OutputStream through which data can be written to append
2096     * to a file on the server with the given name.  If the current file type
2097     * is ASCII, the returned OutputStream will convert line separators in
2098     * the file to the NETASCII format  (i.e., you should not attempt to
2099     * create a special OutputStream to do this).  You must close the
2100     * OutputStream when you finish writing to it.  The OutputStream itself
2101     * will take care of closing the parent data connection socket upon being
2102     * closed.
2103     * <p>
2104     * <b>To finalize the file transfer you must call
2105     * {@link #completePendingCommand  completePendingCommand } and
2106     * check its return value to verify success.</b>
2107     * If this is not done, subsequent commands may behave unexpectedly.
2108     *
2109     * @param remote  The name of the remote file.
2110     * @return An OutputStream through which the remote file can be appended.
2111     *      If the data connection cannot be opened (e.g., the file does not
2112     *      exist), null is returned (in which case you may check the reply
2113     *      code to determine the exact reason for failure).
2114     * @throws FTPConnectionClosedException
2115     *      If the FTP server prematurely closes the connection as a result
2116     *      of the client being idle or some other reason causing the server
2117     *      to send FTP reply code 421.  This exception may be caught either
2118     *      as an IOException or independently as itself.
2119     * @throws IOException  If an I/O error occurs while either sending a
2120     *      command to the server or receiving a reply from the server.
2121     */
2122    public OutputStream appendFileStream(final String remote) throws IOException
2123    {
2124        return storeFileStream(FTPCmd.APPE, remote);
2125    }
2126
2127    /**
2128     * Stores a file on the server using a unique name derived from the
2129     * given name and taking input
2130     * from the given InputStream.  This method does NOT close the given
2131     * InputStream.  If the current file type is ASCII, line separators in
2132     * the file are transparently converted to the NETASCII format (i.e.,
2133     * you should not attempt to create a special InputStream to do this).
2134     *
2135     * @param remote  The name on which to base the unique name given to
2136     *                the remote file.
2137     * @param local   The local InputStream from which to read the file.
2138     * @return True if successfully completed, false if not.
2139     * @throws FTPConnectionClosedException
2140     *      If the FTP server prematurely closes the connection as a result
2141     *      of the client being idle or some other reason causing the server
2142     *      to send FTP reply code 421.  This exception may be caught either
2143     *      as an IOException or independently as itself.
2144     * @throws org.apache.commons.net.io.CopyStreamException
2145     *      If an I/O error occurs while actually
2146     *      transferring the file.  The CopyStreamException allows you to
2147     *      determine the number of bytes transferred and the IOException
2148     *      causing the error.  This exception may be caught either
2149     *      as an IOException or independently as itself.
2150     * @throws IOException  If an I/O error occurs while either sending a
2151     *      command to the server or receiving a reply from the server.
2152     */
2153    public boolean storeUniqueFile(final String remote, final InputStream local)
2154    throws IOException
2155    {
2156        return storeFile(FTPCmd.STOU, remote, local);
2157    }
2158
2159
2160    /**
2161     * Returns an OutputStream through which data can be written to store
2162     * a file on the server using a unique name derived from the given name.
2163     * If the current file type
2164     * is ASCII, the returned OutputStream will convert line separators in
2165     * the file to the NETASCII format  (i.e., you should not attempt to
2166     * create a special OutputStream to do this).  You must close the
2167     * OutputStream when you finish writing to it.  The OutputStream itself
2168     * will take care of closing the parent data connection socket upon being
2169     * closed.
2170     * <p>
2171     * <b>To finalize the file transfer you must call
2172     * {@link #completePendingCommand  completePendingCommand } and
2173     * check its return value to verify success.</b>
2174     * If this is not done, subsequent commands may behave unexpectedly.
2175     *
2176     * @param remote  The name on which to base the unique name given to
2177     *                the remote file.
2178     * @return An OutputStream through which the remote file can be written.  If
2179     *      the data connection cannot be opened (e.g., the file does not
2180     *      exist), null is returned (in which case you may check the reply
2181     *      code to determine the exact reason for failure).
2182     * @throws FTPConnectionClosedException
2183     *      If the FTP server prematurely closes the connection as a result
2184     *      of the client being idle or some other reason causing the server
2185     *      to send FTP reply code 421.  This exception may be caught either
2186     *      as an IOException or independently as itself.
2187     * @throws IOException  If an I/O error occurs while either sending a
2188     *      command to the server or receiving a reply from the server.
2189     */
2190    public OutputStream storeUniqueFileStream(final String remote) throws IOException
2191    {
2192        return storeFileStream(FTPCmd.STOU, remote);
2193    }
2194
2195    /**
2196     * Stores a file on the server using a unique name assigned by the
2197     * server and taking input from the given InputStream.  This method does
2198     * NOT close the given
2199     * InputStream.  If the current file type is ASCII, line separators in
2200     * the file are transparently converted to the NETASCII format (i.e.,
2201     * you should not attempt to create a special InputStream to do this).
2202     *
2203     * @param local   The local InputStream from which to read the file.
2204     * @return True if successfully completed, false if not.
2205     * @throws FTPConnectionClosedException
2206     *      If the FTP server prematurely closes the connection as a result
2207     *      of the client being idle or some other reason causing the server
2208     *      to send FTP reply code 421.  This exception may be caught either
2209     *      as an IOException or independently as itself.
2210     * @throws org.apache.commons.net.io.CopyStreamException
2211     *      If an I/O error occurs while actually
2212     *      transferring the file.  The CopyStreamException allows you to
2213     *      determine the number of bytes transferred and the IOException
2214     *      causing the error.  This exception may be caught either
2215     *      as an IOException or independently as itself.
2216     * @throws IOException  If an I/O error occurs while either sending a
2217     *      command to the server or receiving a reply from the server.
2218     */
2219    public boolean storeUniqueFile(final InputStream local) throws IOException
2220    {
2221        return storeFile(FTPCmd.STOU, null, local);
2222    }
2223
2224    /**
2225     * Returns an OutputStream through which data can be written to store
2226     * a file on the server using a unique name assigned by the server.
2227     * If the current file type
2228     * is ASCII, the returned OutputStream will convert line separators in
2229     * the file to the NETASCII format  (i.e., you should not attempt to
2230     * create a special OutputStream to do this).  You must close the
2231     * OutputStream when you finish writing to it.  The OutputStream itself
2232     * will take care of closing the parent data connection socket upon being
2233     * closed.
2234     * <p>
2235     * <b>To finalize the file transfer you must call
2236     * {@link #completePendingCommand  completePendingCommand } and
2237     * check its return value to verify success.</b>
2238     * If this is not done, subsequent commands may behave unexpectedly.
2239     *
2240     * @return An OutputStream through which the remote file can be written.  If
2241     *      the data connection cannot be opened (e.g., the file does not
2242     *      exist), null is returned (in which case you may check the reply
2243     *      code to determine the exact reason for failure).
2244     * @throws FTPConnectionClosedException
2245     *      If the FTP server prematurely closes the connection as a result
2246     *      of the client being idle or some other reason causing the server
2247     *      to send FTP reply code 421.  This exception may be caught either
2248     *      as an IOException or independently as itself.
2249     * @throws IOException  If an I/O error occurs while either sending a
2250     *      command to the server or receiving a reply from the server.
2251     */
2252    public OutputStream storeUniqueFileStream() throws IOException
2253    {
2254        return storeFileStream(FTPCmd.STOU, null);
2255    }
2256
2257    /**
2258     * Reserve a number of bytes on the server for the next file transfer.
2259     *
2260     * @param bytes  The number of bytes which the server should allocate.
2261     * @return True if successfully completed, false if not.
2262     * @throws FTPConnectionClosedException
2263     *      If the FTP server prematurely closes the connection as a result
2264     *      of the client being idle or some other reason causing the server
2265     *      to send FTP reply code 421.  This exception may be caught either
2266     *      as an IOException or independently as itself.
2267     * @throws IOException  If an I/O error occurs while either sending a
2268     *      command to the server or receiving a reply from the server.
2269     */
2270    public boolean allocate(final int bytes) throws IOException
2271    {
2272        return FTPReply.isPositiveCompletion(allo(bytes));
2273    }
2274
2275    /**
2276     * Reserve a number of bytes on the server for the next file transfer.
2277     *
2278     * @param bytes  The number of bytes which the server should allocate.
2279     * @return True if successfully completed, false if not.
2280     * @throws FTPConnectionClosedException
2281     *      If the FTP server prematurely closes the connection as a result
2282     *      of the client being idle or some other reason causing the server
2283     *      to send FTP reply code 421.  This exception may be caught either
2284     *      as an IOException or independently as itself.
2285     * @throws IOException  If an I/O error occurs while either sending a
2286     *      command to the server or receiving a reply from the server.
2287     */
2288    public boolean allocate(final long bytes) throws IOException
2289    {
2290        return FTPReply.isPositiveCompletion(allo(bytes));
2291    }
2292
2293    /**
2294     * Query the server for supported features. The server may reply with a list of server-supported exensions.
2295     * For example, a typical client-server interaction might be (from RFC 2389):
2296     * <pre>
2297        C&gt; feat
2298        S&gt; 211-Extensions supported:
2299        S&gt;  MLST size*;create;modify*;perm;media-type
2300        S&gt;  SIZE
2301        S&gt;  COMPRESSION
2302        S&gt;  MDTM
2303        S&gt; 211 END
2304     * </pre>
2305     * @see <a href="http://www.faqs.org/rfcs/rfc2389.html">http://www.faqs.org/rfcs/rfc2389.html</a>
2306     * @return True if successfully completed, false if not.
2307     * @throws IOException on error
2308     * @since 2.2
2309     */
2310    public boolean features() throws IOException {
2311        return FTPReply.isPositiveCompletion(feat());
2312    }
2313
2314    /**
2315     * Query the server for a supported feature, and returns its values (if any).
2316     * Caches the parsed response to avoid resending the command repeatedly.
2317     * @param feature the feature to check
2318     *
2319     * @return if the feature is present, returns the feature values (empty array if none)
2320     * Returns {@code null} if the feature is not found or the command failed.
2321     * Check {@link #getReplyCode()} or {@link #getReplyString()} if so.
2322     * @throws IOException on error
2323     * @since 3.0
2324     */
2325    public String[] featureValues(final String feature) throws IOException {
2326        if (!initFeatureMap()) {
2327            return null;
2328        }
2329        final Set<String> entries = featuresMap.get(feature.toUpperCase(Locale.ENGLISH));
2330        if (entries != null) {
2331            return entries.toArray(new String[entries.size()]);
2332        }
2333        return null;
2334    }
2335
2336    /**
2337     * Query the server for a supported feature, and returns the its value (if any).
2338     * Caches the parsed response to avoid resending the command repeatedly.
2339     * @param feature the feature to check
2340     *
2341     * @return if the feature is present, returns the feature value or the empty string
2342     * if the feature exists but has no value.
2343     * Returns {@code null} if the feature is not found or the command failed.
2344     * Check {@link #getReplyCode()} or {@link #getReplyString()} if so.
2345     * @throws IOException on error
2346     * @since 3.0
2347     */
2348    public String featureValue(final String feature) throws IOException {
2349        final String [] values = featureValues(feature);
2350        if (values != null) {
2351            return values[0];
2352        }
2353        return null;
2354    }
2355
2356    /**
2357     * Query the server for a supported feature.
2358     * Caches the parsed response to avoid resending the command repeatedly.
2359     *
2360     * @param feature the name of the feature; it is converted to upper case.
2361     * @return {@code true} if the feature is present, {@code false} if the feature is not present
2362     * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link #getReplyString()}
2363     * if it is necessary to distinguish these cases.
2364     *
2365     * @throws IOException on error
2366     * @since 3.0
2367     */
2368    public boolean hasFeature(final String feature) throws IOException {
2369        if (!initFeatureMap()) {
2370            return false;
2371        }
2372        return featuresMap.containsKey(feature.toUpperCase(Locale.ENGLISH));
2373    }
2374
2375    /**
2376     * Query the server for a supported feature with particular value,
2377     * for example "AUTH SSL" or "AUTH TLS".
2378     * Caches the parsed response to avoid resending the command repeatedly.
2379     *
2380     * @param feature the name of the feature; it is converted to upper case.
2381     * @param value the value to find.
2382     *
2383     * @return {@code true} if the feature is present, {@code false} if the feature is not present
2384     * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link #getReplyString()}
2385     * if it is necessary to distinguish these cases.
2386     *
2387     * @throws IOException on error
2388     * @since 3.0
2389     */
2390    public boolean hasFeature(final String feature, final String value) throws IOException {
2391        if (!initFeatureMap()) {
2392            return false;
2393        }
2394        final Set<String> entries = featuresMap.get(feature.toUpperCase(Locale.ENGLISH));
2395        if (entries != null) {
2396            return entries.contains(value);
2397        }
2398        return false;
2399    }
2400
2401    /*
2402     * Create the feature map if not already created.
2403     */
2404    private boolean initFeatureMap() throws IOException {
2405        if (featuresMap == null) {
2406            // Don't create map here, because next line may throw exception
2407            final int replyCode = feat();
2408            if (replyCode == FTPReply.NOT_LOGGED_IN) { // 503
2409                return false; // NET-518; don't create empy map
2410            }
2411            final boolean success = FTPReply.isPositiveCompletion(replyCode);
2412            // we init the map here, so we don't keep trying if we know the command will fail
2413            featuresMap = new HashMap<>();
2414            if (!success) {
2415                return false;
2416            }
2417            for (final String l : getReplyStrings()) {
2418                if (l.startsWith(" ")) { // it's a FEAT entry
2419                    String key;
2420                    String value="";
2421                    final int varsep = l.indexOf(' ', 1);
2422                    if (varsep > 0) {
2423                        key = l.substring(1, varsep);
2424                        value = l.substring(varsep+1);
2425                    } else {
2426                        key = l.substring(1);
2427                    }
2428                    key = key.toUpperCase(Locale.ENGLISH);
2429                    Set<String> entries = featuresMap.get(key);
2430                    if (entries == null) {
2431                        entries = new HashSet<>();
2432                        featuresMap.put(key, entries);
2433                    }
2434                    entries.add(value);
2435                }
2436            }
2437        }
2438        return true;
2439    }
2440
2441    /**
2442     * Reserve space on the server for the next file transfer.
2443     *
2444     * @param bytes  The number of bytes which the server should allocate.
2445     * @param recordSize  The size of a file record.
2446     * @return True if successfully completed, false if not.
2447     * @throws FTPConnectionClosedException
2448     *      If the FTP server prematurely closes the connection as a result
2449     *      of the client being idle or some other reason causing the server
2450     *      to send FTP reply code 421.  This exception may be caught either
2451     *      as an IOException or independently as itself.
2452     * @throws IOException  If an I/O error occurs while either sending a
2453     *      command to the server or receiving a reply from the server.
2454     */
2455    public boolean allocate(final int bytes, final int recordSize) throws IOException
2456    {
2457        return FTPReply.isPositiveCompletion(allo(bytes, recordSize));
2458    }
2459
2460    /**
2461     * Reserve space on the server for the next file transfer.
2462     *
2463     * @param bytes  The number of bytes which the server should allocate.
2464     * @param recordSize  The size of a file record.
2465     * @return True if successfully completed, false if not.
2466     * @throws FTPConnectionClosedException
2467     *      If the FTP server prematurely closes the connection as a result
2468     *      of the client being idle or some other reason causing the server
2469     *      to send FTP reply code 421.  This exception may be caught either
2470     *      as an IOException or independently as itself.
2471     * @throws IOException  If an I/O error occurs while either sending a
2472     *      command to the server or receiving a reply from the server.
2473     */
2474    public boolean allocate(final long bytes, final int recordSize) throws IOException
2475    {
2476        return FTPReply.isPositiveCompletion(allo(bytes, recordSize));
2477    }
2478
2479
2480    /**
2481     * Issue a command and wait for the reply.
2482     * <p>
2483     * Should only be used with commands that return replies on the
2484     * command channel - do not use for LIST, NLST, MLSD etc.
2485     *
2486     * @param command  The command to invoke
2487     * @param params  The parameters string, may be {@code null}
2488     * @return True if successfully completed, false if not, in which case
2489     * call {@link #getReplyCode()} or {@link #getReplyString()}
2490     * to get the reason.
2491     *
2492     * @throws IOException  If an I/O error occurs while either sending a
2493     *      command to the server or receiving a reply from the server.
2494     * @since 3.0
2495     */
2496    public boolean doCommand(final String command, final String params) throws IOException
2497    {
2498        return FTPReply.isPositiveCompletion(sendCommand(command, params));
2499    }
2500
2501    /**
2502     * Issue a command and wait for the reply, returning it as an array of strings.
2503     * <p>
2504     * Should only be used with commands that return replies on the
2505     * command channel - do not use for LIST, NLST, MLSD etc.
2506     *
2507     * @param command  The command to invoke
2508     * @param params  The parameters string, may be {@code null}
2509     * @return The array of replies, or {@code null} if the command failed, in which case
2510     * call {@link #getReplyCode()} or {@link #getReplyString()}
2511     * to get the reason.
2512     *
2513     * @throws IOException  If an I/O error occurs while either sending a
2514     *      command to the server or receiving a reply from the server.
2515     * @since 3.0
2516     */
2517    public String[] doCommandAsStrings(final String command, final String params) throws IOException
2518    {
2519        final boolean success = FTPReply.isPositiveCompletion(sendCommand(command, params));
2520        if (success){
2521            return getReplyStrings();
2522        }
2523        return null;
2524    }
2525
2526    /**
2527     * Get file details using the MLST command
2528     *
2529     * @param pathname the file or directory to list, may be {@code null}
2530     * @return the file details, may be {@code null}
2531     * @throws IOException on error
2532     * @since 3.0
2533     */
2534    public FTPFile mlistFile(final String pathname) throws IOException
2535    {
2536        final boolean success = FTPReply.isPositiveCompletion(sendCommand(FTPCmd.MLST, pathname));
2537        if (success){
2538            String reply = getReplyStrings()[1];
2539            // some FTP server reply not contains space before fact(s)
2540            if(reply.charAt(0) != ' ') { reply = " " + reply; }
2541            /* check the response makes sense.
2542             * Must have space before fact(s) and between fact(s) and file name
2543             * Fact(s) can be absent, so at least 3 chars are needed.
2544             */
2545            if (reply.length() < 3) {
2546                throw new MalformedServerReplyException("Invalid server reply (MLST): '" + reply + "'");
2547            }
2548            // some FTP server reply contains more than one space before fact(s)
2549            final String entry = reply.replaceAll("^\\s+", ""); // skip leading space for parser
2550            return MLSxEntryParser.parseEntry(entry);
2551        }
2552        return null;
2553    }
2554
2555    /**
2556     * Generate a directory listing for the current directory using the MLSD command.
2557     *
2558     * @return the array of file entries
2559     * @throws IOException on error
2560     * @since 3.0
2561     */
2562    public FTPFile[] mlistDir() throws IOException
2563    {
2564        return mlistDir(null);
2565    }
2566
2567    /**
2568     * Generate a directory listing using the MLSD command.
2569     *
2570     * @param pathname the directory name, may be {@code null}
2571     * @return the array of file entries
2572     * @throws IOException on error
2573     * @since 3.0
2574     */
2575    public FTPFile[] mlistDir(final String pathname) throws IOException
2576    {
2577        final FTPListParseEngine engine = initiateMListParsing( pathname);
2578        return engine.getFiles();
2579    }
2580
2581    /**
2582     * Generate a directory listing using the MLSD command.
2583     *
2584     * @param pathname the directory name, may be {@code null}
2585     * @param filter the filter to apply to the responses
2586     * @return the array of file entries
2587     * @throws IOException on error
2588     * @since 3.0
2589     */
2590    public FTPFile[] mlistDir(final String pathname, final FTPFileFilter filter) throws IOException
2591    {
2592        final FTPListParseEngine engine = initiateMListParsing( pathname);
2593        return engine.getFiles(filter);
2594    }
2595
2596    /**
2597     * Restart a <code>STREAM_TRANSFER_MODE</code> file transfer starting
2598     * from the given offset.  This will only work on FTP servers supporting
2599     * the REST comand for the stream transfer mode.  However, most FTP
2600     * servers support this.  Any subsequent file transfer will start
2601     * reading or writing the remote file from the indicated offset.
2602     *
2603     * @param offset  The offset into the remote file at which to start the
2604     *           next file transfer.
2605     * @return True if successfully completed, false if not.
2606     * @throws FTPConnectionClosedException
2607     *      If the FTP server prematurely closes the connection as a result
2608     *      of the client being idle or some other reason causing the server
2609     *      to send FTP reply code 421.  This exception may be caught either
2610     *      as an IOException or independently as itself.
2611     * @throws IOException  If an I/O error occurs while either sending a
2612     *      command to the server or receiving a reply from the server.
2613     * @since 3.1 (changed from private to protected)
2614     */
2615    protected boolean restart(final long offset) throws IOException
2616    {
2617        restartOffset = 0;
2618        return FTPReply.isPositiveIntermediate(rest(Long.toString(offset)));
2619    }
2620
2621    /**
2622     * Sets the restart offset for file transfers.
2623     * <p>
2624     * The restart command is not sent to the server immediately.
2625     * It is sent when a data connection is created as part of a
2626     * subsequent command.
2627     * The restart marker is reset to zero after use.
2628     * </p>
2629     * <p>
2630     * <b>Note: This method should only be invoked immediately prior to
2631     * the transfer to which it applies.</b>
2632     *
2633     * @param offset  The offset into the remote file at which to start the
2634     *           next file transfer.  This must be a value greater than or
2635     *           equal to zero.
2636     */
2637    public void setRestartOffset(final long offset)
2638    {
2639        if (offset >= 0) {
2640            restartOffset = offset;
2641        }
2642    }
2643
2644    /**
2645     * Fetches the restart offset.
2646     *
2647     * @return offset  The offset into the remote file at which to start the
2648     *           next file transfer.
2649     */
2650    public long getRestartOffset()
2651    {
2652        return restartOffset;
2653    }
2654
2655
2656
2657    /**
2658     * Renames a remote file.
2659     *
2660     * @param from  The name of the remote file to rename.
2661     * @param to    The new name of the remote file.
2662     * @return True if successfully completed, false if not.
2663     * @throws FTPConnectionClosedException
2664     *      If the FTP server prematurely closes the connection as a result
2665     *      of the client being idle or some other reason causing the server
2666     *      to send FTP reply code 421.  This exception may be caught either
2667     *      as an IOException or independently as itself.
2668     * @throws IOException  If an I/O error occurs while either sending a
2669     *      command to the server or receiving a reply from the server.
2670     */
2671    public boolean rename(final String from, final String to) throws IOException
2672    {
2673        if (!FTPReply.isPositiveIntermediate(rnfr(from))) {
2674            return false;
2675        }
2676
2677        return FTPReply.isPositiveCompletion(rnto(to));
2678    }
2679
2680
2681    /**
2682     * Abort a transfer in progress.
2683     *
2684     * @return True if successfully completed, false if not.
2685     * @throws FTPConnectionClosedException
2686     *      If the FTP server prematurely closes the connection as a result
2687     *      of the client being idle or some other reason causing the server
2688     *      to send FTP reply code 421.  This exception may be caught either
2689     *      as an IOException or independently as itself.
2690     * @throws IOException  If an I/O error occurs while either sending a
2691     *      command to the server or receiving a reply from the server.
2692     */
2693    public boolean abort() throws IOException
2694    {
2695        return FTPReply.isPositiveCompletion(abor());
2696    }
2697
2698    /**
2699     * Deletes a file on the FTP server.
2700     *
2701     * @param pathname   The pathname of the file to be deleted.
2702     * @return True if successfully completed, false if not.
2703     * @throws FTPConnectionClosedException
2704     *      If the FTP server prematurely closes the connection as a result
2705     *      of the client being idle or some other reason causing the server
2706     *      to send FTP reply code 421.  This exception may be caught either
2707     *      as an IOException or independently as itself.
2708     * @throws IOException  If an I/O error occurs while either sending a
2709     *      command to the server or receiving a reply from the server.
2710     */
2711    public boolean deleteFile(final String pathname) throws IOException
2712    {
2713        return FTPReply.isPositiveCompletion(dele(pathname));
2714    }
2715
2716
2717    /**
2718     * Removes a directory on the FTP server (if empty).
2719     *
2720     * @param pathname  The pathname of the directory to remove.
2721     * @return True if successfully completed, false if not.
2722     * @throws FTPConnectionClosedException
2723     *      If the FTP server prematurely closes the connection as a result
2724     *      of the client being idle or some other reason causing the server
2725     *      to send FTP reply code 421.  This exception may be caught either
2726     *      as an IOException or independently as itself.
2727     * @throws IOException  If an I/O error occurs while either sending a
2728     *      command to the server or receiving a reply from the server.
2729     */
2730    public boolean removeDirectory(final String pathname) throws IOException
2731    {
2732        return FTPReply.isPositiveCompletion(rmd(pathname));
2733    }
2734
2735
2736    /**
2737     * Creates a new subdirectory on the FTP server in the current directory
2738     * (if a relative pathname is given) or where specified (if an absolute
2739     * pathname is given).
2740     *
2741     * @param pathname The pathname of the directory to create.
2742     * @return True if successfully completed, false if not.
2743     * @throws FTPConnectionClosedException
2744     *      If the FTP server prematurely closes the connection as a result
2745     *      of the client being idle or some other reason causing the server
2746     *      to send FTP reply code 421.  This exception may be caught either
2747     *      as an IOException or independently as itself.
2748     * @throws IOException  If an I/O error occurs while either sending a
2749     *      command to the server or receiving a reply from the server.
2750     */
2751    public boolean makeDirectory(final String pathname) throws IOException
2752    {
2753        return FTPReply.isPositiveCompletion(mkd(pathname));
2754    }
2755
2756
2757    /**
2758     * Returns the pathname of the current working directory.
2759     *
2760     * @return The pathname of the current working directory.  If it cannot
2761     *         be obtained, returns null.
2762     * @throws FTPConnectionClosedException
2763     *      If the FTP server prematurely closes the connection as a result
2764     *      of the client being idle or some other reason causing the server
2765     *      to send FTP reply code 421.  This exception may be caught either
2766     *      as an IOException or independently as itself.
2767     * @throws IOException  If an I/O error occurs while either sending a
2768     *      command to the server or receiving a reply from the server.
2769     */
2770    public String printWorkingDirectory() throws IOException
2771    {
2772        if (pwd() != FTPReply.PATHNAME_CREATED) {
2773            return null;
2774        }
2775
2776        return parsePathname(_replyLines.get( _replyLines.size() - 1));
2777    }
2778
2779
2780    /**
2781     * Send a site specific command.
2782     * @param arguments The site specific command and arguments.
2783     * @return True if successfully completed, false if not.
2784     * @throws FTPConnectionClosedException
2785     *      If the FTP server prematurely closes the connection as a result
2786     *      of the client being idle or some other reason causing the server
2787     *      to send FTP reply code 421.  This exception may be caught either
2788     *      as an IOException or independently as itself.
2789     * @throws IOException  If an I/O error occurs while either sending a
2790     *      command to the server or receiving a reply from the server.
2791     */
2792    public boolean sendSiteCommand(final String arguments) throws IOException
2793    {
2794        return FTPReply.isPositiveCompletion(site(arguments));
2795    }
2796
2797
2798    /**
2799     * Fetches the system type from the server and returns the string.
2800     * This value is cached for the duration of the connection after the
2801     * first call to this method.  In other words, only the first time
2802     * that you invoke this method will it issue a SYST command to the
2803     * FTP server.  FTPClient will remember the value and return the
2804     * cached value until a call to disconnect.
2805     * <p>
2806     * If the SYST command fails, and the system property
2807     * {@link #FTP_SYSTEM_TYPE_DEFAULT} is defined, then this is used instead.
2808     * @return The system type obtained from the server. Never null.
2809     * @throws FTPConnectionClosedException
2810     *      If the FTP server prematurely closes the connection as a result
2811     *      of the client being idle or some other reason causing the server
2812     *      to send FTP reply code 421.  This exception may be caught either
2813     *      as an IOException or independently as itself.
2814     * @throws IOException  If an I/O error occurs while either sending a
2815     *  command to the server or receiving a reply from the server (and the default
2816     *  system type property is not defined)
2817     *  @since 2.2
2818     */
2819    public String getSystemType() throws IOException
2820    {
2821        //if (syst() == FTPReply.NAME_SYSTEM_TYPE)
2822        // Technically, we should expect a NAME_SYSTEM_TYPE response, but
2823        // in practice FTP servers deviate, so we soften the condition to
2824        // a positive completion.
2825        if (systemName == null){
2826            if (FTPReply.isPositiveCompletion(syst())) {
2827                // Assume that response is not empty here (cannot be null)
2828                systemName = _replyLines.get(_replyLines.size() - 1).substring(4);
2829            } else {
2830                // Check if the user has provided a default for when the SYST command fails
2831                final String systDefault = System.getProperty(FTP_SYSTEM_TYPE_DEFAULT);
2832                if (systDefault != null) {
2833                    systemName = systDefault;
2834                } else {
2835                    throw new IOException("Unable to determine system type - response: " + getReplyString());
2836                }
2837            }
2838        }
2839        return systemName;
2840    }
2841
2842
2843    /**
2844     * Fetches the system help information from the server and returns the
2845     * full string.
2846     *
2847     * @return The system help string obtained from the server.  null if the
2848     *       information could not be obtained.
2849     * @throws FTPConnectionClosedException
2850     *      If the FTP server prematurely closes the connection as a result
2851     *      of the client being idle or some other reason causing the server
2852     *      to send FTP reply code 421.  This exception may be caught either
2853     *      as an IOException or independently as itself.
2854     * @throws IOException  If an I/O error occurs while either sending a
2855     *  command to the server or receiving a reply from the server.
2856     */
2857    public String listHelp() throws IOException
2858    {
2859        if (FTPReply.isPositiveCompletion(help())) {
2860            return getReplyString();
2861        }
2862        return null;
2863    }
2864
2865
2866    /**
2867     * Fetches the help information for a given command from the server and
2868     * returns the full string.
2869     * @param command The command on which to ask for help.
2870     * @return The command help string obtained from the server.  null if the
2871     *       information could not be obtained.
2872     * @throws FTPConnectionClosedException
2873     *      If the FTP server prematurely closes the connection as a result
2874     *      of the client being idle or some other reason causing the server
2875     *      to send FTP reply code 421.  This exception may be caught either
2876     *      as an IOException or independently as itself.
2877     * @throws IOException  If an I/O error occurs while either sending a
2878     *  command to the server or receiving a reply from the server.
2879     */
2880    public String listHelp(final String command) throws IOException
2881    {
2882        if (FTPReply.isPositiveCompletion(help(command))) {
2883            return getReplyString();
2884        }
2885        return null;
2886    }
2887
2888
2889    /**
2890     * Sends a NOOP command to the FTP server.  This is useful for preventing
2891     * server timeouts.
2892     *
2893     * @return True if successfully completed, false if not.
2894     * @throws FTPConnectionClosedException
2895     *      If the FTP server prematurely closes the connection as a result
2896     *      of the client being idle or some other reason causing the server
2897     *      to send FTP reply code 421.  This exception may be caught either
2898     *      as an IOException or independently as itself.
2899     * @throws IOException  If an I/O error occurs while either sending a
2900     *      command to the server or receiving a reply from the server.
2901     */
2902    public boolean sendNoOp() throws IOException
2903    {
2904        return FTPReply.isPositiveCompletion(noop());
2905    }
2906
2907
2908    /**
2909     * Obtain a list of file names in a directory (or just the name of a given
2910     * file, which is not particularly useful).  This information is obtained
2911     * through the NLST command.  If the given pathname is a directory and
2912     * contains no files,  a zero length array is returned only
2913     * if the FTP server returned a positive completion code, otherwise
2914     * null is returned (the FTP server returned a 550 error No files found.).
2915     * If the directory is not empty, an array of file names in the directory is
2916     * returned. If the pathname corresponds
2917     * to a file, only that file will be listed.  The server may or may not
2918     * expand glob expressions.
2919     *
2920     * @param pathname  The file or directory to list.
2921     *                  Warning: the server may treat a leading '-' as an
2922     *                  option introducer. If so, try using an absolute path,
2923     *                  or prefix the path with ./ (unix style servers).
2924     *                  Some servers may support "--" as meaning end of options,
2925     *                  in which case "-- -xyz" should work.
2926     * @return The list of file names contained in the given path.  null if
2927     *     the list could not be obtained.  If there are no file names in
2928     *     the directory, a zero-length array is returned.
2929     * @throws FTPConnectionClosedException
2930     *      If the FTP server prematurely closes the connection as a result
2931     *      of the client being idle or some other reason causing the server
2932     *      to send FTP reply code 421.  This exception may be caught either
2933     *      as an IOException or independently as itself.
2934     * @throws IOException  If an I/O error occurs while either sending a
2935     *      command to the server or receiving a reply from the server.
2936     */
2937    public String[] listNames(final String pathname) throws IOException
2938    {
2939        final ArrayList<String> results = new ArrayList<>();
2940        try (final Socket socket = _openDataConnection_(FTPCmd.NLST, getListArguments(pathname))) {
2941
2942            if (socket == null) {
2943                return null;
2944            }
2945
2946            try (final BufferedReader reader = new BufferedReader(
2947                    new InputStreamReader(socket.getInputStream(), getControlEncoding()))) {
2948
2949                String line;
2950                while ((line = reader.readLine()) != null) {
2951                    results.add(line);
2952                }
2953            }
2954        }
2955
2956        if (completePendingCommand()) {
2957            final String[] names = new String[results.size()];
2958            return results.toArray(names);
2959        }
2960
2961        return null;
2962    }
2963
2964
2965    /**
2966     * Obtain a list of file names in the current working directory
2967     * This information is obtained through the NLST command.  If the current
2968     * directory contains no files, a zero length array is returned only
2969     * if the FTP server returned a positive completion code, otherwise,
2970     * null is returned (the FTP server returned a 550 error No files found.).
2971     * If the directory is not empty, an array of file names in the directory is
2972     * returned.
2973     *
2974     * @return The list of file names contained in the current working
2975     *     directory.  null if the list could not be obtained.
2976     *     If there are no file names in the directory, a zero-length array
2977     *     is returned.
2978     * @throws FTPConnectionClosedException
2979     *      If the FTP server prematurely closes the connection as a result
2980     *      of the client being idle or some other reason causing the server
2981     *      to send FTP reply code 421.  This exception may be caught either
2982     *      as an IOException or independently as itself.
2983     * @throws IOException  If an I/O error occurs while either sending a
2984     *      command to the server or receiving a reply from the server.
2985     */
2986    public String[] listNames() throws IOException
2987    {
2988        return listNames(null);
2989    }
2990
2991
2992
2993    /**
2994     * Using the default system autodetect mechanism, obtain a
2995     * list of file information for the current working directory
2996     * or for just a single file.
2997     * <p>
2998     * This information is obtained through the LIST command.  The contents of
2999     * the returned array is determined by the<code> FTPFileEntryParser </code>
3000     * used.
3001     * <p>
3002     * N.B. the LIST command does not generally return very precise timestamps.
3003     * For recent files, the response usually contains hours and minutes (not seconds).
3004     * For older files, the output may only contain a date.
3005     * If the server supports it, the MLSD command returns timestamps with a precision
3006     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3007     *
3008     * @param pathname  The file or directory to list.  Since the server may
3009     *                  or may not expand glob expressions, using them here
3010     *                  is not recommended and may well cause this method to
3011     *                  fail.
3012     *                  Also, some servers treat a leading '-' as being an option.
3013     *                  To avoid this interpretation, use an absolute pathname
3014     *                  or prefix the pathname with ./ (unix style servers).
3015     *                  Some servers may support "--" as meaning end of options,
3016     *                  in which case "-- -xyz" should work.
3017     *
3018     * @return The list of file information contained in the given path in
3019     *         the format determined by the autodetection mechanism
3020     * @throws FTPConnectionClosedException
3021     *                   If the FTP server prematurely closes the connection
3022     *                   as a result of the client being idle or some other
3023     *                   reason causing the server to send FTP reply code 421.
3024     *                   This exception may be caught either as an IOException
3025     *                   or independently as itself.
3026     * @throws IOException
3027     *                   If an I/O error occurs while either sending a
3028     *                   command to the server or receiving a reply
3029     *                   from the server.
3030     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3031     *                   Thrown if the parserKey parameter cannot be
3032     *                   resolved by the selected parser factory.
3033     *                   In the DefaultFTPEntryParserFactory, this will
3034     *                   happen when parserKey is neither
3035     *                   the fully qualified class name of a class
3036     *                   implementing the interface
3037     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3038     *                   nor a string containing one of the recognized keys
3039     *                   mapping to such a parser or if class loader
3040     *                   security issues prevent its being loaded.
3041     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3042     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3043     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3044     */
3045    public FTPFile[] listFiles(final String pathname)
3046    throws IOException
3047    {
3048        final FTPListParseEngine engine = initiateListParsing((String) null, pathname);
3049        return engine.getFiles();
3050
3051    }
3052
3053    /**
3054     * Using the default system autodetect mechanism, obtain a
3055     * list of file information for the current working directory.
3056     * <p>
3057     * This information is obtained through the LIST command.  The contents of
3058     * the returned array is determined by the<code> FTPFileEntryParser </code>
3059     * used.
3060     * <p>
3061     * N.B. the LIST command does not generally return very precise timestamps.
3062     * For recent files, the response usually contains hours and minutes (not seconds).
3063     * For older files, the output may only contain a date.
3064     * If the server supports it, the MLSD command returns timestamps with a precision
3065     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3066     *
3067     * @return The list of file information contained in the current directory
3068     *         in the format determined by the autodetection mechanism.
3069     *         <p><b>
3070     *         NOTE:</b> This array may contain null members if any of the
3071     *         individual file listings failed to parse.  The caller should
3072     *         check each entry for null before referencing it.
3073     * @throws FTPConnectionClosedException
3074     *                   If the FTP server prematurely closes the connection
3075     *                   as a result of the client being idle or some other
3076     *                   reason causing the server to send FTP reply code 421.
3077     *                   This exception may be caught either as an IOException
3078     *                   or independently as itself.
3079     * @throws IOException
3080     *                   If an I/O error occurs while either sending a
3081     *                   command to the server or receiving a reply
3082     *                   from the server.
3083     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3084     *                   Thrown if the parserKey parameter cannot be
3085     *                   resolved by the selected parser factory.
3086     *                   In the DefaultFTPEntryParserFactory, this will
3087     *                   happen when parserKey is neither
3088     *                   the fully qualified class name of a class
3089     *                   implementing the interface
3090     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3091     *                   nor a string containing one of the recognized keys
3092     *                   mapping to such a parser or if class loader
3093     *                   security issues prevent its being loaded.
3094     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3095     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3096     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3097     */
3098    public FTPFile[] listFiles()
3099    throws IOException
3100    {
3101        return listFiles((String) null);
3102    }
3103
3104    /**
3105     * Version of {@link #listFiles(String)} which allows a filter to be provided.
3106     * For example: <code>listFiles("site", FTPFileFilters.DIRECTORY);</code>
3107     * @param pathname the initial path, may be null
3108     * @param filter the filter, non-null
3109     * @return the list of FTPFile entries.
3110     * @throws IOException on error
3111     * @since 2.2
3112     */
3113    public FTPFile[] listFiles(final String pathname, final FTPFileFilter filter)
3114    throws IOException
3115    {
3116        final FTPListParseEngine engine = initiateListParsing((String) null, pathname);
3117        return engine.getFiles(filter);
3118
3119    }
3120
3121    /**
3122     * Using the default system autodetect mechanism, obtain a
3123     * list of directories contained in the current working directory.
3124     * <p>
3125     * This information is obtained through the LIST command.  The contents of
3126     * the returned array is determined by the<code> FTPFileEntryParser </code>
3127     * used.
3128     * <p>
3129     * N.B. the LIST command does not generally return very precise timestamps.
3130     * For recent files, the response usually contains hours and minutes (not seconds).
3131     * For older files, the output may only contain a date.
3132     * If the server supports it, the MLSD command returns timestamps with a precision
3133     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3134     *
3135     * @return The list of directories contained in the current directory
3136     *         in the format determined by the autodetection mechanism.
3137     *
3138     * @throws FTPConnectionClosedException
3139     *                   If the FTP server prematurely closes the connection
3140     *                   as a result of the client being idle or some other
3141     *                   reason causing the server to send FTP reply code 421.
3142     *                   This exception may be caught either as an IOException
3143     *                   or independently as itself.
3144     * @throws IOException
3145     *                   If an I/O error occurs while either sending a
3146     *                   command to the server or receiving a reply
3147     *                   from the server.
3148     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3149     *                   Thrown if the parserKey parameter cannot be
3150     *                   resolved by the selected parser factory.
3151     *                   In the DefaultFTPEntryParserFactory, this will
3152     *                   happen when parserKey is neither
3153     *                   the fully qualified class name of a class
3154     *                   implementing the interface
3155     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3156     *                   nor a string containing one of the recognized keys
3157     *                   mapping to such a parser or if class loader
3158     *                   security issues prevent its being loaded.
3159     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3160     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3161     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3162     * @since 3.0
3163     */
3164    public FTPFile[] listDirectories() throws IOException {
3165        return listDirectories((String) null);
3166    }
3167
3168    /**
3169     * Using the default system autodetect mechanism, obtain a
3170     * list of directories contained in the specified directory.
3171     * <p>
3172     * This information is obtained through the LIST command.  The contents of
3173     * the returned array is determined by the<code> FTPFileEntryParser </code>
3174     * used.
3175     * <p>
3176     * N.B. the LIST command does not generally return very precise timestamps.
3177     * For recent files, the response usually contains hours and minutes (not seconds).
3178     * For older files, the output may only contain a date.
3179     * If the server supports it, the MLSD command returns timestamps with a precision
3180     * of seconds, and may include milliseconds. See {@link #mlistDir()}
3181     * @param parent the starting directory
3182     *
3183     * @return The list of directories contained in the specified directory
3184     *         in the format determined by the autodetection mechanism.
3185     *
3186     * @throws FTPConnectionClosedException
3187     *                   If the FTP server prematurely closes the connection
3188     *                   as a result of the client being idle or some other
3189     *                   reason causing the server to send FTP reply code 421.
3190     *                   This exception may be caught either as an IOException
3191     *                   or independently as itself.
3192     * @throws IOException
3193     *                   If an I/O error occurs while either sending a
3194     *                   command to the server or receiving a reply
3195     *                   from the server.
3196     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3197     *                   Thrown if the parserKey parameter cannot be
3198     *                   resolved by the selected parser factory.
3199     *                   In the DefaultFTPEntryParserFactory, this will
3200     *                   happen when parserKey is neither
3201     *                   the fully qualified class name of a class
3202     *                   implementing the interface
3203     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3204     *                   nor a string containing one of the recognized keys
3205     *                   mapping to such a parser or if class loader
3206     *                   security issues prevent its being loaded.
3207     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
3208     * @see org.apache.commons.net.ftp.parser.FTPFileEntryParserFactory
3209     * @see org.apache.commons.net.ftp.FTPFileEntryParser
3210     * @since 3.0
3211     */
3212    public FTPFile[] listDirectories(final String parent) throws IOException {
3213        return listFiles(parent, FTPFileFilters.DIRECTORIES);
3214    }
3215
3216    /**
3217     * Using the default autodetect mechanism, initialize an FTPListParseEngine
3218     * object containing a raw file information for the current working
3219     * directory on the server
3220     * This information is obtained through the LIST command.  This object
3221     * is then capable of being iterated to return a sequence of FTPFile
3222     * objects with information filled in by the
3223     * <code> FTPFileEntryParser </code> used.
3224     * <p>
3225     * This method differs from using the listFiles() methods in that
3226     * expensive FTPFile objects are not created until needed which may be
3227     * an advantage on large lists.
3228     *
3229     * @return A FTPListParseEngine object that holds the raw information and
3230     * is capable of providing parsed FTPFile objects, one for each file
3231     * containing information contained in the given path in the format
3232     * determined by the <code> parser </code> parameter.   Null will be
3233     * returned if a data connection cannot be opened.  If the current working
3234     * directory contains no files, an empty array will be the return.
3235     *
3236     * @throws FTPConnectionClosedException
3237     *                   If the FTP server prematurely closes the connection as a result
3238     *                   of the client being idle or some other reason causing the server
3239     *                   to send FTP reply code 421.  This exception may be caught either
3240     *                   as an IOException or independently as itself.
3241     * @throws IOException
3242     *                   If an I/O error occurs while either sending a
3243     *                   command to the server or receiving a reply from the server.
3244     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3245     *                   Thrown if the autodetect mechanism cannot
3246     *                   resolve the type of system we are connected with.
3247     * @see FTPListParseEngine
3248     */
3249    public FTPListParseEngine initiateListParsing()
3250    throws IOException
3251    {
3252        return initiateListParsing((String) null);
3253    }
3254
3255    /**
3256     * Using the default autodetect mechanism, initialize an FTPListParseEngine
3257     * object containing a raw file information for the supplied directory.
3258     * This information is obtained through the LIST command.  This object
3259     * is then capable of being iterated to return a sequence of FTPFile
3260     * objects with information filled in by the
3261     * <code> FTPFileEntryParser </code> used.
3262     * <p>
3263     * The server may or may not expand glob expressions.  You should avoid
3264     * using glob expressions because the return format for glob listings
3265     * differs from server to server and will likely cause this method to fail.
3266     * <p>
3267     * This method differs from using the listFiles() methods in that
3268     * expensive FTPFile objects are not created until needed which may be
3269     * an advantage on large lists.
3270     *
3271     * <pre>
3272     *    FTPClient f=FTPClient();
3273     *    f.connect(server);
3274     *    f.login(username, password);
3275     *    FTPListParseEngine engine = f.initiateListParsing(directory);
3276     *
3277     *    while (engine.hasNext()) {
3278     *       FTPFile[] files = engine.getNext(25);  // "page size" you want
3279     *       //do whatever you want with these files, display them, etc.
3280     *       //expensive FTPFile objects not created until needed.
3281     *    }
3282     * </pre>
3283     * @param pathname the starting directory
3284     *
3285     * @return A FTPListParseEngine object that holds the raw information and
3286     * is capable of providing parsed FTPFile objects, one for each file
3287     * containing information contained in the given path in the format
3288     * determined by the <code> parser </code> parameter.   Null will be
3289     * returned if a data connection cannot be opened.  If the current working
3290     * directory contains no files, an empty array will be the return.
3291     *
3292     * @throws FTPConnectionClosedException
3293     *                   If the FTP server prematurely closes the connection as a result
3294     *                   of the client being idle or some other reason causing the server
3295     *                   to send FTP reply code 421.  This exception may be caught either
3296     *                   as an IOException or independently as itself.
3297     * @throws IOException
3298     *                   If an I/O error occurs while either sending a
3299     *                   command to the server or receiving a reply from the server.
3300     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3301     *                   Thrown if the autodetect mechanism cannot
3302     *                   resolve the type of system we are connected with.
3303     * @see FTPListParseEngine
3304     */
3305    public FTPListParseEngine initiateListParsing(final String pathname)
3306    throws IOException
3307    {
3308        return initiateListParsing((String) null, pathname);
3309    }
3310
3311    /**
3312     * Using the supplied parser key, initialize an FTPListParseEngine
3313     * object containing a raw file information for the supplied directory.
3314     * This information is obtained through the LIST command.  This object
3315     * is then capable of being iterated to return a sequence of FTPFile
3316     * objects with information filled in by the
3317     * <code> FTPFileEntryParser </code> used.
3318     * <p>
3319     * The server may or may not expand glob expressions.  You should avoid
3320     * using glob expressions because the return format for glob listings
3321     * differs from server to server and will likely cause this method to fail.
3322     * <p>
3323     * This method differs from using the listFiles() methods in that
3324     * expensive FTPFile objects are not created until needed which may be
3325     * an advantage on large lists.
3326     *
3327     * @param parserKey A string representing a designated code or fully-qualified
3328     * class name of an  <code> FTPFileEntryParser </code> that should be
3329     *               used to parse each server file listing.
3330     *               May be {@code null}, in which case the code checks first
3331     *               the system property {@link #FTP_SYSTEM_TYPE}, and if that is
3332     *               not defined the SYST command is used to provide the value.
3333     *               To allow for arbitrary system types, the return from the
3334     *               SYST command is used to look up an alias for the type in the
3335     *               {@link #SYSTEM_TYPE_PROPERTIES} properties file if it is available.
3336     * @param pathname the starting directory
3337     *
3338     * @return A FTPListParseEngine object that holds the raw information and
3339     * is capable of providing parsed FTPFile objects, one for each file
3340     * containing information contained in the given path in the format
3341     * determined by the <code> parser </code> parameter.   Null will be
3342     * returned if a data connection cannot be opened.  If the current working
3343     * directory contains no files, an empty array will be the return.
3344     *
3345     * @throws FTPConnectionClosedException
3346     *                   If the FTP server prematurely closes the connection as a result
3347     *                   of the client being idle or some other reason causing the server
3348     *                   to send FTP reply code 421.  This exception may be caught either
3349     *                   as an IOException or independently as itself.
3350     * @throws IOException
3351     *                   If an I/O error occurs while either sending a
3352     *                   command to the server or receiving a reply from the server.
3353     * @throws org.apache.commons.net.ftp.parser.ParserInitializationException
3354     *                   Thrown if the parserKey parameter cannot be
3355     *                   resolved by the selected parser factory.
3356     *                   In the DefaultFTPEntryParserFactory, this will
3357     *                   happen when parserKey is neither
3358     *                   the fully qualified class name of a class
3359     *                   implementing the interface
3360     *                   org.apache.commons.net.ftp.FTPFileEntryParser
3361     *                   nor a string containing one of the recognized keys
3362     *                   mapping to such a parser or if class loader
3363     *                   security issues prevent its being loaded.
3364     * @see FTPListParseEngine
3365     */
3366    public FTPListParseEngine initiateListParsing(
3367            final String parserKey, final String pathname)
3368    throws IOException
3369    {
3370        createParser(parserKey); // create and cache parser
3371        return initiateListParsing(entryParser, pathname);
3372    }
3373
3374    // package access for test purposes
3375    void createParser(final String parserKey) throws IOException {
3376        // We cache the value to avoid creation of a new object every
3377        // time a file listing is generated.
3378        // Note: we don't check against a null parserKey (NET-544)
3379        if(entryParser == null ||  (parserKey != null && ! entryParserKey.equals(parserKey))) {
3380            if (null != parserKey) {
3381                // if a parser key was supplied in the parameters,
3382                // use that to create the parser
3383                entryParser =
3384                    parserFactory.createFileEntryParser(parserKey);
3385                entryParserKey = parserKey;
3386
3387            } else {
3388                // if no parserKey was supplied, check for a configuration
3389                // in the params, and if it has a non-empty system type, use that.
3390                if (null != configuration && configuration.getServerSystemKey().length() > 0) {
3391                    entryParser =
3392                        parserFactory.createFileEntryParser(configuration);
3393                    entryParserKey = configuration.getServerSystemKey();
3394                } else {
3395                    // if a parserKey hasn't been supplied, and a configuration
3396                    // hasn't been supplied, and the override property is not set
3397                    // then autodetect by calling
3398                    // the SYST command and use that to choose the parser.
3399                    String systemType = System.getProperty(FTP_SYSTEM_TYPE);
3400                    if (systemType == null) {
3401                        systemType = getSystemType(); // cannot be null
3402                        final Properties override = getOverrideProperties();
3403                        if (override != null) {
3404                            final String newType = override.getProperty(systemType);
3405                            if (newType != null) {
3406                                systemType = newType;
3407                            }
3408                        }
3409                    }
3410                    if (null != configuration) { // system type must have been empty above
3411                        entryParser = parserFactory.createFileEntryParser(new FTPClientConfig(systemType, configuration));
3412                    } else {
3413                        entryParser = parserFactory.createFileEntryParser(systemType);
3414                    }
3415                    entryParserKey = systemType;
3416                }
3417            }
3418        }
3419
3420
3421    }
3422
3423    /**
3424     * private method through which all listFiles() and
3425     * initiateListParsing methods pass once a parser is determined.
3426     *
3427     * @throws FTPConnectionClosedException
3428     *                   If the FTP server prematurely closes the connection as a result
3429     *                   of the client being idle or some other reason causing the server
3430     *                   to send FTP reply code 421.  This exception may be caught either
3431     *                   as an IOException or independently as itself.
3432     * @throws IOException
3433     *                   If an I/O error occurs while either sending a
3434     *                   command to the server or receiving a reply from the server.
3435     * @see FTPListParseEngine
3436     */
3437    private FTPListParseEngine initiateListParsing(
3438            final FTPFileEntryParser parser, final String pathname)
3439    throws IOException
3440    {
3441        final Socket socket = _openDataConnection_(FTPCmd.LIST, getListArguments(pathname));
3442
3443        final FTPListParseEngine engine = new FTPListParseEngine(parser, configuration);
3444        if (socket == null)
3445        {
3446            return engine;
3447        }
3448
3449        try {
3450            engine.readServerList(socket.getInputStream(), getControlEncoding());
3451        }
3452        finally {
3453            Util.closeQuietly(socket);
3454        }
3455
3456        completePendingCommand();
3457        return engine;
3458    }
3459
3460    /**
3461     * Initiate list parsing for MLSD listings in the current working directory.
3462     *
3463     * @return the engine
3464     * @throws IOException on error
3465     */
3466    public FTPListParseEngine initiateMListParsing() throws IOException
3467    {
3468        return initiateMListParsing(null);
3469    }
3470
3471    /**
3472     * Initiate list parsing for MLSD listings.
3473     *
3474     * @param pathname the path from where to MLSD.
3475     * @return the engine.
3476     * @throws IOException on error
3477     */
3478    public FTPListParseEngine initiateMListParsing(final String pathname) throws IOException
3479    {
3480        final Socket socket = _openDataConnection_(FTPCmd.MLSD, pathname);
3481        final FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance(), configuration);
3482        if (socket == null)
3483        {
3484            return engine;
3485        }
3486
3487        try {
3488            engine.readServerList(socket.getInputStream(), getControlEncoding());
3489        }
3490        finally {
3491            Util.closeQuietly(socket);
3492            completePendingCommand();
3493        }
3494        return engine;
3495    }
3496
3497    /**
3498     * @param pathname the initial pathname
3499     * @return the adjusted string with "-a" added if necessary
3500     * @since 2.0
3501     */
3502    protected String getListArguments(final String pathname) {
3503        if (getListHiddenFiles())
3504        {
3505            if (pathname != null)
3506            {
3507                final StringBuilder sb = new StringBuilder(pathname.length() + 3);
3508                sb.append("-a ");
3509                sb.append(pathname);
3510                return sb.toString();
3511            }
3512            return "-a";
3513        }
3514
3515        return pathname;
3516    }
3517
3518
3519    /**
3520     * Issue the FTP STAT command to the server.
3521     *
3522     * @return The status information returned by the server.
3523     * @throws FTPConnectionClosedException
3524     *      If the FTP server prematurely closes the connection as a result
3525     *      of the client being idle or some other reason causing the server
3526     *      to send FTP reply code 421.  This exception may be caught either
3527     *      as an IOException or independently as itself.
3528     * @throws IOException  If an I/O error occurs while either sending a
3529     *      command to the server or receiving a reply from the server.
3530     */
3531    public String getStatus() throws IOException
3532    {
3533        if (FTPReply.isPositiveCompletion(stat())) {
3534            return getReplyString();
3535        }
3536        return null;
3537    }
3538
3539
3540    /**
3541     * Issue the FTP STAT command to the server for a given pathname.  This
3542     * should produce a listing of the file or directory.
3543     * @param pathname the file name
3544     *
3545     * @return The status information returned by the server.
3546     * @throws FTPConnectionClosedException
3547     *      If the FTP server prematurely closes the connection as a result
3548     *      of the client being idle or some other reason causing the server
3549     *      to send FTP reply code 421.  This exception may be caught either
3550     *      as an IOException or independently as itself.
3551     * @throws IOException  If an I/O error occurs while either sending a
3552     *      command to the server or receiving a reply from the server.
3553     */
3554    public String getStatus(final String pathname) throws IOException
3555    {
3556        if (FTPReply.isPositiveCompletion(stat(pathname))) {
3557            return getReplyString();
3558        }
3559        return null;
3560    }
3561
3562
3563    /**
3564     * Issue the FTP SIZE command to the server for a given pathname.
3565     * This should produce the size of the file.
3566     *
3567     * @param pathname the file name
3568     *
3569     * @return The size information returned by the server; {@code null} if there was an error
3570     * @throws FTPConnectionClosedException
3571     *      If the FTP server prematurely closes the connection as a result
3572     *      of the client being idle or some other reason causing the server
3573     *      to send FTP reply code 421.  This exception may be caught either
3574     *      as an IOException or independently as itself.
3575     * @throws IOException  If an I/O error occurs while either sending a
3576     *      command to the server or receiving a reply from the server.
3577     * @since 3.7
3578     */
3579    public String getSize(final String pathname) throws IOException
3580    {
3581        if (FTPReply.isPositiveCompletion(size(pathname))) {
3582            return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space
3583        }
3584        return null;
3585    }
3586
3587
3588    /**
3589     * Issue the FTP MDTM command (not supported by all servers) to retrieve the last
3590     * modification time of a file. The modification string should be in the
3591     * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in
3592     * GMT, but not all FTP servers honour this.
3593     *
3594     * @param pathname The file path to query.
3595     * @return A string representing the last file modification time in <code>YYYYMMDDhhmmss</code> format.
3596     * @throws IOException if an I/O error occurs.
3597     * @since 2.0
3598     */
3599    public String getModificationTime(final String pathname) throws IOException {
3600        if (FTPReply.isPositiveCompletion(mdtm(pathname))) {
3601            return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space
3602        }
3603        return null;
3604    }
3605
3606
3607    /**
3608     * Issue the FTP MDTM command (not supported by all servers) to retrieve the last
3609     * modification time of a file. The modification string should be in the
3610     * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in
3611     * GMT, but not all FTP servers honour this.
3612     *
3613     * @param pathname The file path to query.
3614     * @return A FTPFile representing the last file modification time, may be {@code null}.
3615     * The FTPFile timestamp will be null if a parse error occurs.
3616     * @throws IOException if an I/O error occurs.
3617     * @since 3.4
3618     */
3619    public FTPFile mdtmFile(final String pathname) throws IOException {
3620        if (FTPReply.isPositiveCompletion(mdtm(pathname))) {
3621            final String reply = getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space
3622            final FTPFile file = new FTPFile();
3623            file.setName(pathname);
3624            file.setRawListing(reply);
3625            file.setTimestamp(MLSxEntryParser.parseGMTdateTime(reply));
3626            return file;
3627        }
3628        return null;
3629    }
3630
3631
3632    /**
3633     * Issue the FTP MFMT command (not supported by all servers) which sets the last
3634     * modified time of a file.
3635     *
3636     * The timestamp should be in the form <code>YYYYMMDDhhmmss</code>. It should also
3637     * be in GMT, but not all servers honour this.
3638     *
3639     * An FTP server would indicate its support of this feature by including "MFMT"
3640     * in its response to the FEAT command, which may be retrieved by FTPClient.features()
3641     *
3642     * @param pathname The file path for which last modified time is to be changed.
3643     * @param timeval The timestamp to set to, in <code>YYYYMMDDhhmmss</code> format.
3644     * @return true if successfully set, false if not
3645     * @throws IOException if an I/O error occurs.
3646     * @since 2.2
3647     * @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a>
3648     */
3649    public boolean setModificationTime(final String pathname, final String timeval) throws IOException {
3650        return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval)));
3651    }
3652
3653
3654    /**
3655     * Set the internal buffer size for buffered data streams.
3656     *
3657     * @param bufSize The size of the buffer. Use a non-positive value to use the default.
3658     */
3659    public void setBufferSize(final int bufSize) {
3660        bufferSize = bufSize;
3661    }
3662
3663    /**
3664     * Retrieve the current internal buffer size for buffered data streams.
3665     * @return The current buffer size.
3666     */
3667    public int getBufferSize() {
3668        return bufferSize;
3669    }
3670
3671    /**
3672     * Sets the value to be used for the data socket SO_SNDBUF option.
3673     * If the value is positive, the option will be set when the data socket has been created.
3674     *
3675     * @param bufSize The size of the buffer, zero or negative means the value is ignored.
3676      * @since 3.3
3677    */
3678    public void setSendDataSocketBufferSize(final int bufSize) {
3679        sendDataSocketBufferSize = bufSize;
3680    }
3681
3682    /**
3683     * Retrieve the value to be used for the data socket SO_SNDBUF option.
3684     * @return The current buffer size.
3685     * @since 3.3
3686     */
3687    public int getSendDataSocketBufferSize() {
3688        return sendDataSocketBufferSize;
3689    }
3690
3691    /**
3692     * Sets the value to be used for the data socket SO_RCVBUF option.
3693     * If the value is positive, the option will be set when the data socket has been created.
3694     *
3695     * @param bufSize The size of the buffer, zero or negative means the value is ignored.
3696     * @since 3.3
3697     */
3698    public void setReceieveDataSocketBufferSize(final int bufSize) {
3699        receiveDataSocketBufferSize = bufSize;
3700    }
3701
3702    /**
3703     * Retrieve the value to be used for the data socket SO_RCVBUF option.
3704     * @return The current buffer size.
3705     * @since 3.3
3706     */
3707    public int getReceiveDataSocketBufferSize() {
3708        return receiveDataSocketBufferSize;
3709    }
3710
3711    /**
3712     * Implementation of the {@link Configurable Configurable} interface.
3713     * In the case of this class, configuring merely makes the config object available for the
3714     * factory methods that construct parsers.
3715     * @param config {@link FTPClientConfig FTPClientConfig} object used to
3716     * provide non-standard configurations to the parser.
3717     * @since 1.4
3718     */
3719    @Override
3720    public void configure(final FTPClientConfig config) {
3721        this.configuration = config;
3722    }
3723
3724    /**
3725     * You can set this to true if you would like to get hidden files when {@link #listFiles} too.
3726     * A <code>LIST -a</code> will be issued to the ftp server.
3727     * It depends on your ftp server if you need to call this method, also dont expect to get rid
3728     * of hidden files if you call this method with "false".
3729     *
3730     * @param listHiddenFiles true if hidden files should be listed
3731     * @since 2.0
3732     */
3733    public void setListHiddenFiles(final boolean listHiddenFiles) {
3734        this.listHiddenFiles = listHiddenFiles;
3735    }
3736
3737    /**
3738     * @see #setListHiddenFiles(boolean)
3739     * @return the current state
3740     * @since 2.0
3741     */
3742    public boolean getListHiddenFiles() {
3743        return this.listHiddenFiles;
3744    }
3745
3746    /**
3747     * Whether should attempt to use EPSV with IPv4.
3748     * Default (if not set) is <code>false</code>
3749     * @return true if should attempt EPSV
3750     * @since 2.2
3751     */
3752    public boolean isUseEPSVwithIPv4() {
3753        return useEPSVwithIPv4;
3754    }
3755
3756
3757    /**
3758     * Set whether to use EPSV with IPv4.
3759     * Might be worth enabling in some circumstances.
3760     *
3761     * For example, when using IPv4 with NAT it
3762     * may work with some rare configurations.
3763     * E.g. if FTP server has a static PASV address (external network)
3764     * and the client is coming from another internal network.
3765     * In that case the data connection after PASV command would fail,
3766     * while EPSV would make the client succeed by taking just the port.
3767     *
3768     * @param selected value to set.
3769     * @since 2.2
3770     */
3771    public void setUseEPSVwithIPv4(final boolean selected) {
3772        this.useEPSVwithIPv4 = selected;
3773    }
3774
3775    /**
3776     * Set the listener to be used when performing store/retrieve operations.
3777     * The default value (if not set) is {@code null}.
3778     *
3779     * @param listener to be used, may be {@code null} to disable
3780     * @since 3.0
3781     */
3782    public void setCopyStreamListener(final CopyStreamListener listener){
3783        copyStreamListener = listener;
3784    }
3785
3786    /**
3787     * Obtain the currently active listener.
3788     *
3789     * @return the listener, may be {@code null}
3790     * @since 3.0
3791     */
3792    public CopyStreamListener getCopyStreamListener(){
3793        return copyStreamListener;
3794    }
3795
3796    /**
3797     * Set the time to wait between sending control connection keepalive messages
3798     * when processing file upload or download.
3799     * <p>
3800     * See the class Javadoc section "Control channel keep-alive feature:"
3801     *
3802     * @param controlIdle the wait (in secs) between keepalive messages. Zero (or less) disables.
3803     * @since 3.0
3804     * @see #setControlKeepAliveReplyTimeout(int)
3805     */
3806    public void setControlKeepAliveTimeout(final long controlIdle){
3807        controlKeepAliveTimeout = controlIdle * 1000;
3808    }
3809
3810    /**
3811     * Get the time to wait between sending control connection keepalive messages
3812     * when processing file upload or download.
3813     * <p>
3814     * See the class Javadoc section "Control channel keep-alive feature:"
3815     *
3816     * @return the number of seconds between keepalive messages.
3817     * @since 3.0
3818     */
3819    public long getControlKeepAliveTimeout() {
3820        return controlKeepAliveTimeout / 1000;
3821    }
3822
3823    /**
3824     * Get the CSL debug array.
3825     * <p>
3826     * <b>For debug use only</b>
3827     * <p>
3828     * Currently contains:
3829     * <ul>
3830     * <li>successfully acked NOOPs at end of transfer</li>
3831     * <li>unanswered NOOPs at end of transfer</li>
3832     * <li>unanswered NOOPs after fetching additional replies</li>
3833     * <li>Number of IOErrors ignored</li>
3834     * </ul>
3835     * @return the debug array
3836     * @deprecated 3.7 For testing only; may be dropped or changed at any time
3837     */
3838    @Deprecated // only for use in testing
3839    public int[] getCslDebug() {
3840        return cslDebug;
3841    }
3842    /**
3843     * Set how long to wait for control keep-alive message replies.
3844     *
3845     * @param timeout number of milliseconds to wait (defaults to 1000)
3846     * @since 3.0
3847     * @see #setControlKeepAliveTimeout(long)
3848     */
3849    public void setControlKeepAliveReplyTimeout(final int timeout) {
3850        controlKeepAliveReplyTimeout = timeout;
3851    }
3852
3853    /**
3854     * Get how long to wait for control keep-alive message replies.
3855     * @return wait time in msec
3856     * @since 3.0
3857     */
3858    public int getControlKeepAliveReplyTimeout() {
3859        return controlKeepAliveReplyTimeout;
3860    }
3861
3862    /**
3863     * Enable or disable passive mode NAT workaround.
3864     * If enabled, a site-local PASV mode reply address will be replaced with the
3865     * remote host address to which the PASV mode request was sent
3866     * (unless that is also a site local address).
3867     * This gets around the problem that some NAT boxes may change the
3868     * reply.
3869     *
3870     * The default is true, i.e. site-local replies are replaced.
3871     * @param enabled true to enable replacing internal IP's in passive
3872     * mode.
3873     * @deprecated (3.6) use {@link #setPassiveNatWorkaroundStrategy(HostnameResolver)} instead
3874     */
3875    @Deprecated
3876    public void setPassiveNatWorkaround(final boolean enabled) {
3877        if (enabled) {
3878            this.passiveNatWorkaroundStrategy = new NatServerResolverImpl(this);
3879        } else {
3880            this.passiveNatWorkaroundStrategy = null;
3881        }
3882    }
3883
3884    /**
3885     * Set the workaround strategy to replace the PASV mode reply addresses.
3886     * This gets around the problem that some NAT boxes may change the reply.
3887     *
3888     * The default implementation is {@code NatServerResolverImpl}, i.e. site-local
3889     * replies are replaced.
3890     * @param resolver strategy to replace internal IP's in passive mode
3891     * or null to disable the workaround (i.e. use PASV mode reply address.)
3892     * @since 3.6
3893     */
3894    public void setPassiveNatWorkaroundStrategy(final HostnameResolver resolver) {
3895        this.passiveNatWorkaroundStrategy = resolver;
3896    }
3897
3898    /**
3899     * Strategy interface for updating host names received from FTP server
3900     * for passive NAT workaround.
3901     *
3902     * @since 3.6
3903     */
3904    public interface HostnameResolver {
3905        String resolve(String hostname) throws UnknownHostException;
3906    }
3907
3908    /**
3909     * Default strategy for passive NAT workaround (site-local
3910     * replies are replaced.)
3911     * @since 3.6
3912     */
3913    public static class NatServerResolverImpl implements HostnameResolver {
3914        private final FTPClient client;
3915
3916        public NatServerResolverImpl(final FTPClient client) {
3917            this.client = client;
3918        }
3919
3920        @Override
3921        public String resolve(final String hostname) throws UnknownHostException {
3922            String newHostname = hostname;
3923            final InetAddress host = InetAddress.getByName(newHostname);
3924            // reply is a local address, but target is not - assume NAT box changed the PASV reply
3925            if (host.isSiteLocalAddress()) {
3926                final InetAddress remote = this.client.getRemoteAddress();
3927                if (!remote.isSiteLocalAddress()){
3928                    newHostname = remote.getHostAddress();
3929                }
3930            }
3931            return newHostname;
3932        }
3933    }
3934
3935    private OutputStream getBufferedOutputStream(final OutputStream outputStream) {
3936        if (bufferSize > 0) {
3937            return new BufferedOutputStream(outputStream, bufferSize);
3938        }
3939        return new BufferedOutputStream(outputStream);
3940    }
3941
3942    private InputStream getBufferedInputStream(final InputStream inputStream) {
3943        if (bufferSize > 0) {
3944            return new BufferedInputStream(inputStream, bufferSize);
3945        }
3946        return new BufferedInputStream(inputStream);
3947    }
3948
3949    // @since 3.0
3950    private static class CSL implements CopyStreamListener {
3951
3952        private final FTPClient parent;
3953        private final long idle;
3954        private final int currentSoTimeout;
3955
3956        private long time = System.currentTimeMillis();
3957        private int notAcked;
3958        private int acksAcked;
3959        private int ioErrors;
3960
3961        CSL(final FTPClient parent, final long idleTime, final int maxWait) throws SocketException {
3962            this.idle = idleTime;
3963            this.parent = parent;
3964            this.currentSoTimeout = parent.getSoTimeout();
3965            parent.setSoTimeout(maxWait);
3966        }
3967
3968        @Override
3969        public void bytesTransferred(final CopyStreamEvent event) {
3970            bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
3971        }
3972
3973        @Override
3974        public void bytesTransferred(final long totalBytesTransferred,
3975                final int bytesTransferred, final long streamSize) {
3976            final long now = System.currentTimeMillis();
3977            if (now - time > idle) {
3978                try {
3979                    parent.__noop();
3980                    acksAcked++;
3981                } catch (final SocketTimeoutException e) {
3982                    notAcked++;
3983                } catch (final IOException e) {
3984                    ioErrors++;
3985                    // Ignored
3986                }
3987                time = now;
3988            }
3989        }
3990
3991        int[] cleanUp() throws IOException {
3992            final int remain = notAcked;
3993            try {
3994                while(notAcked > 0) {
3995                    parent.getReply(); // we do want to see these
3996                    notAcked--; // only decrement if actually received
3997                }
3998            } catch (final SocketTimeoutException e) { // NET-584
3999                // ignored
4000            } finally {
4001                parent.setSoTimeout(currentSoTimeout);
4002            }
4003            return new int [] {acksAcked, remain, notAcked, ioErrors}; // debug counts
4004        }
4005
4006    }
4007
4008    /**
4009     * Merge two copystream listeners, either or both of which may be null.
4010     *
4011     * @param local the listener used by this class, may be null
4012     * @return a merged listener or a single listener or null
4013     * @since 3.0
4014     */
4015    private CopyStreamListener mergeListeners(final CopyStreamListener local) {
4016        if (local == null) {
4017            return copyStreamListener;
4018        }
4019        if (copyStreamListener == null) {
4020            return local;
4021        }
4022        // Both are non-null
4023        final CopyStreamAdapter merged = new CopyStreamAdapter();
4024        merged.addCopyStreamListener(local);
4025        merged.addCopyStreamListener(copyStreamListener);
4026        return merged;
4027    }
4028
4029    /**
4030     * Enables or disables automatic server encoding detection (only UTF-8 supported).
4031     * <p>
4032     * Does not affect existing connections; must be invoked before a connection is established.
4033     *
4034     * @param autodetect If true, automatic server encoding detection will be enabled.
4035     */
4036    public void setAutodetectUTF8(final boolean autodetect)
4037    {
4038        autodetectEncoding = autodetect;
4039    }
4040
4041    /**
4042     * Tells if automatic server encoding detection is enabled or disabled.
4043     * @return true, if automatic server encoding detection is enabled.
4044     */
4045    public boolean getAutodetectUTF8()
4046    {
4047        return autodetectEncoding;
4048    }
4049
4050    // Method for use by unit test code only
4051    FTPFileEntryParser getEntryParser() {
4052        return entryParser;
4053    }
4054
4055    // DEPRECATED METHODS - for API compatibility only - DO NOT USE
4056
4057    /**
4058     * @return the name
4059     * @throws IOException on error
4060     * @deprecated use {@link #getSystemType()} instead
4061     */
4062    @Deprecated
4063    public String getSystemName() throws IOException
4064    {
4065        if (systemName == null && FTPReply.isPositiveCompletion(syst())) {
4066            systemName = _replyLines.get(_replyLines.size() - 1).substring(4);
4067        }
4068        return systemName;
4069    }
4070}
4071
4072/* Emacs configuration
4073 * Local variables:        **
4074 * mode:             java  **
4075 * c-basic-offset:   4     **
4076 * indent-tabs-mode: nil   **
4077 * End:                    **
4078 */
4079/* kate: indent-width 4; replace-tabs on; */