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