All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.lib.net.ftp.FTPClient Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.feilong.lib.net.ftp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Properties;
import java.util.Random;
import java.util.Set;

import com.feilong.lib.net.MalformedServerReplyException;
import com.feilong.lib.net.ftp.parser.DefaultFTPFileEntryParserFactory;
import com.feilong.lib.net.ftp.parser.FTPFileEntryParserFactory;
import com.feilong.lib.net.ftp.parser.MLSxEntryParser;
import com.feilong.lib.net.io.CRLFLineReader;
import com.feilong.lib.net.io.CopyStreamAdapter;
import com.feilong.lib.net.io.CopyStreamEvent;
import com.feilong.lib.net.io.CopyStreamListener;
import com.feilong.lib.net.io.FromNetASCIIInputStream;
import com.feilong.lib.net.io.ToNetASCIIOutputStream;
import com.feilong.lib.net.io.Util;

/**
 * FTPClient encapsulates all the functionality necessary to store and
 * retrieve files from an FTP server. This class takes care of all
 * low level details of interacting with an FTP server and provides
 * a convenient higher level interface. As with all classes derived
 * from {@link com.feilong.lib.net.SocketClient},
 * you must first connect to the server with
 * {@link com.feilong.lib.net.SocketClient#connect connect }
 * before doing anything, and finally
 * {@link com.feilong.lib.net.SocketClient#disconnect disconnect }
 * after you're completely finished interacting with the server.
 * Then you need to check the FTP reply code to see if the connection
 * was successful. For example:
 * 
 * 
 *    FTPClient ftp = new FTPClient();
 *    FTPClientConfig config = new FTPClientConfig();
 *    config.setXXX(YYY); // change required options
 *    // for example config.setServerTimeZoneId("Pacific/Pitcairn")
 *    ftp.configure(config );
 *    boolean error = false;
 *    try {
 *      int reply;
 *      String server = "ftp.example.com";
 *      ftp.connect(server);
 *      System.out.println("Connected to " + server + ".");
 *      System.out.print(ftp.getReplyString());
 *
 *      // After connection attempt, you should check the reply code to verify
 *      // success.
 *      reply = ftp.getReplyCode();
 *
 *      if(!FTPReply.isPositiveCompletion(reply)) {
 *        ftp.disconnect();
 *        System.err.println("FTP server refused connection.");
 *        System.exit(1);
 *      }
 *      ... // transfer files
 *      ftp.logout();
 *    } catch(IOException e) {
 *      error = true;
 *      e.printStackTrace();
 *    } finally {
 *      if(ftp.isConnected()) {
 *        try {
 *          ftp.disconnect();
 *        } catch(IOException ioe) {
 *          // do nothing
 *        }
 *      }
 *      System.exit(error ? 1 : 0);
 *    }
 * 
*

* Immediately after connecting is the only real time you need to check the * reply code (because connect is of type void). The convention for all the * FTP command methods in FTPClient is such that they either return a * boolean value or some other value. * The boolean methods return true on a successful completion reply from * the FTP server and false on a reply resulting in an error condition or * failure. The methods returning a value other than boolean return a value * containing the higher level data produced by the FTP command, or null if a * reply resulted in an error condition or failure. If you want to access * the exact FTP reply code causing a success or failure, you must call * {@link com.feilong.lib.net.ftp.FTP#getReplyCode getReplyCode } after * a success or failure. *

* The default settings for FTPClient are for it to use * FTP.ASCII_FILE_TYPE , * FTP.NON_PRINT_TEXT_FORMAT , * FTP.STREAM_TRANSFER_MODE , and * FTP.FILE_STRUCTURE . The only file types directly supported * are FTP.ASCII_FILE_TYPE and * FTP.BINARY_FILE_TYPE . Because there are at least 4 * different EBCDIC encodings, we have opted not to provide direct support * for EBCDIC. To transfer EBCDIC and other unsupported file types you * must create your own filter InputStreams and OutputStreams and wrap * them around the streams returned or required by the FTPClient methods. * FTPClient uses the {@link ToNetASCIIOutputStream NetASCII} * filter streams to provide transparent handling of ASCII files. We will * consider incorporating EBCDIC support if there is enough demand. *

* FTP.NON_PRINT_TEXT_FORMAT , * FTP.STREAM_TRANSFER_MODE , and * FTP.FILE_STRUCTURE are the only supported formats, * transfer modes, and file structures. *

* Because the handling of sockets on different platforms can differ * significantly, the FTPClient automatically issues a new PORT (or EPRT) command * prior to every transfer requiring that the server connect to the client's * data port. This ensures identical problem-free behavior on Windows, Unix, * and Macintosh platforms. Additionally, it relieves programmers from * having to issue the PORT (or EPRT) command themselves and dealing with platform * dependent issues. *

* Additionally, for security purposes, all data connections to the * client are verified to ensure that they originated from the intended * party (host and port). If a data connection is initiated by an unexpected * party, the command will close the socket and throw an IOException. You * may disable this behavior with * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}. *

* You should keep in mind that the FTP server may choose to prematurely * close a connection if the client has been idle for longer than a * given time period (usually 900 seconds). The FTPClient class will detect a * premature FTP server connection closing when it receives a * {@link com.feilong.lib.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } * response to a command. * When that occurs, the FTP class method encountering that reply will throw * an {@link com.feilong.lib.net.ftp.FTPConnectionClosedException} * . * FTPConnectionClosedException * is a subclass of IOException and therefore need not be * caught separately, but if you are going to catch it separately, its * catch block must appear before the more general IOException * catch block. When you encounter an * {@link com.feilong.lib.net.ftp.FTPConnectionClosedException} * , you must disconnect the connection with * {@link #disconnect disconnect() } to properly clean up the * system resources used by FTPClient. Before disconnecting, you may check the * last reply code and text with * {@link com.feilong.lib.net.ftp.FTP#getReplyCode getReplyCode }, * {@link com.feilong.lib.net.ftp.FTP#getReplyString getReplyString }, * and * {@link com.feilong.lib.net.ftp.FTP#getReplyStrings getReplyStrings}. * You may avoid server disconnections while the client is idle by * periodically sending NOOP commands to the server. *

* Rather than list it separately for each method, we mention here that * every method communicating with the server and throwing an IOException * can also throw a * {@link com.feilong.lib.net.MalformedServerReplyException} * , which is a subclass * of IOException. A MalformedServerReplyException will be thrown when * the reply received from the server deviates enough from the protocol * specification that it cannot be interpreted in a useful manner despite * attempts to be as lenient as possible. *

* Listing API Examples * Both paged and unpaged examples of directory listings are available, * as follows: *

* Unpaged (whole list) access, using a parser accessible by auto-detect: * *

 * FTPClient f = new FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPFile[] files = f.listFiles(directory);
 * 
*

* Paged access, using a parser not accessible by auto-detect. The class * defined in the first parameter of initateListParsing should be derived * from org.apache.commons.net.FTPFileEntryParser: * *

 * FTPClient f = new FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPListParseEngine engine = f.initiateListParsing("com.whatever.YourOwnParser", directory);
 *
 * while (engine.hasNext()){
 *     FTPFile[] files = engine.getNext(25); // "page size" you want
 *     //do whatever you want with these files, display them, etc.
 *     //expensive FTPFile objects not created until needed.
 * }
 * 
*

* Paged access, using a parser accessible by auto-detect: * *

 * FTPClient f = new FTPClient();
 * f.connect(server);
 * f.login(username, password);
 * FTPListParseEngine engine = f.initiateListParsing(directory);
 *
 * while (engine.hasNext()){
 *     FTPFile[] files = engine.getNext(25); // "page size" you want
 *     //do whatever you want with these files, display them, etc.
 *     //expensive FTPFile objects not created until needed.
 * }
 * 
*

* For examples of using FTPClient on servers whose directory listings *

    *
  • use languages other than English
  • *
  • use date formats other than the American English "standard" MM d yyyy
  • *
  • are in different timezones and you need accurate timestamps for dependency checking * as in Ant
  • *
* see {@link FTPClientConfig FTPClientConfig}. *

* Control channel keep-alive feature: *

* Please note: this does not apply to the methods where the user is responsible for writing or reading * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} * and the other xxxFileStream methods *

* During file transfers, the data connection is busy, but the control connection is idle. * FTP servers know that the control connection is in use, so won't close it through lack of activity, * but it's a lot harder for network routers to know that the control and data connections are associated * with each other. * Some routers may treat the control connection as idle, and disconnect it if the transfer over the data * connection takes longer than the allowable idle time for the router. *
* One solution to this is to send a safe command (i.e. NOOP) over the control connection to reset the router's * idle timer. This is enabled as follows: * *

 * ftpClient.setControlKeepAliveTimeout(300); // set timeout to 5 minutes
 * 
* * This will cause the file upload/download methods to send a NOOP approximately every 5 minutes. * The following public methods support this: *
    *
  • {@link #retrieveFile(String, OutputStream)}
  • *
  • {@link #appendFile(String, InputStream)}
  • *
  • {@link #storeFile(String, InputStream)}
  • *
  • {@link #storeUniqueFile(InputStream)}
  • *
  • {@link #storeUniqueFileStream(String)}
  • *
* This feature does not apply to the methods where the user is responsible for writing or reading * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} * and the other xxxFileStream methods. * In such cases, the user is responsible for keeping the control connection alive if necessary. *

* The implementation currently uses a {@link CopyStreamListener} which is passed to the * {@link Util#copyStream(InputStream, OutputStream, int, long, CopyStreamListener, boolean)} * method, so the timing is partially dependent on how long each block transfer takes. * * @see #FTP_SYSTEM_TYPE * @see #SYSTEM_TYPE_PROPERTIES * @see FTP * @see FTPConnectionClosedException * @see FTPFileEntryParser * @see FTPFileEntryParserFactory * @see DefaultFTPFileEntryParserFactory * @see FTPClientConfig * * @see com.feilong.lib.net.MalformedServerReplyException */ public class FTPClient extends FTP implements Configurable{ /** * The system property ({@value}) which can be used to override the system type.
* If defined, the value will be used to create any automatically created parsers. * * @since 3.0 */ public static final String FTP_SYSTEM_TYPE = "org.apache.commons.net.ftp.systemType"; /** * The system property ({@value}) which can be used as the default system type.
* If defined, the value will be used if the SYST command fails. * * @since 3.1 */ public static final String FTP_SYSTEM_TYPE_DEFAULT = "org.apache.commons.net.ftp.systemType.default"; /** * The name of an optional systemType properties file ({@value}), which is loaded * using {@link Class#getResourceAsStream(String)}.
* The entries are the systemType (as determined by {@link FTPClient#getSystemType}) * and the values are the replacement type or parserClass, which is passed to * {@link FTPFileEntryParserFactory#createFileEntryParser(String)}.
* For example: * *

     * Plan 9=Unix
     * OS410=org.apache.commons.net.ftp.parser.OS400FTPEntryParser
     * 
* * @since 3.0 */ public static final String SYSTEM_TYPE_PROPERTIES = "/systemType.properties"; /** * A constant indicating the FTP session is expecting all transfers * to occur between the client (local) and server and that the server * should connect to the client's data port to initiate a data transfer. * This is the default data connection mode when and FTPClient instance * is created. */ public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0; /** * A constant indicating the FTP session is expecting all transfers * to occur between two remote servers and that the server * the client is connected to should connect to the other server's * data port to initiate a data transfer. */ public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1; /** * A constant indicating the FTP session is expecting all transfers * to occur between the client (local) and server and that the server * is in passive mode, requiring the client to connect to the * server's data port to initiate a transfer. */ public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2; /** * A constant indicating the FTP session is expecting all transfers * to occur between two remote servers and that the server * the client is connected to is in passive mode, requiring the other * server to connect to the first server's data port to initiate a data * transfer. */ public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3; private int __dataConnectionMode; private int __dataTimeout; private int __passivePort; private String __passiveHost; private final Random __random; private int __activeMinPort; private int __activeMaxPort; private InetAddress __activeExternalHost; private InetAddress __reportActiveExternalHost; // overrides __activeExternalHost in EPRT/PORT commands /** The address to bind to on passive connections, if necessary. */ private InetAddress __passiveLocalHost; private int __fileType; @SuppressWarnings("unused") // fields are written, but currently not read private int __fileFormat; @SuppressWarnings("unused") // field is written, but currently not read private int __fileStructure; @SuppressWarnings("unused") // field is written, but currently not read private int __fileTransferMode; private boolean __remoteVerificationEnabled; private long __restartOffset; private FTPFileEntryParserFactory __parserFactory; private int __bufferSize; // buffersize for buffered data streams private int __sendDataSocketBufferSize; private int __receiveDataSocketBufferSize; private boolean __listHiddenFiles; private boolean __useEPSVwithIPv4; // whether to attempt EPSV with an IPv4 connection // __systemName is a cached value that should not be referenced directly // except when assigned in getSystemName and __initDefaults. private String __systemName; // __entryParser is a cached value that should not be referenced directly // except when assigned in listFiles(String, String) and __initDefaults. private FTPFileEntryParser __entryParser; // Key used to create the parser; necessary to ensure that the parser type is not ignored private String __entryParserKey; private FTPClientConfig __configuration; // Listener used by store/retrieve methods to handle keepalive private CopyStreamListener __copyStreamListener; // How long to wait before sending another control keep-alive message private long __controlKeepAliveTimeout; // How long to wait (ms) for keepalive message replies before continuing // Most FTP servers don't seem to support concurrent control and data connection usage private int __controlKeepAliveReplyTimeout = 1000; /** * Enable or disable replacement of internal IP in passive mode. Default enabled * using {code NatServerResolverImpl}. */ private HostnameResolver __passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); /** Pattern for PASV mode responses. Groups: (n,n,n,n),(n),(n) */ private static final java.util.regex.Pattern __PARMS_PAT; static{ __PARMS_PAT = java.util.regex.Pattern.compile("(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})"); } /** Controls the automatic server encoding detection (only UTF-8 supported). */ private boolean __autodetectEncoding = false; /** Map of FEAT responses. If null, has not been initialised. */ private HashMap> __featuresMap; private static class PropertiesSingleton{ static final Properties PROPERTIES; static{ InputStream resourceAsStream = FTPClient.class.getResourceAsStream(SYSTEM_TYPE_PROPERTIES); Properties p = null; if (resourceAsStream != null){ p = new Properties(); try{ p.load(resourceAsStream); }catch (IOException e){ // Ignored }finally{ try{ resourceAsStream.close(); }catch (IOException e){ // Ignored } } } PROPERTIES = p; } } private static Properties getOverrideProperties(){ return PropertiesSingleton.PROPERTIES; } /** * Default FTPClient constructor. Creates a new FTPClient instance * with the data connection mode set to * ACTIVE_LOCAL_DATA_CONNECTION_MODE , the file type * set to FTP.ASCII_FILE_TYPE , the * file format set to FTP.NON_PRINT_TEXT_FORMAT , * the file structure set to FTP.FILE_STRUCTURE , and * the transfer mode set to FTP.STREAM_TRANSFER_MODE . *

* The list parsing auto-detect feature can be configured to use lenient future * dates (short dates may be up to one day in the future) as follows: * *

     * FTPClient ftp = new FTPClient();
     * FTPClientConfig config = new FTPClientConfig();
     * config.setLenientFutureDates(true);
     * ftp.configure(config);
     * 
*/ public FTPClient(){ __initDefaults(); __dataTimeout = -1; __remoteVerificationEnabled = true; __parserFactory = new DefaultFTPFileEntryParserFactory(); __configuration = null; __listHiddenFiles = false; __useEPSVwithIPv4 = false; __random = new Random(); __passiveLocalHost = null; } private void __initDefaults(){ __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; __passiveHost = null; __passivePort = -1; __activeExternalHost = null; __reportActiveExternalHost = null; __activeMinPort = 0; __activeMaxPort = 0; __fileType = FTP.ASCII_FILE_TYPE; __fileStructure = FTP.FILE_STRUCTURE; __fileFormat = FTP.NON_PRINT_TEXT_FORMAT; __fileTransferMode = FTP.STREAM_TRANSFER_MODE; __restartOffset = 0; __systemName = null; __entryParser = null; __entryParserKey = ""; __featuresMap = null; } /** * Parse the pathname from a CWD reply. *

* According to RFC959 (http://www.ietf.org/rfc/rfc959.txt), * it should be the same as for MKD i.e. * {@code 257""[commentary]} * where any double-quotes in {@code } are doubled. * Unlike MKD, the commentary is optional. *

* However, see NET-442 for an exception. * * @param reply * @return the pathname, without enclosing quotes, * or the full string after the reply code and space if the syntax is invalid * (i.e. enclosing quotes are missing or embedded quotes are not doubled) */ // package protected for access by test cases static String __parsePathname(String reply){ String param = reply.substring(REPLY_CODE_LEN + 1); if (param.startsWith("\"")){ StringBuilder sb = new StringBuilder(); boolean quoteSeen = false; // start after initial quote for (int i = 1; i < param.length(); i++){ char ch = param.charAt(i); if (ch == '"'){ if (quoteSeen){ sb.append(ch); quoteSeen = false; }else{ // don't output yet, in case doubled quoteSeen = true; } }else{ if (quoteSeen){ // found lone trailing quote within string return sb.toString(); } sb.append(ch); // just another character } } if (quoteSeen){ // found lone trailing quote at end of string return sb.toString(); } } // malformed reply, return all after reply code and space return param; } /** * @since 3.1 * @param reply * the reply to parse * @throws MalformedServerReplyException * if the server reply does not match (n,n,n,n),(n),(n) */ protected void _parsePassiveModeReply(String reply) throws MalformedServerReplyException{ java.util.regex.Matcher m = __PARMS_PAT.matcher(reply); if (!m.find()){ throw new MalformedServerReplyException("Could not parse passive host information.\nServer Reply: " + reply); } __passiveHost = m.group(1).replace(',', '.'); // Fix up to look like IP address try{ int oct1 = Integer.parseInt(m.group(2)); int oct2 = Integer.parseInt(m.group(3)); __passivePort = (oct1 << 8) | oct2; }catch (NumberFormatException e){ throw new MalformedServerReplyException("Could not parse passive port information.\nServer Reply: " + reply); } if (__passiveNatWorkaroundStrategy != null){ try{ String passiveHost = __passiveNatWorkaroundStrategy.resolve(__passiveHost); if (!__passiveHost.equals(passiveHost)){ fireReplyReceived(0, "[Replacing PASV mode reply address " + __passiveHost + " with " + passiveHost + "]\n"); __passiveHost = passiveHost; } }catch (UnknownHostException e){ // Should not happen as we are passing in an IP address throw new MalformedServerReplyException("Could not parse passive host information.\nServer Reply: " + reply); } } } protected void _parseExtendedPassiveModeReply(String reply) throws MalformedServerReplyException{ reply = reply.substring(reply.indexOf('(') + 1, reply.indexOf(')')).trim(); char delim1, delim2, delim3, delim4; delim1 = reply.charAt(0); delim2 = reply.charAt(1); delim3 = reply.charAt(2); delim4 = reply.charAt(reply.length() - 1); if (!(delim1 == delim2) || !(delim2 == delim3) || !(delim3 == delim4)){ throw new MalformedServerReplyException("Could not parse extended passive host information.\nServer Reply: " + reply); } int port; try{ port = Integer.parseInt(reply.substring(3, reply.length() - 1)); }catch (NumberFormatException e){ throw new MalformedServerReplyException("Could not parse extended passive host information.\nServer Reply: " + reply); } // in EPSV mode, the passive host address is implicit __passiveHost = getRemoteAddress().getHostAddress(); __passivePort = port; } private boolean __storeFile(FTPCmd command,String remote,InputStream local) throws IOException{ return _storeFile(command.getCommand(), remote, local); } /** * @since 3.1 * @param command * the command to send * @param remote * the remote file name * @param local * the local file name * @return true if successful * @throws IOException * on error */ protected boolean _storeFile(String command,String remote,InputStream local) throws IOException{ Socket socket = _openDataConnection_(command, remote); if (socket == null){ return false; } final OutputStream output; if (__fileType == ASCII_FILE_TYPE){ output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); }else{ output = getBufferedOutputStream(socket.getOutputStream()); } CSL csl = null; if (__controlKeepAliveTimeout > 0){ csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout); } // Treat everything else as binary for now try{ Util.copyStream(local, output, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, __mergeListeners(csl), false); }catch (IOException e){ Util.closeQuietly(socket); // ignore close errors here if (csl != null){ csl.cleanUp(); // fetch any outstanding keepalive replies } throw e; } output.close(); // ensure the file is fully written socket.close(); // done writing the file if (csl != null){ csl.cleanUp(); // fetch any outstanding keepalive replies } // Get the transfer response boolean ok = completePendingCommand(); return ok; } private OutputStream __storeFileStream(FTPCmd command,String remote) throws IOException{ return _storeFileStream(command.getCommand(), remote); } /** * @param command * the command to send * @param remote * the remote file name * @return the output stream to write to * @throws IOException * on error * @since 3.1 */ protected OutputStream _storeFileStream(String command,String remote) throws IOException{ Socket socket = _openDataConnection_(command, remote); if (socket == null){ return null; } final OutputStream output; if (__fileType == ASCII_FILE_TYPE){ // We buffer ascii transfers because the buffering has to // be interposed between ToNetASCIIOutputSream and the underlying // socket output stream. We don't buffer binary transfers // because we don't want to impose a buffering policy on the // programmer if possible. Programmers can decide on their // own if they want to wrap the SocketOutputStream we return // for file types other than ASCII. output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); }else{ output = socket.getOutputStream(); } return new com.feilong.lib.net.io.SocketOutputStream(socket, output); } /** * Establishes a data connection with the FTP server, returning * a Socket for the connection if successful. If a restart * offset has been set with {@link #setRestartOffset(long)}, * a REST command is issued to the server with the offset as * an argument before establishing the data connection. Active * mode connections also cause a local PORT command to be issued. * * @param command * The int representation of the FTP command to send. * @param arg * The arguments to the FTP command. If this parameter is * set to null, then the command is sent with no argument. * @return A Socket corresponding to the established data connection. * Null is returned if an FTP protocol error is reported at * any point during the establishment and initialization of * the connection. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @since 3.3 */ protected Socket _openDataConnection_(FTPCmd command,String arg) throws IOException{ return _openDataConnection_(command.getCommand(), arg); } /** * Establishes a data connection with the FTP server, returning * a Socket for the connection if successful. If a restart * offset has been set with {@link #setRestartOffset(long)}, * a REST command is issued to the server with the offset as * an argument before establishing the data connection. Active * mode connections also cause a local PORT command to be issued. * * @param command * The text representation of the FTP command to send. * @param arg * The arguments to the FTP command. If this parameter is * set to null, then the command is sent with no argument. * @return A Socket corresponding to the established data connection. * Null is returned if an FTP protocol error is reported at * any point during the establishment and initialization of * the connection. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @since 3.1 */ protected Socket _openDataConnection_(String command,String arg) throws IOException{ if (__dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE && __dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE){ return null; } final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; Socket socket; if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE){ // if no activePortRange was set (correctly) -> getActivePort() = 0 // -> new ServerSocket(0) -> bind to any free local port ServerSocket server = _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress()); try{ // Try EPRT only if remote server is over IPv6, if not use PORT, // because EPRT has no advantage over PORT on IPv4. // It could even have the disadvantage, // that EPRT will make the data connection fail, because // today's intelligent NAT Firewalls are able to // substitute IP addresses in the PORT command, // but might not be able to recognize the EPRT command. if (isInet6Address){ if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))){ return null; } }else{ if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))){ return null; } } if ((__restartOffset > 0) && !restart(__restartOffset)){ return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))){ return null; } // For now, let's just use the data timeout value for waiting for // the data connection. It may be desirable to let this be a // separately configurable value. In any case, we really want // to allow preventing the accept from blocking indefinitely. if (__dataTimeout >= 0){ server.setSoTimeout(__dataTimeout); } socket = server.accept(); // Ensure the timeout is set before any commands are issued on the new socket if (__dataTimeout >= 0){ socket.setSoTimeout(__dataTimeout); } if (__receiveDataSocketBufferSize > 0){ socket.setReceiveBufferSize(__receiveDataSocketBufferSize); } if (__sendDataSocketBufferSize > 0){ socket.setSendBufferSize(__sendDataSocketBufferSize); } }finally{ server.close(); } }else{ // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE // Try EPSV command first on IPv6 - and IPv4 if enabled. // When using IPv4 with NAT it has the advantage // to work with more rare configurations. // E.g. if FTP server has a static PASV address (external network) // and the client is coming from another internal network. // In that case the data connection after PASV command would fail, // while EPSV would make the client succeed by taking just the port. boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE){ _parseExtendedPassiveModeReply(_replyLines.get(0)); }else{ if (isInet6Address){ return null; // Must use EPSV for IPV6 } // If EPSV failed on IPV4, revert to PASV if (pasv() != FTPReply.ENTERING_PASSIVE_MODE){ return null; } _parsePassiveModeReply(_replyLines.get(0)); } socket = _socketFactory_.createSocket(); if (__receiveDataSocketBufferSize > 0){ socket.setReceiveBufferSize(__receiveDataSocketBufferSize); } if (__sendDataSocketBufferSize > 0){ socket.setSendBufferSize(__sendDataSocketBufferSize); } if (__passiveLocalHost != null){ socket.bind(new InetSocketAddress(__passiveLocalHost, 0)); } // For now, let's just use the data timeout value for waiting for // the data connection. It may be desirable to let this be a // separately configurable value. In any case, we really want // to allow preventing the accept from blocking indefinitely. if (__dataTimeout >= 0){ socket.setSoTimeout(__dataTimeout); } socket.connect(new InetSocketAddress(__passiveHost, __passivePort), connectTimeout); if ((__restartOffset > 0) && !restart(__restartOffset)){ socket.close(); return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))){ socket.close(); return null; } } if (__remoteVerificationEnabled && !verifyRemote(socket)){ socket.close(); throw new IOException( "Host attempting data connection " + socket.getInetAddress().getHostAddress() + " is not same as server " + getRemoteAddress().getHostAddress()); } return socket; } @Override protected void _connectAction_() throws IOException{ _connectAction_(null); } /** * @param socketIsReader * the reader to reuse (if non-null) * @throws IOException * on error * @since 3.4 */ @Override protected void _connectAction_(Reader socketIsReader) throws IOException{ super._connectAction_(socketIsReader); // sets up _input_ and _output_ __initDefaults(); // must be after super._connectAction_(), because otherwise we get an // Exception claiming we're not connected if (__autodetectEncoding){ ArrayList oldReplyLines = new ArrayList<>(_replyLines); int oldReplyCode = _replyCode; if (hasFeature("UTF8") || hasFeature("UTF-8")) // UTF8 appears to be the default { setControlEncoding("UTF-8"); _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); } // restore the original reply (server greeting) _replyLines.clear(); _replyLines.addAll(oldReplyLines); _replyCode = oldReplyCode; _newReplyString = true; } } /** * Sets the timeout in milliseconds to use when reading from the * data connection. This timeout will be set immediately after * opening the data connection, provided that the value is ≥ 0. *

* Note: the timeout will also be applied when calling accept() * whilst establishing an active local data connection. * * @param timeout * The default timeout in milliseconds that is used when * opening a data connection socket. The value 0 means an infinite timeout. */ public void setDataTimeout(int timeout){ __dataTimeout = timeout; } /** * set the factory used for parser creation to the supplied factory object. * * @param parserFactory * factory object used to create FTPFileEntryParsers * * @see com.feilong.lib.net.ftp.parser.FTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.parser.DefaultFTPFileEntryParserFactory */ public void setParserFactory(FTPFileEntryParserFactory parserFactory){ __parserFactory = parserFactory; } /** * Closes the connection to the FTP server and restores * connection parameters to the default values. * * @throws IOException * If an error occurs while disconnecting. */ @Override public void disconnect() throws IOException{ super.disconnect(); __initDefaults(); } /** * Enable or disable verification that the remote host taking part * of a data connection is the same as the host to which the control * connection is attached. The default is for verification to be * enabled. You may set this value at any time, whether the * FTPClient is currently connected or not. * * @param enable * True to enable verification, false to disable verification. */ public void setRemoteVerificationEnabled(boolean enable){ __remoteVerificationEnabled = enable; } /** * Return whether or not verification of the remote host participating * in data connections is enabled. The default behavior is for * verification to be enabled. * * @return True if verification is enabled, false if not. */ public boolean isRemoteVerificationEnabled(){ return __remoteVerificationEnabled; } /** * Login to the FTP server using the provided username and password. * * @param username * The username to login under. * @param password * The password to use. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean login(String username,String password) throws IOException{ user(username); if (FTPReply.isPositiveCompletion(_replyCode)){ return true; } // If we get here, we either have an error code, or an intermmediate // reply requesting password. if (!FTPReply.isPositiveIntermediate(_replyCode)){ return false; } return FTPReply.isPositiveCompletion(pass(password)); } /** * Login to the FTP server using the provided username, password, * and account. If no account is required by the server, only * the username and password, the account information is not used. * * @param username * The username to login under. * @param password * The password to use. * @param account * The account to use. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean login(String username,String password,String account) throws IOException{ user(username); if (FTPReply.isPositiveCompletion(_replyCode)){ return true; } // If we get here, we either have an error code, or an intermmediate // reply requesting password. if (!FTPReply.isPositiveIntermediate(_replyCode)){ return false; } pass(password); if (FTPReply.isPositiveCompletion(_replyCode)){ return true; } if (!FTPReply.isPositiveIntermediate(_replyCode)){ return false; } return FTPReply.isPositiveCompletion(acct(account)); } /** * Logout of the FTP server by sending the QUIT command. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean logout() throws IOException{ return FTPReply.isPositiveCompletion(quit()); } /** * Change the current working directory of the FTP session. * * @param pathname * The new current working directory. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean changeWorkingDirectory(String pathname) throws IOException{ return FTPReply.isPositiveCompletion(cwd(pathname)); } /** * Change to the parent directory of the current working directory. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean changeToParentDirectory() throws IOException{ return FTPReply.isPositiveCompletion(cdup()); } /** * Issue the FTP SMNT command. * * @param pathname * The pathname to mount. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean structureMount(String pathname) throws IOException{ return FTPReply.isPositiveCompletion(smnt(pathname)); } /** * Reinitialize the FTP session. Not all FTP servers support this * command, which issues the FTP REIN command. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @since 3.4 (made public) */ public boolean reinitialize() throws IOException{ rein(); if (FTPReply.isPositiveCompletion(_replyCode) || (FTPReply.isPositivePreliminary(_replyCode) && FTPReply.isPositiveCompletion(getReply()))){ __initDefaults(); return true; } return false; } /** * Set the current data connection mode to * ACTIVE_LOCAL_DATA_CONNECTION_MODE. No communication * with the FTP server is conducted, but this causes all future data * transfers to require the FTP server to connect to the client's * data port. Additionally, to accommodate differences between socket * implementations on different platforms, this method causes the * client to issue a PORT command before every data transfer. */ public void enterLocalActiveMode(){ __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; __passiveHost = null; __passivePort = -1; } /** * Set the current data connection mode to * PASSIVE_LOCAL_DATA_CONNECTION_MODE . Use this * method only for data transfers between the client and server. * This method causes a PASV (or EPSV) command to be issued to the server * before the opening of every data connection, telling the server to * open a data port to which the client will connect to conduct * data transfers. The FTPClient will stay in * PASSIVE_LOCAL_DATA_CONNECTION_MODE until the * mode is changed by calling some other method such as * {@link #enterLocalActiveMode enterLocalActiveMode() } *

* N.B. currently calling any connect method will reset the mode to * ACTIVE_LOCAL_DATA_CONNECTION_MODE. */ public void enterLocalPassiveMode(){ __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE; // These will be set when just before a data connection is opened // in _openDataConnection_() __passiveHost = null; __passivePort = -1; } /** * Set the current data connection mode to * ACTIVE_REMOTE_DATA_CONNECTION . Use this method only * for server to server data transfers. This method issues a PORT * command to the server, indicating the other server and port to which * it should connect for data transfers. You must call this method * before EVERY server to server transfer attempt. The FTPClient will * NOT automatically continue to issue PORT commands. You also * must remember to call * {@link #enterLocalActiveMode enterLocalActiveMode() } if you * wish to return to the normal data connection mode. * * @param host * The passive mode server accepting connections for data * transfers. * @param port * The passive mode server's data port. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean enterRemoteActiveMode(InetAddress host,int port) throws IOException{ if (FTPReply.isPositiveCompletion(port(host, port))){ __dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE; __passiveHost = null; __passivePort = -1; return true; } return false; } /** * Set the current data connection mode to * PASSIVE_REMOTE_DATA_CONNECTION_MODE . Use this * method only for server to server data transfers. * This method issues a PASV command to the server, telling it to * open a data port to which the active server will connect to conduct * data transfers. You must call this method * before EVERY server to server transfer attempt. The FTPClient will * NOT automatically continue to issue PASV commands. You also * must remember to call * {@link #enterLocalActiveMode enterLocalActiveMode() } if you * wish to return to the normal data connection mode. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean enterRemotePassiveMode() throws IOException{ if (pasv() != FTPReply.ENTERING_PASSIVE_MODE){ return false; } __dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE; _parsePassiveModeReply(_replyLines.get(0)); return true; } /** * Returns the hostname or IP address (in the form of a string) returned * by the server when entering passive mode. If not in passive mode, * returns null. This method only returns a valid value AFTER a * data connection has been opened after a call to * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * This is because FTPClient sends a PASV command to the server only * just before opening a data connection, and not when you call * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * * @return The passive host name if in passive mode, otherwise null. */ public String getPassiveHost(){ return __passiveHost; } /** * If in passive mode, returns the data port of the passive host. * This method only returns a valid value AFTER a * data connection has been opened after a call to * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * This is because FTPClient sends a PASV command to the server only * just before opening a data connection, and not when you call * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. * * @return The data port of the passive server. If not in passive * mode, undefined. */ public int getPassivePort(){ return __passivePort; } /** * Returns the current data connection mode (one of the * _DATA_CONNECTION_MODE constants. * * @return The current data connection mode (one of the * _DATA_CONNECTION_MODE constants. */ public int getDataConnectionMode(){ return __dataConnectionMode; } /** * Get the client port for active mode. * * @return The client port for active mode. */ private int getActivePort(){ if (__activeMinPort > 0 && __activeMaxPort >= __activeMinPort){ if (__activeMaxPort == __activeMinPort){ return __activeMaxPort; } // Get a random port between the min and max port range return __random.nextInt(__activeMaxPort - __activeMinPort + 1) + __activeMinPort; } // default port return 0; } /** * Get the host address for active mode; allows the local address to be overridden. * * @return __activeExternalHost if non-null, else getLocalAddress() * @see #setActiveExternalIPAddress(String) */ private InetAddress getHostAddress(){ if (__activeExternalHost != null){ return __activeExternalHost; } // default local address return getLocalAddress(); } /** * Get the reported host address for active mode EPRT/PORT commands; * allows override of {@link #getHostAddress()}. * * Useful for FTP Client behind Firewall NAT. * * @return __reportActiveExternalHost if non-null, else getHostAddress(); */ private InetAddress getReportHostAddress(){ if (__reportActiveExternalHost != null){ return __reportActiveExternalHost; } return getHostAddress(); } /** * Set the client side port range in active mode. * * @param minPort * The lowest available port (inclusive). * @param maxPort * The highest available port (inclusive). * @since 2.2 */ public void setActivePortRange(int minPort,int maxPort){ this.__activeMinPort = minPort; this.__activeMaxPort = maxPort; } /** * Set the external IP address in active mode. * Useful when there are multiple network cards. * * @param ipAddress * The external IP address of this machine. * @throws UnknownHostException * if the ipAddress cannot be resolved * @since 2.2 */ public void setActiveExternalIPAddress(String ipAddress) throws UnknownHostException{ this.__activeExternalHost = InetAddress.getByName(ipAddress); } /** * Set the local IP address to use in passive mode. * Useful when there are multiple network cards. * * @param ipAddress * The local IP address of this machine. * @throws UnknownHostException * if the ipAddress cannot be resolved */ public void setPassiveLocalIPAddress(String ipAddress) throws UnknownHostException{ this.__passiveLocalHost = InetAddress.getByName(ipAddress); } /** * Set the local IP address to use in passive mode. * Useful when there are multiple network cards. * * @param inetAddress * The local IP address of this machine. */ public void setPassiveLocalIPAddress(InetAddress inetAddress){ this.__passiveLocalHost = inetAddress; } /** * Set the local IP address in passive mode. * Useful when there are multiple network cards. * * @return The local IP address in passive mode. */ public InetAddress getPassiveLocalIPAddress(){ return this.__passiveLocalHost; } /** * Set the external IP address to report in EPRT/PORT commands in active mode. * Useful when there are multiple network cards. * * @param ipAddress * The external IP address of this machine. * @throws UnknownHostException * if the ipAddress cannot be resolved * @since 3.1 * @see #getReportHostAddress() */ public void setReportActiveExternalIPAddress(String ipAddress) throws UnknownHostException{ this.__reportActiveExternalHost = InetAddress.getByName(ipAddress); } /** * Sets the file type to be transferred. This should be one of * FTP.ASCII_FILE_TYPE , FTP.BINARY_FILE_TYPE, * etc. The file type only needs to be set when you want to change the * type. After changing it, the new type stays in effect until you change * it again. The default file type is FTP.ASCII_FILE_TYPE * if this method is never called. *
* The server default is supposed to be ASCII (see RFC 959), however many * ftp servers default to BINARY. To ensure correct operation with all servers, * always specify the appropriate file type after connecting to the server. *
*

* N.B. currently calling any connect method will reset the type to * FTP.ASCII_FILE_TYPE. * * @param fileType * The _FILE_TYPE constant indcating the * type of file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean setFileType(int fileType) throws IOException{ if (FTPReply.isPositiveCompletion(type(fileType))){ __fileType = fileType; __fileFormat = FTP.NON_PRINT_TEXT_FORMAT; return true; } return false; } /** * Sets the file type to be transferred and the format. The type should be * one of FTP.ASCII_FILE_TYPE , * FTP.BINARY_FILE_TYPE , etc. The file type only needs to * be set when you want to change the type. After changing it, the new * type stays in effect until you change it again. The default file type * is FTP.ASCII_FILE_TYPE if this method is never called. *
* The server default is supposed to be ASCII (see RFC 959), however many * ftp servers default to BINARY. To ensure correct operation with all servers, * always specify the appropriate file type after connecting to the server. *
* The format should be one of the FTP class TEXT_FORMAT * constants, or if the type is FTP.LOCAL_FILE_TYPE , the * format should be the byte size for that type. The default format * is FTP.NON_PRINT_TEXT_FORMAT if this method is never * called. *

* N.B. currently calling any connect method will reset the type to * FTP.ASCII_FILE_TYPE and the formatOrByteSize to FTP.NON_PRINT_TEXT_FORMAT. * * @param fileType * The _FILE_TYPE constant indcating the * type of file. * @param formatOrByteSize * The format of the file (one of the * _FORMAT constants. In the case of * LOCAL_FILE_TYPE, the byte size. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean setFileType(int fileType,int formatOrByteSize) throws IOException{ if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize))){ __fileType = fileType; __fileFormat = formatOrByteSize; return true; } return false; } /** * Sets the file structure. The default structure is * FTP.FILE_STRUCTURE if this method is never called * or if a connect method is called. * * @param structure * The structure of the file (one of the FTP class * _STRUCTURE constants). * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean setFileStructure(int structure) throws IOException{ if (FTPReply.isPositiveCompletion(stru(structure))){ __fileStructure = structure; return true; } return false; } /** * Sets the transfer mode. The default transfer mode * FTP.STREAM_TRANSFER_MODE if this method is never called * or if a connect method is called. * * @param mode * The new transfer mode to use (one of the FTP class * _TRANSFER_MODE constants). * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean setFileTransferMode(int mode) throws IOException{ if (FTPReply.isPositiveCompletion(mode(mode))){ __fileTransferMode = mode; return true; } return false; } /** * Initiate a server to server file transfer. This method tells the * server to which the client is connected to retrieve a given file from * the other server. * * @param filename * The name of the file to retrieve. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean remoteRetrieve(String filename) throws IOException{ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE){ return FTPReply.isPositivePreliminary(retr(filename)); } return false; } /** * Initiate a server to server file transfer. This method tells the * server to which the client is connected to store a file on * the other server using the given filename. The other server must * have had a remoteRetrieve issued to it by another * FTPClient. * * @param filename * The name to call the file that is to be stored. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean remoteStore(String filename) throws IOException{ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE){ return FTPReply.isPositivePreliminary(stor(filename)); } return false; } /** * Initiate a server to server file transfer. This method tells the * server to which the client is connected to store a file on * the other server using a unique filename based on the given filename. * The other server must have had a remoteRetrieve issued * to it by another FTPClient. * * @param filename * The name on which to base the filename of the file * that is to be stored. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean remoteStoreUnique(String filename) throws IOException{ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE){ return FTPReply.isPositivePreliminary(stou(filename)); } return false; } /** * Initiate a server to server file transfer. This method tells the * server to which the client is connected to store a file on * the other server using a unique filename. * The other server must have had a remoteRetrieve issued * to it by another FTPClient. Many FTP servers require that a base * filename be given from which the unique filename can be derived. For * those servers use the other version of remoteStoreUnique * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean remoteStoreUnique() throws IOException{ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE){ return FTPReply.isPositivePreliminary(stou()); } return false; } // For server to server transfers /** * Initiate a server to server file transfer. This method tells the * server to which the client is connected to append to a given file on * the other server. The other server must have had a * remoteRetrieve issued to it by another FTPClient. * * @param filename * The name of the file to be appended to, or if the * file does not exist, the name to call the file being stored. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean remoteAppend(String filename) throws IOException{ if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE){ return FTPReply.isPositivePreliminary(appe(filename)); } return false; } /** * There are a few FTPClient methods that do not complete the * entire sequence of FTP commands to complete a transaction. These * commands require some action by the programmer after the reception * of a positive intermediate command. After the programmer's code * completes its actions, it must call this method to receive * the completion reply from the server and verify the success of the * entire transaction. *

* For example, * *

     * InputStream input;
     * OutputStream output;
     * input  = new FileInputStream("foobaz.txt");
     * output = ftp.storeFileStream("foobar.txt")
     * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
     *     input.close();
     *     output.close();
     *     ftp.logout();
     *     ftp.disconnect();
     *     System.err.println("File transfer failed.");
     *     System.exit(1);
     * }
     * Util.copyStream(input, output);
     * input.close();
     * output.close();
     * // Must call completePendingCommand() to finish command.
     * if(!ftp.completePendingCommand()) {
     *     ftp.logout();
     *     ftp.disconnect();
     *     System.err.println("File transfer failed.");
     *     System.exit(1);
     * }
     * 
* * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean completePendingCommand() throws IOException{ return FTPReply.isPositiveCompletion(getReply()); } /** * Retrieves a named file from the server and writes it to the given * OutputStream. This method does NOT close the given OutputStream. * If the current file type is ASCII, line separators in the file are * converted to the local representation. *

* Note: if you have used {@link #setRestartOffset(long)}, * the file data will start from the selected offset. * * @param remote * The name of the remote file. * @param local * The local OutputStream to which to write the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws com.feilong.lib.net.io.CopyStreamException * If an I/O error occurs while actually * transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException * causing the error. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean retrieveFile(String remote,OutputStream local) throws IOException{ return _retrieveFile(FTPCmd.RETR.getCommand(), remote, local); } /** * @param command * the command to get * @param remote * the remote file name * @param local * the local file name * @return true if successful * @throws IOException * on error * @since 3.1 */ protected boolean _retrieveFile(String command,String remote,OutputStream local) throws IOException{ Socket socket = _openDataConnection_(command, remote); if (socket == null){ return false; } final InputStream input; if (__fileType == ASCII_FILE_TYPE){ input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); }else{ input = getBufferedInputStream(socket.getInputStream()); } CSL csl = null; if (__controlKeepAliveTimeout > 0){ csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout); } // Treat everything else as binary for now try{ Util.copyStream(input, local, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, __mergeListeners(csl), false); }finally{ Util.closeQuietly(input); Util.closeQuietly(socket); if (csl != null){ csl.cleanUp(); // fetch any outstanding keepalive replies } } // Get the transfer response boolean ok = completePendingCommand(); return ok; } /** * Returns an InputStream from which a named file from the server * can be read. If the current file type is ASCII, the returned * InputStream will convert line separators in the file to * the local representation. You must close the InputStream when you * finish reading from it. The InputStream itself will take care of * closing the parent data connection socket upon being closed. *

* To finalize the file transfer you must call * {@link #completePendingCommand completePendingCommand } and * check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. *

* Note: if you have used {@link #setRestartOffset(long)}, * the file data will start from the selected offset. * * @param remote * The name of the remote file. * @return An InputStream from which the remote file can be read. If * the data connection cannot be opened (e.g., the file does not * exist), null is returned (in which case you may check the reply * code to determine the exact reason for failure). * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public InputStream retrieveFileStream(String remote) throws IOException{ return _retrieveFileStream(FTPCmd.RETR.getCommand(), remote); } /** * @param command * the command to send * @param remote * the remote file name * @return the stream from which to read the file * @throws IOException * on error * @since 3.1 */ protected InputStream _retrieveFileStream(String command,String remote) throws IOException{ Socket socket = _openDataConnection_(command, remote); if (socket == null){ return null; } final InputStream input; if (__fileType == ASCII_FILE_TYPE){ // We buffer ascii transfers because the buffering has to // be interposed between FromNetASCIIOutputSream and the underlying // socket input stream. We don't buffer binary transfers // because we don't want to impose a buffering policy on the // programmer if possible. Programmers can decide on their // own if they want to wrap the SocketInputStream we return // for file types other than ASCII. input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); }else{ input = socket.getInputStream(); } return new com.feilong.lib.net.io.SocketInputStream(socket, input); } /** * Stores a file on the server using the given name and taking input * from the given InputStream. This method does NOT close the given * InputStream. If the current file type is ASCII, line separators in * the file are transparently converted to the NETASCII format (i.e., * you should not attempt to create a special InputStream to do this). * * @param remote * The name to give the remote file. * @param local * The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws com.feilong.lib.net.io.CopyStreamException * If an I/O error occurs while actually * transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException * causing the error. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean storeFile(String remote,InputStream local) throws IOException{ return __storeFile(FTPCmd.STOR, remote, local); } /** * Returns an OutputStream through which data can be written to store * a file on the server using the given name. If the current file type * is ASCII, the returned OutputStream will convert line separators in * the file to the NETASCII format (i.e., you should not attempt to * create a special OutputStream to do this). You must close the * OutputStream when you finish writing to it. The OutputStream itself * will take care of closing the parent data connection socket upon being * closed. *

* To finalize the file transfer you must call * {@link #completePendingCommand completePendingCommand } and * check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @param remote * The name to give the remote file. * @return An OutputStream through which the remote file can be written. If * the data connection cannot be opened (e.g., the file does not * exist), null is returned (in which case you may check the reply * code to determine the exact reason for failure). * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public OutputStream storeFileStream(String remote) throws IOException{ return __storeFileStream(FTPCmd.STOR, remote); } /** * Appends to a file on the server with the given name, taking input * from the given InputStream. This method does NOT close the given * InputStream. If the current file type is ASCII, line separators in * the file are transparently converted to the NETASCII format (i.e., * you should not attempt to create a special InputStream to do this). * * @param remote * The name of the remote file. * @param local * The local InputStream from which to read the data to * be appended to the remote file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws com.feilong.lib.net.io.CopyStreamException * If an I/O error occurs while actually * transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException * causing the error. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean appendFile(String remote,InputStream local) throws IOException{ return __storeFile(FTPCmd.APPE, remote, local); } /** * Returns an OutputStream through which data can be written to append * to a file on the server with the given name. If the current file type * is ASCII, the returned OutputStream will convert line separators in * the file to the NETASCII format (i.e., you should not attempt to * create a special OutputStream to do this). You must close the * OutputStream when you finish writing to it. The OutputStream itself * will take care of closing the parent data connection socket upon being * closed. *

* To finalize the file transfer you must call * {@link #completePendingCommand completePendingCommand } and * check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @param remote * The name of the remote file. * @return An OutputStream through which the remote file can be appended. * If the data connection cannot be opened (e.g., the file does not * exist), null is returned (in which case you may check the reply * code to determine the exact reason for failure). * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public OutputStream appendFileStream(String remote) throws IOException{ return __storeFileStream(FTPCmd.APPE, remote); } /** * Stores a file on the server using a unique name derived from the * given name and taking input * from the given InputStream. This method does NOT close the given * InputStream. If the current file type is ASCII, line separators in * the file are transparently converted to the NETASCII format (i.e., * you should not attempt to create a special InputStream to do this). * * @param remote * The name on which to base the unique name given to * the remote file. * @param local * The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws com.feilong.lib.net.io.CopyStreamException * If an I/O error occurs while actually * transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException * causing the error. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean storeUniqueFile(String remote,InputStream local) throws IOException{ return __storeFile(FTPCmd.STOU, remote, local); } /** * Returns an OutputStream through which data can be written to store * a file on the server using a unique name derived from the given name. * If the current file type * is ASCII, the returned OutputStream will convert line separators in * the file to the NETASCII format (i.e., you should not attempt to * create a special OutputStream to do this). You must close the * OutputStream when you finish writing to it. The OutputStream itself * will take care of closing the parent data connection socket upon being * closed. *

* To finalize the file transfer you must call * {@link #completePendingCommand completePendingCommand } and * check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @param remote * The name on which to base the unique name given to * the remote file. * @return An OutputStream through which the remote file can be written. If * the data connection cannot be opened (e.g., the file does not * exist), null is returned (in which case you may check the reply * code to determine the exact reason for failure). * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public OutputStream storeUniqueFileStream(String remote) throws IOException{ return __storeFileStream(FTPCmd.STOU, remote); } /** * Stores a file on the server using a unique name assigned by the * server and taking input from the given InputStream. This method does * NOT close the given * InputStream. If the current file type is ASCII, line separators in * the file are transparently converted to the NETASCII format (i.e., * you should not attempt to create a special InputStream to do this). * * @param local * The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws com.feilong.lib.net.io.CopyStreamException * If an I/O error occurs while actually * transferring the file. The CopyStreamException allows you to * determine the number of bytes transferred and the IOException * causing the error. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean storeUniqueFile(InputStream local) throws IOException{ return __storeFile(FTPCmd.STOU, null, local); } /** * Returns an OutputStream through which data can be written to store * a file on the server using a unique name assigned by the server. * If the current file type * is ASCII, the returned OutputStream will convert line separators in * the file to the NETASCII format (i.e., you should not attempt to * create a special OutputStream to do this). You must close the * OutputStream when you finish writing to it. The OutputStream itself * will take care of closing the parent data connection socket upon being * closed. *

* To finalize the file transfer you must call * {@link #completePendingCommand completePendingCommand } and * check its return value to verify success. * If this is not done, subsequent commands may behave unexpectedly. * * @return An OutputStream through which the remote file can be written. If * the data connection cannot be opened (e.g., the file does not * exist), null is returned (in which case you may check the reply * code to determine the exact reason for failure). * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public OutputStream storeUniqueFileStream() throws IOException{ return __storeFileStream(FTPCmd.STOU, null); } /** * Reserve a number of bytes on the server for the next file transfer. * * @param bytes * The number of bytes which the server should allocate. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean allocate(int bytes) throws IOException{ return FTPReply.isPositiveCompletion(allo(bytes)); } /** * Query the server for supported features. The server may reply with a list of server-supported exensions. * For example, a typical client-server interaction might be (from RFC 2389): * *

        C> feat
        S> 211-Extensions supported:
        S>  MLST size*;create;modify*;perm;media-type
        S>  SIZE
        S>  COMPRESSION
        S>  MDTM
        S> 211 END
     * 
* * @see http://www.faqs.org/rfcs/rfc2389.html * @return True if successfully completed, false if not. * @throws IOException * on error * @since 2.2 */ public boolean features() throws IOException{ return FTPReply.isPositiveCompletion(feat()); } /** * Query the server for a supported feature, and returns its values (if any). * Caches the parsed response to avoid resending the command repeatedly. * * @param feature * the feature to check * * @return if the feature is present, returns the feature values (empty array if none) * Returns {@code null} if the feature is not found or the command failed. * Check {@link #getReplyCode()} or {@link #getReplyString()} if so. * @throws IOException * on error * @since 3.0 */ public String[] featureValues(String feature) throws IOException{ if (!initFeatureMap()){ return null; } Set entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); if (entries != null){ return entries.toArray(new String[entries.size()]); } return null; } /** * Query the server for a supported feature, and returns the its value (if any). * Caches the parsed response to avoid resending the command repeatedly. * * @param feature * the feature to check * * @return if the feature is present, returns the feature value or the empty string * if the feature exists but has no value. * Returns {@code null} if the feature is not found or the command failed. * Check {@link #getReplyCode()} or {@link #getReplyString()} if so. * @throws IOException * on error * @since 3.0 */ public String featureValue(String feature) throws IOException{ String[] values = featureValues(feature); if (values != null){ return values[0]; } return null; } /** * Query the server for a supported feature. * Caches the parsed response to avoid resending the command repeatedly. * * @param feature * the name of the feature; it is converted to upper case. * @return {@code true} if the feature is present, {@code false} if the feature is not present * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link #getReplyString()} * if it is necessary to distinguish these cases. * * @throws IOException * on error * @since 3.0 */ public boolean hasFeature(String feature) throws IOException{ if (!initFeatureMap()){ return false; } return __featuresMap.containsKey(feature.toUpperCase(Locale.ENGLISH)); } /** * Query the server for a supported feature with particular value, * for example "AUTH SSL" or "AUTH TLS". * Caches the parsed response to avoid resending the command repeatedly. * * @param feature * the name of the feature; it is converted to upper case. * @param value * the value to find. * * @return {@code true} if the feature is present, {@code false} if the feature is not present * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link #getReplyString()} * if it is necessary to distinguish these cases. * * @throws IOException * on error * @since 3.0 */ public boolean hasFeature(String feature,String value) throws IOException{ if (!initFeatureMap()){ return false; } Set entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); if (entries != null){ return entries.contains(value); } return false; } /* * Create the feature map if not already created. */ private boolean initFeatureMap() throws IOException{ if (__featuresMap == null){ // Don't create map here, because next line may throw exception final int replyCode = feat(); if (replyCode == FTPReply.NOT_LOGGED_IN){ // 503 return false; // NET-518; don't create empy map } boolean success = FTPReply.isPositiveCompletion(replyCode); // we init the map here, so we don't keep trying if we know the command will fail __featuresMap = new HashMap<>(); if (!success){ return false; } for (String l : getReplyStrings()){ if (l.startsWith(" ")){ // it's a FEAT entry String key; String value = ""; int varsep = l.indexOf(' ', 1); if (varsep > 0){ key = l.substring(1, varsep); value = l.substring(varsep + 1); }else{ key = l.substring(1); } key = key.toUpperCase(Locale.ENGLISH); Set entries = __featuresMap.get(key); if (entries == null){ entries = new HashSet<>(); __featuresMap.put(key, entries); } entries.add(value); } } } return true; } /** * Reserve space on the server for the next file transfer. * * @param bytes * The number of bytes which the server should allocate. * @param recordSize * The size of a file record. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean allocate(int bytes,int recordSize) throws IOException{ return FTPReply.isPositiveCompletion(allo(bytes, recordSize)); } /** * Issue a command and wait for the reply. *

* Should only be used with commands that return replies on the * command channel - do not use for LIST, NLST, MLSD etc. * * @param command * The command to invoke * @param params * The parameters string, may be {@code null} * @return True if successfully completed, false if not, in which case * call {@link #getReplyCode()} or {@link #getReplyString()} * to get the reason. * * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @since 3.0 */ public boolean doCommand(String command,String params) throws IOException{ return FTPReply.isPositiveCompletion(sendCommand(command, params)); } /** * Issue a command and wait for the reply, returning it as an array of strings. *

* Should only be used with commands that return replies on the * command channel - do not use for LIST, NLST, MLSD etc. * * @param command * The command to invoke * @param params * The parameters string, may be {@code null} * @return The array of replies, or {@code null} if the command failed, in which case * call {@link #getReplyCode()} or {@link #getReplyString()} * to get the reason. * * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @since 3.0 */ public String[] doCommandAsStrings(String command,String params) throws IOException{ boolean success = FTPReply.isPositiveCompletion(sendCommand(command, params)); if (success){ return getReplyStrings(); } return null; } /** * Get file details using the MLST command * * @param pathname * the file or directory to list, may be {@code null} * @return the file details, may be {@code null} * @throws IOException * on error * @since 3.0 */ public FTPFile mlistFile(String pathname) throws IOException{ boolean success = FTPReply.isPositiveCompletion(sendCommand(FTPCmd.MLST, pathname)); if (success){ String reply = getReplyStrings()[1]; /* * check the response makes sense. * Must have space before fact(s) and between fact(s) and filename * Fact(s) can be absent, so at least 3 chars are needed. */ if (reply.length() < 3 || reply.charAt(0) != ' '){ throw new MalformedServerReplyException("Invalid server reply (MLST): '" + reply + "'"); } String entry = reply.substring(1); // skip leading space for parser return MLSxEntryParser.parseEntry(entry); } return null; } /** * Generate a directory listing for the current directory using the MLSD command. * * @return the array of file entries * @throws IOException * on error * @since 3.0 */ public FTPFile[] mlistDir() throws IOException{ return mlistDir(null); } /** * Generate a directory listing using the MLSD command. * * @param pathname * the directory name, may be {@code null} * @return the array of file entries * @throws IOException * on error * @since 3.0 */ public FTPFile[] mlistDir(String pathname) throws IOException{ FTPListParseEngine engine = initiateMListParsing(pathname); return engine.getFiles(); } /** * Generate a directory listing using the MLSD command. * * @param pathname * the directory name, may be {@code null} * @param filter * the filter to apply to the responses * @return the array of file entries * @throws IOException * on error * @since 3.0 */ public FTPFile[] mlistDir(String pathname,FTPFileFilter filter) throws IOException{ FTPListParseEngine engine = initiateMListParsing(pathname); return engine.getFiles(filter); } /** * Restart a STREAM_TRANSFER_MODE file transfer starting * from the given offset. This will only work on FTP servers supporting * the REST comand for the stream transfer mode. However, most FTP * servers support this. Any subsequent file transfer will start * reading or writing the remote file from the indicated offset. * * @param offset * The offset into the remote file at which to start the * next file transfer. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @since 3.1 (changed from private to protected) */ protected boolean restart(long offset) throws IOException{ __restartOffset = 0; return FTPReply.isPositiveIntermediate(rest(Long.toString(offset))); } /** * Sets the restart offset for file transfers. *

* The restart command is not sent to the server immediately. * It is sent when a data connection is created as part of a * subsequent command. * The restart marker is reset to zero after use. *

*

* Note: This method should only be invoked immediately prior to * the transfer to which it applies. * * @param offset * The offset into the remote file at which to start the * next file transfer. This must be a value greater than or * equal to zero. */ public void setRestartOffset(long offset){ if (offset >= 0){ __restartOffset = offset; } } /** * Fetches the restart offset. * * @return offset The offset into the remote file at which to start the * next file transfer. */ public long getRestartOffset(){ return __restartOffset; } /** * Renames a remote file. * * @param from * The name of the remote file to rename. * @param to * The new name of the remote file. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean rename(String from,String to) throws IOException{ if (!FTPReply.isPositiveIntermediate(rnfr(from))){ return false; } return FTPReply.isPositiveCompletion(rnto(to)); } /** * Abort a transfer in progress. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean abort() throws IOException{ return FTPReply.isPositiveCompletion(abor()); } /** * Deletes a file on the FTP server. * * @param pathname * The pathname of the file to be deleted. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean deleteFile(String pathname) throws IOException{ return FTPReply.isPositiveCompletion(dele(pathname)); } /** * Removes a directory on the FTP server (if empty). * * @param pathname * The pathname of the directory to remove. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean removeDirectory(String pathname) throws IOException{ return FTPReply.isPositiveCompletion(rmd(pathname)); } /** * Creates a new subdirectory on the FTP server in the current directory * (if a relative pathname is given) or where specified (if an absolute * pathname is given). * * @param pathname * The pathname of the directory to create. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean makeDirectory(String pathname) throws IOException{ return FTPReply.isPositiveCompletion(mkd(pathname)); } /** * Returns the pathname of the current working directory. * * @return The pathname of the current working directory. If it cannot * be obtained, returns null. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String printWorkingDirectory() throws IOException{ if (pwd() != FTPReply.PATHNAME_CREATED){ return null; } return __parsePathname(_replyLines.get(_replyLines.size() - 1)); } /** * Send a site specific command. * * @param arguments * The site specific command and arguments. * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean sendSiteCommand(String arguments) throws IOException{ return FTPReply.isPositiveCompletion(site(arguments)); } /** * Fetches the system type from the server and returns the string. * This value is cached for the duration of the connection after the * first call to this method. In other words, only the first time * that you invoke this method will it issue a SYST command to the * FTP server. FTPClient will remember the value and return the * cached value until a call to disconnect. *

* If the SYST command fails, and the system property * {@link #FTP_SYSTEM_TYPE_DEFAULT} is defined, then this is used instead. * * @return The system type obtained from the server. Never null. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server (and the default * system type property is not defined) * @since 2.2 */ public String getSystemType() throws IOException{ //if (syst() == FTPReply.NAME_SYSTEM_TYPE) // Technically, we should expect a NAME_SYSTEM_TYPE response, but // in practice FTP servers deviate, so we soften the condition to // a positive completion. if (__systemName == null){ if (FTPReply.isPositiveCompletion(syst())){ // Assume that response is not empty here (cannot be null) __systemName = _replyLines.get(_replyLines.size() - 1).substring(4); }else{ // Check if the user has provided a default for when the SYST command fails String systDefault = System.getProperty(FTP_SYSTEM_TYPE_DEFAULT); if (systDefault != null){ __systemName = systDefault; }else{ throw new IOException("Unable to determine system type - response: " + getReplyString()); } } } return __systemName; } /** * Fetches the system help information from the server and returns the * full string. * * @return The system help string obtained from the server. null if the * information could not be obtained. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String listHelp() throws IOException{ if (FTPReply.isPositiveCompletion(help())){ return getReplyString(); } return null; } /** * Fetches the help information for a given command from the server and * returns the full string. * * @param command * The command on which to ask for help. * @return The command help string obtained from the server. null if the * information could not be obtained. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String listHelp(String command) throws IOException{ if (FTPReply.isPositiveCompletion(help(command))){ return getReplyString(); } return null; } /** * Sends a NOOP command to the FTP server. This is useful for preventing * server timeouts. * * @return True if successfully completed, false if not. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public boolean sendNoOp() throws IOException{ return FTPReply.isPositiveCompletion(noop()); } /** * Obtain a list of filenames in a directory (or just the name of a given * file, which is not particularly useful). This information is obtained * through the NLST command. If the given pathname is a directory and * contains no files, a zero length array is returned only * if the FTP server returned a positive completion code, otherwise * null is returned (the FTP server returned a 550 error No files found.). * If the directory is not empty, an array of filenames in the directory is * returned. If the pathname corresponds * to a file, only that file will be listed. The server may or may not * expand glob expressions. * * @param pathname * The file or directory to list. * Warning: the server may treat a leading '-' as an * option introducer. If so, try using an absolute path, * or prefix the path with ./ (unix style servers). * Some servers may support "--" as meaning end of options, * in which case "-- -xyz" should work. * @return The list of filenames contained in the given path. null if * the list could not be obtained. If there are no filenames in * the directory, a zero-length array is returned. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String[] listNames(String pathname) throws IOException{ Socket socket = _openDataConnection_(FTPCmd.NLST, getListArguments(pathname)); if (socket == null){ return null; } BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); ArrayList results = new ArrayList<>(); String line; while ((line = reader.readLine()) != null){ results.add(line); } reader.close(); socket.close(); if (completePendingCommand()){ String[] names = new String[results.size()]; return results.toArray(names); } return null; } /** * Obtain a list of filenames in the current working directory * This information is obtained through the NLST command. If the current * directory contains no files, a zero length array is returned only * if the FTP server returned a positive completion code, otherwise, * null is returned (the FTP server returned a 550 error No files found.). * If the directory is not empty, an array of filenames in the directory is * returned. * * @return The list of filenames contained in the current working * directory. null if the list could not be obtained. * If there are no filenames in the directory, a zero-length array * is returned. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String[] listNames() throws IOException{ return listNames(null); } /** * Using the default system autodetect mechanism, obtain a * list of file information for the current working directory * or for just a single file. *

* This information is obtained through the LIST command. The contents of * the returned array is determined by the FTPFileEntryParser * used. *

* N.B. the LIST command does not generally return very precise timestamps. * For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. * If the server supports it, the MLSD command returns timestamps with a precision * of seconds, and may include milliseconds. See {@link #mlistDir()} * * @param pathname * The file or directory to list. Since the server may * or may not expand glob expressions, using them here * is not recommended and may well cause this method to * fail. * Also, some servers treat a leading '-' as being an option. * To avoid this interpretation, use an absolute pathname * or prefix the pathname with ./ (unix style servers). * Some servers may support "--" as meaning end of options, * in which case "-- -xyz" should work. * * @return The list of file information contained in the given path in * the format determined by the autodetection mechanism * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection * as a result of the client being idle or some other * reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException * or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply * from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the parserKey parameter cannot be * resolved by the selected parser factory. * In the DefaultFTPEntryParserFactory, this will * happen when parserKey is neither * the fully qualified class name of a class * implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser * nor a string containing one of the recognized keys * mapping to such a parser or if class loader * security issues prevent its being loaded. * @see com.feilong.lib.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.parser.FTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.FTPFileEntryParser */ public FTPFile[] listFiles(String pathname) throws IOException{ FTPListParseEngine engine = initiateListParsing((String) null, pathname); return engine.getFiles(); } /** * Using the default system autodetect mechanism, obtain a * list of file information for the current working directory. *

* This information is obtained through the LIST command. The contents of * the returned array is determined by the FTPFileEntryParser * used. *

* N.B. the LIST command does not generally return very precise timestamps. * For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. * If the server supports it, the MLSD command returns timestamps with a precision * of seconds, and may include milliseconds. See {@link #mlistDir()} * * @return The list of file information contained in the current directory * in the format determined by the autodetection mechanism. *

* * NOTE: This array may contain null members if any of the * individual file listings failed to parse. The caller should * check each entry for null before referencing it. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection * as a result of the client being idle or some other * reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException * or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply * from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the parserKey parameter cannot be * resolved by the selected parser factory. * In the DefaultFTPEntryParserFactory, this will * happen when parserKey is neither * the fully qualified class name of a class * implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser * nor a string containing one of the recognized keys * mapping to such a parser or if class loader * security issues prevent its being loaded. * @see com.feilong.lib.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.parser.FTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.FTPFileEntryParser */ public FTPFile[] listFiles() throws IOException{ return listFiles((String) null); } /** * Version of {@link #listFiles(String)} which allows a filter to be provided. * For example: listFiles("site", FTPFileFilters.DIRECTORY); * * @param pathname * the initial path, may be null * @param filter * the filter, non-null * @return the list of FTPFile entries. * @throws IOException * on error * @since 2.2 */ public FTPFile[] listFiles(String pathname,FTPFileFilter filter) throws IOException{ FTPListParseEngine engine = initiateListParsing((String) null, pathname); return engine.getFiles(filter); } /** * Using the default system autodetect mechanism, obtain a * list of directories contained in the current working directory. *

* This information is obtained through the LIST command. The contents of * the returned array is determined by the FTPFileEntryParser * used. *

* N.B. the LIST command does not generally return very precise timestamps. * For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. * If the server supports it, the MLSD command returns timestamps with a precision * of seconds, and may include milliseconds. See {@link #mlistDir()} * * @return The list of directories contained in the current directory * in the format determined by the autodetection mechanism. * * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection * as a result of the client being idle or some other * reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException * or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply * from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the parserKey parameter cannot be * resolved by the selected parser factory. * In the DefaultFTPEntryParserFactory, this will * happen when parserKey is neither * the fully qualified class name of a class * implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser * nor a string containing one of the recognized keys * mapping to such a parser or if class loader * security issues prevent its being loaded. * @see com.feilong.lib.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.parser.FTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.FTPFileEntryParser * @since 3.0 */ public FTPFile[] listDirectories() throws IOException{ return listDirectories((String) null); } /** * Using the default system autodetect mechanism, obtain a * list of directories contained in the specified directory. *

* This information is obtained through the LIST command. The contents of * the returned array is determined by the FTPFileEntryParser * used. *

* N.B. the LIST command does not generally return very precise timestamps. * For recent files, the response usually contains hours and minutes (not seconds). * For older files, the output may only contain a date. * If the server supports it, the MLSD command returns timestamps with a precision * of seconds, and may include milliseconds. See {@link #mlistDir()} * * @param parent * the starting directory * * @return The list of directories contained in the specified directory * in the format determined by the autodetection mechanism. * * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection * as a result of the client being idle or some other * reason causing the server to send FTP reply code 421. * This exception may be caught either as an IOException * or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply * from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the parserKey parameter cannot be * resolved by the selected parser factory. * In the DefaultFTPEntryParserFactory, this will * happen when parserKey is neither * the fully qualified class name of a class * implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser * nor a string containing one of the recognized keys * mapping to such a parser or if class loader * security issues prevent its being loaded. * @see com.feilong.lib.net.ftp.parser.DefaultFTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.parser.FTPFileEntryParserFactory * @see com.feilong.lib.net.ftp.FTPFileEntryParser * @since 3.0 */ public FTPFile[] listDirectories(String parent) throws IOException{ return listFiles(parent, FTPFileFilters.DIRECTORIES); } /** * Using the default autodetect mechanism, initialize an FTPListParseEngine * object containing a raw file information for the current working * directory on the server * This information is obtained through the LIST command. This object * is then capable of being iterated to return a sequence of FTPFile * objects with information filled in by the * FTPFileEntryParser used. *

* This method differs from using the listFiles() methods in that * expensive FTPFile objects are not created until needed which may be * an advantage on large lists. * * @return A FTPListParseEngine object that holds the raw information and * is capable of providing parsed FTPFile objects, one for each file * containing information contained in the given path in the format * determined by the parser parameter. Null will be * returned if a data connection cannot be opened. If the current working * directory contains no files, an empty array will be the return. * * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the autodetect mechanism cannot * resolve the type of system we are connected with. * @see FTPListParseEngine */ public FTPListParseEngine initiateListParsing() throws IOException{ return initiateListParsing((String) null); } /** * Using the default autodetect mechanism, initialize an FTPListParseEngine * object containing a raw file information for the supplied directory. * This information is obtained through the LIST command. This object * is then capable of being iterated to return a sequence of FTPFile * objects with information filled in by the * FTPFileEntryParser used. *

* The server may or may not expand glob expressions. You should avoid * using glob expressions because the return format for glob listings * differs from server to server and will likely cause this method to fail. *

* This method differs from using the listFiles() methods in that * expensive FTPFile objects are not created until needed which may be * an advantage on large lists. * *

     * FTPClient f = FTPClient();
     * f.connect(server);
     * f.login(username, password);
     * FTPListParseEngine engine = f.initiateListParsing(directory);
     *
     * while (engine.hasNext()){
     *     FTPFile[] files = engine.getNext(25); // "page size" you want
     *     //do whatever you want with these files, display them, etc.
     *     //expensive FTPFile objects not created until needed.
     * }
     * 
* * @param pathname * the starting directory * * @return A FTPListParseEngine object that holds the raw information and * is capable of providing parsed FTPFile objects, one for each file * containing information contained in the given path in the format * determined by the parser parameter. Null will be * returned if a data connection cannot be opened. If the current working * directory contains no files, an empty array will be the return. * * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the autodetect mechanism cannot * resolve the type of system we are connected with. * @see FTPListParseEngine */ public FTPListParseEngine initiateListParsing(String pathname) throws IOException{ return initiateListParsing((String) null, pathname); } /** * Using the supplied parser key, initialize an FTPListParseEngine * object containing a raw file information for the supplied directory. * This information is obtained through the LIST command. This object * is then capable of being iterated to return a sequence of FTPFile * objects with information filled in by the * FTPFileEntryParser used. *

* The server may or may not expand glob expressions. You should avoid * using glob expressions because the return format for glob listings * differs from server to server and will likely cause this method to fail. *

* This method differs from using the listFiles() methods in that * expensive FTPFile objects are not created until needed which may be * an advantage on large lists. * * @param parserKey * A string representing a designated code or fully-qualified * class name of an FTPFileEntryParser that should be * used to parse each server file listing. * May be {@code null}, in which case the code checks first * the system property {@link #FTP_SYSTEM_TYPE}, and if that is * not defined the SYST command is used to provide the value. * To allow for arbitrary system types, the return from the * SYST command is used to look up an alias for the type in the * {@link #SYSTEM_TYPE_PROPERTIES} properties file if it is available. * @param pathname * the starting directory * * @return A FTPListParseEngine object that holds the raw information and * is capable of providing parsed FTPFile objects, one for each file * containing information contained in the given path in the format * determined by the parser parameter. Null will be * returned if a data connection cannot be opened. If the current working * directory contains no files, an empty array will be the return. * * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @throws com.feilong.lib.net.ftp.parser.ParserInitializationException * Thrown if the parserKey parameter cannot be * resolved by the selected parser factory. * In the DefaultFTPEntryParserFactory, this will * happen when parserKey is neither * the fully qualified class name of a class * implementing the interface * org.apache.commons.net.ftp.FTPFileEntryParser * nor a string containing one of the recognized keys * mapping to such a parser or if class loader * security issues prevent its being loaded. * @see FTPListParseEngine */ public FTPListParseEngine initiateListParsing(String parserKey,String pathname) throws IOException{ __createParser(parserKey); // create and cache parser return initiateListParsing(__entryParser, pathname); } // package access for test purposes void __createParser(String parserKey) throws IOException{ // We cache the value to avoid creation of a new object every // time a file listing is generated. // Note: we don't check against a null parserKey (NET-544) if (__entryParser == null || (parserKey != null && !__entryParserKey.equals(parserKey))){ if (null != parserKey){ // if a parser key was supplied in the parameters, // use that to create the parser __entryParser = __parserFactory.createFileEntryParser(parserKey); __entryParserKey = parserKey; }else{ // if no parserKey was supplied, check for a configuration // in the params, and if it has a non-empty system type, use that. if (null != __configuration && __configuration.getServerSystemKey().length() > 0){ __entryParser = __parserFactory.createFileEntryParser(__configuration); __entryParserKey = __configuration.getServerSystemKey(); }else{ // if a parserKey hasn't been supplied, and a configuration // hasn't been supplied, and the override property is not set // then autodetect by calling // the SYST command and use that to choose the parser. String systemType = System.getProperty(FTP_SYSTEM_TYPE); if (systemType == null){ systemType = getSystemType(); // cannot be null Properties override = getOverrideProperties(); if (override != null){ String newType = override.getProperty(systemType); if (newType != null){ systemType = newType; } } } if (null != __configuration){ // system type must have been empty above __entryParser = __parserFactory.createFileEntryParser(new FTPClientConfig(systemType, __configuration)); }else{ __entryParser = __parserFactory.createFileEntryParser(systemType); } __entryParserKey = systemType; } } } } /** * private method through which all listFiles() and * initiateListParsing methods pass once a parser is determined. * * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @see FTPListParseEngine */ private FTPListParseEngine initiateListParsing(FTPFileEntryParser parser,String pathname) throws IOException{ Socket socket = _openDataConnection_(FTPCmd.LIST, getListArguments(pathname)); FTPListParseEngine engine = new FTPListParseEngine(parser, __configuration); if (socket == null){ return engine; } try{ engine.readServerList(socket.getInputStream(), getControlEncoding()); }finally{ Util.closeQuietly(socket); } completePendingCommand(); return engine; } /** * Initiate list parsing for MLSD listings. * * @param pathname * @return the engine * @throws IOException */ private FTPListParseEngine initiateMListParsing(String pathname) throws IOException{ Socket socket = _openDataConnection_(FTPCmd.MLSD, pathname); FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance(), __configuration); if (socket == null){ return engine; } try{ engine.readServerList(socket.getInputStream(), getControlEncoding()); }finally{ Util.closeQuietly(socket); completePendingCommand(); } return engine; } /** * @param pathname * the initial pathname * @return the adjusted string with "-a" added if necessary * @since 2.0 */ protected String getListArguments(String pathname){ if (getListHiddenFiles()){ if (pathname != null){ StringBuilder sb = new StringBuilder(pathname.length() + 3); sb.append("-a "); sb.append(pathname); return sb.toString(); } return "-a"; } return pathname; } /** * Issue the FTP STAT command to the server. * * @return The status information returned by the server. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String getStatus() throws IOException{ if (FTPReply.isPositiveCompletion(stat())){ return getReplyString(); } return null; } /** * Issue the FTP STAT command to the server for a given pathname. This * should produce a listing of the file or directory. * * @param pathname * the filename * * @return The status information returned by the server. * @throws FTPConnectionClosedException * If the FTP server prematurely closes the connection as a result * of the client being idle or some other reason causing the server * to send FTP reply code 421. This exception may be caught either * as an IOException or independently as itself. * @throws IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ public String getStatus(String pathname) throws IOException{ if (FTPReply.isPositiveCompletion(stat(pathname))){ return getReplyString(); } return null; } /** * Issue the FTP MDTM command (not supported by all servers) to retrieve the last * modification time of a file. The modification string should be in the * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in * GMT, but not all FTP servers honour this. * * @param pathname * The file path to query. * @return A string representing the last file modification time in YYYYMMDDhhmmss format. * @throws IOException * if an I/O error occurs. * @since 2.0 */ public String getModificationTime(String pathname) throws IOException{ if (FTPReply.isPositiveCompletion(mdtm(pathname))){ return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space } return null; } /** * Issue the FTP MDTM command (not supported by all servers) to retrieve the last * modification time of a file. The modification string should be in the * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in * GMT, but not all FTP servers honour this. * * @param pathname * The file path to query. * @return A FTPFile representing the last file modification time, may be {@code null}. * The FTPFile timestamp will be null if a parse error occurs. * @throws IOException * if an I/O error occurs. * @since 3.4 */ public FTPFile mdtmFile(String pathname) throws IOException{ if (FTPReply.isPositiveCompletion(mdtm(pathname))){ String reply = getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space FTPFile file = new FTPFile(); file.setName(pathname); file.setRawListing(reply); file.setTimestamp(MLSxEntryParser.parseGMTdateTime(reply)); return file; } return null; } /** * Issue the FTP MFMT command (not supported by all servers) which sets the last * modified time of a file. * * The timestamp should be in the form YYYYMMDDhhmmss. It should also * be in GMT, but not all servers honour this. * * An FTP server would indicate its support of this feature by including "MFMT" * in its response to the FEAT command, which may be retrieved by FTPClient.features() * * @param pathname * The file path for which last modified time is to be changed. * @param timeval * The timestamp to set to, in YYYYMMDDhhmmss format. * @return true if successfully set, false if not * @throws IOException * if an I/O error occurs. * @since 2.2 * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 */ public boolean setModificationTime(String pathname,String timeval) throws IOException{ return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval))); } /** * Set the internal buffer size for buffered data streams. * * @param bufSize * The size of the buffer. Use a non-positive value to use the default. */ public void setBufferSize(int bufSize){ __bufferSize = bufSize; } /** * Retrieve the current internal buffer size for buffered data streams. * * @return The current buffer size. */ public int getBufferSize(){ return __bufferSize; } /** * Sets the value to be used for the data socket SO_SNDBUF option. * If the value is positive, the option will be set when the data socket has been created. * * @param bufSize * The size of the buffer, zero or negative means the value is ignored. * @since 3.3 */ public void setSendDataSocketBufferSize(int bufSize){ __sendDataSocketBufferSize = bufSize; } /** * Retrieve the value to be used for the data socket SO_SNDBUF option. * * @return The current buffer size. * @since 3.3 */ public int getSendDataSocketBufferSize(){ return __sendDataSocketBufferSize; } /** * Sets the value to be used for the data socket SO_RCVBUF option. * If the value is positive, the option will be set when the data socket has been created. * * @param bufSize * The size of the buffer, zero or negative means the value is ignored. * @since 3.3 */ public void setReceieveDataSocketBufferSize(int bufSize){ __receiveDataSocketBufferSize = bufSize; } /** * Retrieve the value to be used for the data socket SO_RCVBUF option. * * @return The current buffer size. * @since 3.3 */ public int getReceiveDataSocketBufferSize(){ return __receiveDataSocketBufferSize; } /** * Implementation of the {@link Configurable Configurable} interface. * In the case of this class, configuring merely makes the config object available for the * factory methods that construct parsers. * * @param config * {@link FTPClientConfig FTPClientConfig} object used to * provide non-standard configurations to the parser. * @since 1.4 */ @Override public void configure(FTPClientConfig config){ this.__configuration = config; } /** * You can set this to true if you would like to get hidden files when {@link #listFiles} too. * A LIST -a will be issued to the ftp server. * It depends on your ftp server if you need to call this method, also dont expect to get rid * of hidden files if you call this method with "false". * * @param listHiddenFiles * true if hidden files should be listed * @since 2.0 */ public void setListHiddenFiles(boolean listHiddenFiles){ this.__listHiddenFiles = listHiddenFiles; } /** * @see #setListHiddenFiles(boolean) * @return the current state * @since 2.0 */ public boolean getListHiddenFiles(){ return this.__listHiddenFiles; } /** * Whether should attempt to use EPSV with IPv4. * Default (if not set) is false * * @return true if should attempt EPSV * @since 2.2 */ public boolean isUseEPSVwithIPv4(){ return __useEPSVwithIPv4; } /** * Set whether to use EPSV with IPv4. * Might be worth enabling in some circumstances. * * For example, when using IPv4 with NAT it * may work with some rare configurations. * E.g. if FTP server has a static PASV address (external network) * and the client is coming from another internal network. * In that case the data connection after PASV command would fail, * while EPSV would make the client succeed by taking just the port. * * @param selected * value to set. * @since 2.2 */ public void setUseEPSVwithIPv4(boolean selected){ this.__useEPSVwithIPv4 = selected; } /** * Set the listener to be used when performing store/retrieve operations. * The default value (if not set) is {@code null}. * * @param listener * to be used, may be {@code null} to disable * @since 3.0 */ public void setCopyStreamListener(CopyStreamListener listener){ __copyStreamListener = listener; } /** * Obtain the currently active listener. * * @return the listener, may be {@code null} * @since 3.0 */ public CopyStreamListener getCopyStreamListener(){ return __copyStreamListener; } /** * Set the time to wait between sending control connection keepalive messages * when processing file upload or download. * * @param controlIdle * the wait (in secs) between keepalive messages. Zero (or less) disables. * @since 3.0 * @see #setControlKeepAliveReplyTimeout(int) */ public void setControlKeepAliveTimeout(long controlIdle){ __controlKeepAliveTimeout = controlIdle * 1000; } /** * Get the time to wait between sending control connection keepalive messages. * * @return the number of seconds between keepalive messages. * @since 3.0 */ public long getControlKeepAliveTimeout(){ return __controlKeepAliveTimeout / 1000; } /** * Set how long to wait for control keep-alive message replies. * * @param timeout * number of milliseconds to wait (defaults to 1000) * @since 3.0 * @see #setControlKeepAliveTimeout(long) */ public void setControlKeepAliveReplyTimeout(int timeout){ __controlKeepAliveReplyTimeout = timeout; } /** * Get how long to wait for control keep-alive message replies. * * @return wait time in msec * @since 3.0 */ public int getControlKeepAliveReplyTimeout(){ return __controlKeepAliveReplyTimeout; } /** * Enable or disable passive mode NAT workaround. * If enabled, a site-local PASV mode reply address will be replaced with the * remote host address to which the PASV mode request was sent * (unless that is also a site local address). * This gets around the problem that some NAT boxes may change the * reply. * * The default is true, i.e. site-local replies are replaced. * * @param enabled * true to enable replacing internal IP's in passive * mode. * @deprecated (3.6) use {@link #setPassiveNatWorkaroundStrategy(HostnameResolver)} instead */ @Deprecated public void setPassiveNatWorkaround(boolean enabled){ if (enabled){ this.__passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); }else{ this.__passiveNatWorkaroundStrategy = null; } } /** * Set the workaround strategy to replace the PASV mode reply addresses. * This gets around the problem that some NAT boxes may change the reply. * * The default implementation is {@code NatServerResolverImpl}, i.e. site-local * replies are replaced. * * @param resolver * strategy to replace internal IP's in passive mode * or null to disable the workaround (i.e. use PASV mode reply address.) * @since 3.6 */ public void setPassiveNatWorkaroundStrategy(HostnameResolver resolver){ this.__passiveNatWorkaroundStrategy = resolver; } /** * Strategy interface for updating host names received from FTP server * for passive NAT workaround. * * @since 3.6 */ public interface HostnameResolver{ String resolve(String hostname) throws UnknownHostException; } /** * Default strategy for passive NAT workaround (site-local * replies are replaced.) * * @since 3.6 */ public static class NatServerResolverImpl implements HostnameResolver{ private final FTPClient client; public NatServerResolverImpl(FTPClient client){ this.client = client; } @Override public String resolve(String hostname) throws UnknownHostException{ String newHostname = hostname; InetAddress host = InetAddress.getByName(newHostname); // reply is a local address, but target is not - assume NAT box changed the PASV reply if (host.isSiteLocalAddress()){ InetAddress remote = this.client.getRemoteAddress(); if (!remote.isSiteLocalAddress()){ newHostname = remote.getHostAddress(); } } return newHostname; } } private OutputStream getBufferedOutputStream(OutputStream outputStream){ if (__bufferSize > 0){ return new BufferedOutputStream(outputStream, __bufferSize); } return new BufferedOutputStream(outputStream); } private InputStream getBufferedInputStream(InputStream inputStream){ if (__bufferSize > 0){ return new BufferedInputStream(inputStream, __bufferSize); } return new BufferedInputStream(inputStream); } // @since 3.0 private static class CSL implements CopyStreamListener{ private final FTPClient parent; private final long idle; private final int currentSoTimeout; private long time = System.currentTimeMillis(); private int notAcked; CSL(FTPClient parent, long idleTime, int maxWait) throws SocketException{ this.idle = idleTime; this.parent = parent; this.currentSoTimeout = parent.getSoTimeout(); parent.setSoTimeout(maxWait); } @Override public void bytesTransferred(CopyStreamEvent event){ bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); } @Override public void bytesTransferred(long totalBytesTransferred,int bytesTransferred,long streamSize){ long now = System.currentTimeMillis(); if (now - time > idle){ try{ parent.__noop(); }catch (SocketTimeoutException e){ notAcked++; }catch (IOException e){ // Ignored } time = now; } } void cleanUp() throws IOException{ try{ while (notAcked-- > 0){ parent.__getReplyNoReport(); } }finally{ parent.setSoTimeout(currentSoTimeout); } } } /** * Merge two copystream listeners, either or both of which may be null. * * @param local * the listener used by this class, may be null * @return a merged listener or a single listener or null * @since 3.0 */ private CopyStreamListener __mergeListeners(CopyStreamListener local){ if (local == null){ return __copyStreamListener; } if (__copyStreamListener == null){ return local; } // Both are non-null CopyStreamAdapter merged = new CopyStreamAdapter(); merged.addCopyStreamListener(local); merged.addCopyStreamListener(__copyStreamListener); return merged; } /** * Enables or disables automatic server encoding detection (only UTF-8 supported). *

* Does not affect existing connections; must be invoked before a connection is established. * * @param autodetect * If true, automatic server encoding detection will be enabled. */ public void setAutodetectUTF8(boolean autodetect){ __autodetectEncoding = autodetect; } /** * Tells if automatic server encoding detection is enabled or disabled. * * @return true, if automatic server encoding detection is enabled. */ public boolean getAutodetectUTF8(){ return __autodetectEncoding; } // Method for use by unit test code only FTPFileEntryParser getEntryParser(){ return __entryParser; } // DEPRECATED METHODS - for API compatibility only - DO NOT USE /** * @return the name * @throws IOException * on error * @deprecated use {@link #getSystemType()} instead */ @Deprecated public String getSystemName() throws IOException{ if (__systemName == null && FTPReply.isPositiveCompletion(syst())){ __systemName = _replyLines.get(_replyLines.size() - 1).substring(4); } return __systemName; } } /* * Emacs configuration * Local variables: ** * mode: java ** * c-basic-offset: 4 ** * indent-tabs-mode: nil ** * End: ** */ /* kate: indent-width 4; replace-tabs on; */





© 2015 - 2024 Weber Informatics LLC | Privacy Policy