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