/* * Copyright 2010 netling project <http://netling.org> * * Licensed 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. * * This file may incorporate work covered by the following copyright and * permission notice: * * 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 org.netling.ftp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Inet6Address; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Random; import org.netling.ftp.FTP.FileFormat; import org.netling.ftp.FTP.FileStructure; import org.netling.ftp.FTP.FileTransferMode; import org.netling.ftp.FTP.FileType; import org.netling.ftp.parser.DefaultFTPFileEntryParserFactory; import org.netling.ftp.parser.FTPFileEntryParserFactory; import org.netling.ftp.parser.ParserInitializationException; import org.netling.io.CopyStreamException; import org.netling.io.FromNetASCIIInputStream; import org.netling.io.StreamCopier; import org.netling.io.ToNetASCIIOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /*** * 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 org.netling.SocketClient}, * you must first connect to the server with * {@link org.netling.SocketClient#connect connect } * before doing anything, and finally * {@link org.netling.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: * <pre> * boolean error = false; * try { * int reply; * ftp.connect("ftp.foobar.com"); * 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); * } * </pre> * <p> * 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 org.netling.ftp.FTP#getReplyCode getReplyCode } after * a success or failure. * <p> * The default settings for FTPClient are for it to use * <code> {@link FileType#ASCII} </code>, * <code> {@link FileFormat#NON_PRINT_TEXT} </code>, * <code> {@link FileTransferMode#STREAM}</code>, and * <code> {@link FileStructure#FILE}</code>. The only file types directly supported * are <code> {@link FileType#ASCII} </code> and * <code> {@link FileType#BINARY} </code>. 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. * <p> * <code> {@link FileFormat#NON_PRINT_TEXT} </code>, * <code> {@link FileTransferMode#STREAM} </code>, and * <code> {@link FileStructure#FILE} </code> are the only supported formats, * transfer modes, and file structures. * <p> * 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. * <p> * 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()}. * <p> * 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 org.netling.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 org.netling.ftp.FTPConnectionClosedException} * . * <code>FTPConnectionClosedException</code> * is a subclass of <code> IOException </code> 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 <code> IOException </code> * catch block. When you encounter an * {@link org.netling.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 org.netling.ftp.FTP#getReplyCode getReplyCode }, * {@link org.netling.ftp.FTP#getReplyString getReplyString }, * and * {@link org.netling.ftp.FTP#getReplyStrings getReplyStrings}. * You may avoid server disconnections while the client is idle by * periodically sending NOOP commands to the server. * <p> * 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 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. * <p> * Listing API Examples * Both paged and unpaged examples of directory listings are available, * as follows: * <p> * Unpaged (whole list) access, using a parser accessible by auto-detect: * <pre> * FTPClient f = new FTPClient(); * f.connect(server); * f.login(username, password); * FTPFile[] files = listFiles(directory); * </pre> * <p> * Paged access, using a parser not accessible by auto-detect. The class * defined in the first parameter of initateListParsing should be derived * from org.netling.FTPFileEntryParser: * <pre> * 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. * } * </pre> * <p> * Paged access, using a parser accessible by auto-detect: * <pre> * 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. * } * </pre> * <p> * For examples of using FTPClient on servers whose directory listings * <ul> * <li>use languages other than English</li> * <li>use date formats other than the American English "standard" <code>MM d yyyy</code></li> * <li>are in different timezones and you need accurate timestamps for dependency checking * as in Ant</li> * </ul>see {@link FTPClientConfig FTPClientConfig}. * <p> * @see FTP * @see FTPConnectionClosedException * @see FTPFileEntryParser * @see FTPFileEntryParserFactory * @see DefaultFTPFileEntryParserFactory * @see FTPClientConfig * * @see MalformedServerReplyException **/ public class FTPClient extends FTP implements Configurable { /** Logger */ protected final Logger log = LoggerFactory.getLogger(getClass()); enum ConnectionMode { /*** * 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. ***/ ACTIVE_LOCAL, /*** * 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. ***/ ACTIVE_REMOTE, /*** * 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. ***/ PASSIVE_LOCAL, /*** * 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. ***/ PASSIVE_REMOTE; } private ConnectionMode dataConnectionMode; private int dataTimeout; private int passivePort; private String passiveHost; private final Random random; private int activeMinPort, activeMaxPort; private InetAddress activeExternalHost; FileType fileType; FileFormat fileFormat; private FileTransferMode fileTransferMode; FileStructure fileStructure; private boolean remoteVerificationEnabled; private long restartOffset; private FTPFileEntryParserFactory parserFactory; private int bufferSize; 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; /** Pattern for PASV mode responses */ private static final String pasvParameters = "\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}"; private static final java.util.regex.Pattern pasvPattern; static { pasvPattern = java.util.regex.Pattern.compile(pasvParameters); } /*** * Default FTPClient constructor. Creates a new FTPClient instance * with the data connection mode set to * <code> ACTIVE_LOCAL_DATA_CONNECTION_MODE </code>, the file type * set to <code> FTP.ASCII_FILE_TYPE </code>, the * file format set to <code> FTP.NON_PRINT_TEXT_FORMAT </code>, * the file structure set to <code> FTP.FILE_STRUCTURE </code>, and * the transfer mode set to <code> FTP.STREAM_TRANSFER_MODE </code>. ***/ public FTPClient() { initDefaults(); dataTimeout = -1; remoteVerificationEnabled = true; parserFactory = new DefaultFTPFileEntryParserFactory(); configuration = null; listHiddenFiles = false; useEPSVwithIPv4 = false; random = new Random(); } private void initDefaults() { dataConnectionMode = ConnectionMode.ACTIVE_LOCAL; passiveHost = null; passivePort = -1; activeExternalHost = null; activeMinPort = 0; activeMaxPort = 0; fileType = FTP.FileType.ASCII; fileStructure = FTP.FileStructure.FILE; fileFormat = FTP.FileFormat.NON_PRINT_TEXT; fileTransferMode = FTP.FileTransferMode.STREAM; restartOffset = 0; systemName = null; entryParser = null; entryParserKey = ""; bufferSize = 1024; } private String parsePathname(String reply) { int begin, end; begin = reply.indexOf('"') + 1; end = reply.indexOf('"', begin); return reply.substring(begin, end); } void parsePassiveModeReply(String reply) throws MalformedServerReplyException { java.util.regex.Matcher m = pasvPattern.matcher(reply); if (!m.find()) { throw new MalformedServerReplyException( "Could not parse passive host information.\nServer Reply: " + reply); } reply = m.group(); String parts[] = m.group().split(","); passiveHost = parts[0] + '.' + parts[1] + '.' + parts[2] + '.' + parts[3]; try { int oct1 = Integer.parseInt(parts[4]); int oct2 = Integer.parseInt(parts[5]); passivePort = (oct1 << 8) | oct2; } catch (NumberFormatException e) { throw new MalformedServerReplyException( "Could not parse passive host information.\nServer Reply: " + reply); } } void parseExtendedPassiveModeReply(String reply) throws MalformedServerReplyException { int port; 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); 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(FTPCommand command, String remote, InputStream local) throws IOException { OutputStream output; Socket socket; if ((socket = openDataConnection(command, remote)) == null) return false; output = new BufferedOutputStream(socket.getOutputStream(), getBufferSize() ); if (fileType == FileType.ASCII) output = new ToNetASCIIOutputStream(output); // Treat everything else as binary for now try { StreamCopier.copy(local, output, getBufferSize(), false); } catch (IOException e) { try { socket.close(); } catch (IOException f) {} throw e; } output.close(); socket.close(); return completePendingCommand(); } private OutputStream storeFileStream(FTPCommand command, String remote) throws IOException { OutputStream output; Socket socket; if ((socket = openDataConnection(command, remote)) == null) return null; output = socket.getOutputStream(); if (fileType == FileType.ASCII) { // 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 BufferedOutputStream(output, getBufferSize()); output = new ToNetASCIIOutputStream(output); } return new org.netling.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. * <p> * @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. * @exception IOException If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. */ protected Socket openDataConnection(FTPCommand command, String arg) throws IOException { Socket socket; if (dataConnectionMode != ConnectionMode.ACTIVE_LOCAL && dataConnectionMode != ConnectionMode.PASSIVE_LOCAL) return null; final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; if (dataConnectionMode == ConnectionMode.ACTIVE_LOCAL) { // 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 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(getHostAddress(), server.getLocalPort()))) { server.close(); return null; } } else { if (!FTPReply.isPositiveCompletion(port(getHostAddress(), server.getLocalPort()))) { server.close(); return null; } } if ((restartOffset > 0) && !restart(restartOffset)) { server.close(); return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { server.close(); 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); try { socket = server.accept(); } 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.code()) { 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.code()) { return null; } parsePassiveModeReply(replyLines.get(0)); } socket = socketFactory.createSocket(passiveHost, passivePort); if ((restartOffset > 0) && !restart(restartOffset)) { socket.close(); return null; } if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { socket.close(); return null; } } if (remoteVerificationEnabled && !verifyRemote(socket)) { InetAddress host1, host2; host1 = socket.getInetAddress(); host2 = getRemoteAddress(); socket.close(); throw new IOException( "Host attempting data connection " + host1.getHostAddress() + " is not same as server " + host2.getHostAddress()); } if (dataTimeout >= 0) socket.setSoTimeout(dataTimeout); return socket; } @Override protected void onConnect() throws IOException { super.onConnect(); initDefaults(); } /*** * Sets the timeout in milliseconds to use when reading from the * data connection. This timeout will be set immediately after * opening the data connection. * <p> * @param timeout The default timeout in milliseconds that is used when * opening a data connection socket. ***/ 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 org.netling.ftp.parser.FTPFileEntryParserFactory * @see org.netling.ftp.parser.DefaultFTPFileEntryParserFactory */ public void setParserFactory(FTPFileEntryParserFactory parserFactory) { this.parserFactory = parserFactory; } /*** * Closes the connection to the FTP server and restores * connection parameters to the default values. * <p> * @exception 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. * <p> * @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. * <p> * @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. * <p> * @param username The username to login under. * @param password The password to use. * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @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. * @exception 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. * @exception 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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @param pathname The new current working directory. * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @param pathname The pathname to mount. * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception IOException If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. ***/ 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 * <code>ACTIVE_LOCAL_DATA_CONNECTION_MODE</code>. 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 = ConnectionMode.ACTIVE_LOCAL; passiveHost = null; passivePort = -1; } /*** * Set the current data connection mode to * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code>. 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 * <code> PASSIVE_LOCAL_DATA_CONNECTION_MODE </code> until the * mode is changed by calling some other method such as * {@link #enterLocalActiveMode enterLocalActiveMode() } * <p> * <b>N.B.</b> currently calling any connect method will reset the mode to * ACTIVE_LOCAL_DATA_CONNECTION_MODE. ***/ public void enterLocalPassiveMode() { dataConnectionMode = ConnectionMode.PASSIVE_LOCAL; // 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 * <code> ACTIVE_REMOTE_DATA_CONNECTION </code>. 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. * <p> * @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. * @exception 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. * @exception 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 = ConnectionMode.ACTIVE_REMOTE; passiveHost = null; passivePort = -1; return true; } return false; } /*** * Set the current data connection mode to * <code> PASSIVE_REMOTE </code>. 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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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.code()) return false; dataConnectionMode = ConnectionMode.PASSIVE_REMOTE; 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()}. * <p> * @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()}. * <p> * @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 (A {@link ConnectionMode} instance) * <p> * @return The current data connection mode ***/ public ConnectionMode getDataConnectionMode() { return dataConnectionMode; } /** * Get the client port for active mode. * <p> * @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; } else { // default port return 0; } } /** * Get the host address for active mode. * <p> * @return The host address for active mode. */ private InetAddress getHostAddress() { if (activeExternalHost != null) { return activeExternalHost; } else { // default local address return getLocalAddress(); } } /*** * Set the client side port range in active mode. * <p> * @param minPort The lowest available port (inclusive). * @param maxPort The highest available port (inclusive). ***/ 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. * <p> * @param ipAddress The external IP address of this machine. * @throws UnknownHostException ***/ public void setActiveExternalIPAddress(String ipAddress) throws UnknownHostException { this.activeExternalHost = InetAddress.getByName(ipAddress); } /*** * Sets the file type to be transferred. This should be one of * <code> {@link FileType#ASCII} </code>, <code> {@link FileType#BINARY}</code>, * 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 <code> {@link FileType#ASCII}</code> * if this method is never called. * <p> * <b>N.B.</b> currently calling any connect method will reset the mode to * {@link ConnectionMode#ACTIVE_LOCAL} * @param fileType {@link FileType} instance indicating the * type of file. * @return True if successfully completed, false if not. * @exception 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. * @exception 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(FileType fileType) throws IOException { if (FTPReply.isPositiveCompletion(type(fileType))) { this.fileType = fileType; fileFormat = FileFormat.NON_PRINT_TEXT; return true; } return false; } /*** * Sets the file type to be transferred. This should be one of * <code> {@link FileType#ASCII} </code>, <code> {@link FileType#BINARY}</code>, * 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 <code> {@link FileType#ASCII}</code> * if this method is never called. * The format should be one of the FTP class <code> {@link FileFormat} </code> * constants. The default format * is <code> {@link FileFormat#NON_PRINT_TEXT} </code> if this method is never * called. * <p> * <b>N.B.</b> currently calling any connect method will reset the mode to * {@link ConnectionMode#ACTIVE_LOCAL}. * <p> * @param fileType The <code> {@link FileType} </code> constant indicating the * type of file. * @param format The format of the file. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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(FileType fileType, FileFormat format) throws IOException { if (fileType == FileType.LOCAL) throw new IllegalArgumentException("setFileType(FileType,FileFormat) should not be called for FileType LOCAL"); if (FTPReply.isPositiveCompletion(type(fileType, format))) { this.fileType = fileType; fileFormat = format; return true; } return false; } /*** * Sets the file type to be transferred and the format. The type should be * <code> {@link FileType#LOCAL} </code>. * <p> * <b>N.B.</b> currently calling any connect method will reset the mode to * ACTIVE_LOCAL_DATA_CONNECTION_MODE. * <p> * @param fileType Should be <code> {@link FileType#LOCAL} </code>. * @param sizeBytes The byte size. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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(FileType fileType, int sizeBytes) throws IOException { if (fileType != FileType.LOCAL) throw new IllegalArgumentException("setFileType(FileType,int) should only be called for FileType LOCAL"); if (FTPReply.isPositiveCompletion(type(fileType, sizeBytes))) { this.fileType = fileType; return true; } return false; } /*** * Sets the file structure. The default structure is * <code> FTP.FILE_STRUCTURE </code> if this method is never called. * <p> * @param structure The structure of the file (one of the FTP class * <code>_STRUCTURE</code> constants). * @return True if successfully completed, false if not. * @exception 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. * @exception 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(FileStructure structure) throws IOException { if (FTPReply.isPositiveCompletion(stru(structure))) { fileStructure = structure; return true; } return false; } /*** * Sets the transfer mode. The default transfer mode * <code> FTP.STREAM_TRANSFER_MODE </code> if this method is never called. * <p> * @param mode The new transfer mode to use (one of the FTP class * <code>_TRANSFER_MODE</code> constants). * @return True if successfully completed, false if not. * @exception 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. * @exception 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(FileTransferMode 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. * <p> * @param filename The name of the file to retrieve. * @return True if successfully completed, false if not. * @exception 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. * @exception 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 == ConnectionMode.ACTIVE_REMOTE || dataConnectionMode == ConnectionMode.PASSIVE_REMOTE) 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 <code> remoteRetrieve </code> issued to it by another * FTPClient. * <p> * @param filename The name to call the file that is to be stored. * @return True if successfully completed, false if not. * @exception 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. * @exception 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 == ConnectionMode.ACTIVE_REMOTE || dataConnectionMode == ConnectionMode.PASSIVE_REMOTE) 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 <code> remoteRetrieve </code> issued * to it by another FTPClient. * <p> * @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. * @exception 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. * @exception 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 == ConnectionMode.ACTIVE_REMOTE || dataConnectionMode == ConnectionMode.PASSIVE_REMOTE) 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 <code> remoteRetrieve </code> 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 <code> remoteStoreUnique</code> * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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 == ConnectionMode.ACTIVE_REMOTE || dataConnectionMode == ConnectionMode.PASSIVE_REMOTE) 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 * <code> remoteRetrieve </code> issued to it by another FTPClient. * <p> * @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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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 == ConnectionMode.ACTIVE_REMOTE || dataConnectionMode == ConnectionMode.PASSIVE_REMOTE) 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. * <p> * For example, * <pre> * 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); * } * </pre> * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * 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. * @exception 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. * @exception 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. * @exception 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 { InputStream input; Socket socket; if ((socket = openDataConnection(FTPCommand.RETR, remote)) == null) return false; input = new BufferedInputStream(socket.getInputStream(), getBufferSize()); if (fileType == FileType.ASCII) input = new FromNetASCIIInputStream(input); // Treat everything else as binary for now try { StreamCopier.copy(input, local, getBufferSize(), false); } catch (IOException e) { try { socket.close(); } catch (IOException f) {} throw e; } socket.close(); return completePendingCommand(); } /** * Convenience method to download a file. * @param remote The remote filename * @param local The local filename * @return True if the operation succeeded, false if not */ public boolean download(String remote, String local) throws IOException { OutputStream fos; try { fos = new FileOutputStream(local); } catch (FileNotFoundException e) { throw new IOException("Could not create file", e); } boolean success = retrieveFile(remote, fos); fos.close(); return success; } /*** * 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. * <p> * 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). * @exception 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. * @exception 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 { InputStream input; Socket socket; if ((socket = openDataConnection(FTPCommand.RETR, remote)) == null) return null; input = socket.getInputStream(); if (fileType == FileType.ASCII) { // 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 BufferedInputStream(input, getBufferSize()); input = new FromNetASCIIInputStream(input); } return new org.netling.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). * <p> * @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. * @exception 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. * @exception 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. * @exception 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(FTPCommand.STOR, remote, local); } /** * Convenience method to store a file on the server. Wraps the underlying {@link #storeFile(String, InputStream)} * command and closes the associated input stream. * @param remote The remote filename * @param local The local filename * @return True if the operation succeeded, false otherwise * @throws IOException if an error happens during the remote upload */ public boolean upload(String remote, String local) throws IOException { final File localFile = new File(local); InputStream is = new FileInputStream(localFile); boolean success = storeFile(remote, is); is.close(); return success; } /*** * 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. * <p> * @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). * @exception 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. * @exception 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(FTPCommand.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). * <p> * @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. * @exception 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. * @exception 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. * @exception 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(FTPCommand.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. * <p> * @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). * @exception 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. * @exception 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(FTPCommand.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). * <p> * @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. * @exception 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. * @exception 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. * @exception 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(FTPCommand.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. * <p> * @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). * @exception 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. * @exception 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(FTPCommand.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). * <p> * @param local The local InputStream from which to read the file. * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * @exception 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(FTPCommand.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. * <p> * @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). * @exception 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. * @exception 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(FTPCommand.STOU, null); } /*** * Reserve a number of bytes on the server for the next file transfer. * <p> * @param bytes The number of bytes which the server should allocate. * @return True if successfully completed, false if not. * @exception 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. * @exception 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 2289): * <pre> C> feat S> 211-Extensions supported: S> MLST size*;create;modify*;perm;media-type S> SIZE S> COMPRESSION S> MDTM S> 211 END * </pre> * @see <a href="http://www.faqs.org/rfcs/rfc2389.html">http://www.faqs.org/rfcs/rfc2389.html</a> * @return True if successfully completed, false if not. * @throws IOException */ public boolean features() throws IOException { return FTPReply.isPositiveCompletion(feat()); } /** * Reserve space on the server for the next file transfer. * <p> * @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. * @exception 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. * @exception 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)); } /*** * Restart a <code>STREAM_TRANSFER_MODE</code> 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. * <p> * @param offset The offset into the remote file at which to start the * next file transfer. * @return True if successfully completed, false if not. * @exception 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. * @exception IOException If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. ***/ private boolean restart(long offset) throws IOException { restartOffset = 0; return FTPReply.isPositiveIntermediate(rest(Long.toString(offset))); } /*** * Sets the restart offset. The restart command is sent to the server * only before sending the file transfer command. When this is done, * the restart marker is reset to zero. * <p> * @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. * <p> * @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. * <p> * @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. * @exception 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. * @exception 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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @param pathname The pathname of the file to be deleted. * @return True if successfully completed, false if not. * @exception 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. * @exception 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). * <p> * @param pathname The pathname of the directory to remove. * @return True if successfully completed, false if not. * @exception 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. * @exception 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). * <p> * @param pathname The pathname of the directory to create. * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @return The pathname of the current working directory. If it cannot * be obtained, returns null. * @exception 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. * @exception 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.code()) 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. * @exception 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. * @exception 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 name 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. * <p> * @return The system type name obtained from the server. null if the * information could not be obtained. * @exception 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. * @exception IOException If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. ***/ public String getSystemName() 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 && FTPReply.isPositiveCompletion(syst())) systemName = replyLines.get(replyLines.size() - 1).substring(4); return systemName; } /*** * Fetches the system help information from the server and returns the * full string. * <p> * @return The system help string obtained from the server. null if the * information could not be obtained. * @exception 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. * @exception 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. * @exception 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. * @exception 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. * <p> * @return True if successfully completed, false if not. * @exception 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. * @exception 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. * <p> * @param pathname The file or directory to list. * @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. * @exception 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. * @exception 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 { String line; Socket socket; BufferedReader reader; ArrayList<String> results; if ((socket = openDataConnection(FTPCommand.NLST, pathname)) == null) return null; reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); results = new ArrayList<String>(); 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. * <p> * @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. * @exception 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. * @exception 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. * <p> * This information is obtained through the LIST command. The contents of * the returned array is determined by the<code> FTPFileEntryParser </code> * used. * <p> * @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. * * @return The list of file information contained in the given path in * the format determined by the autodetection mechanism * @exception 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. * @exception IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply * from the server. * @exception 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.netling.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 org.netling.ftp.parser.DefaultFTPFileEntryParserFactory * @see org.netling.ftp.parser.FTPFileEntryParserFactory * @see org.netling.ftp.FTPFileEntryParser */ public FTPFile[] listFiles(String pathname) throws IOException { String key = null; FTPListParseEngine engine = initiateListParsing(key, pathname); return engine.getFiles(); } /** * Using the default system autodetect mechanism, obtain a * list of file information for the current working directory. * <p> * This information is obtained through the LIST command. The contents of * the returned array is determined by the<code> FTPFileEntryParser </code> * used. * <p> * @return The list of file information contained in the current directory * in the format determined by the autodetection mechanism. * <p><b> * NOTE:</b> 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. * @exception 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. * @exception IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply * from the server. * @exception 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.netling.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 org.netling.ftp.parser.DefaultFTPFileEntryParserFactory * @see org.netling.ftp.parser.FTPFileEntryParserFactory * @see org.netling.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: <code>listFiles("site", FTPFileFilters.DIRECTORY);</code> * @param pathname the initial path, may be null * @param filter the filter, non-null * @return the list of FTPFile entries. * @throws IOException */ public FTPFile[] listFiles(String pathname, FTPFileFilter filter) throws IOException { FTPListParseEngine engine = initiateListParsing((String) null, pathname); return engine.getFiles(filter); } /** * 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 * <code> FTPFileEntryParser </code> used. * <p> * 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 <code> parser </code> 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. * * @exception 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. * @exception IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @exception 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 * <code> FTPFileEntryParser </code> used. * <p> * 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. * <p> * 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. * <p> * <pre> * 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. * } * </pre> * * @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 <code> parser </code> 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. * * @exception 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. * @exception IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @exception 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 { String key = null; return initiateListParsing(key, 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 * <code> FTPFileEntryParser </code> used. * <p> * 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. * <p> * 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 <code> FTPFileEntryParser </code> that should be * used to parse each server file listing. * * @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 <code> parser </code> 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. * * @exception 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. * @exception IOException * If an I/O error occurs while either sending a * command to the server or receiving a reply from the server. * @exception 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.netling.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 { // We cache the value to avoid creation of a new object every // time a file listing is generated. if(entryParser == 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 non-null, use that. if (null != configuration) { entryParser = parserFactory.createFileEntryParser(configuration); entryParserKey = configuration.getServerSystemKey(); } else { // if a parserKey hasn't been supplied, and a configuration // hasn't been supplied, then autodetect by calling // the SYST command and use that to choose the parser. final String systemName = getSystemName(); entryParser = parserFactory.createFileEntryParser(systemName); entryParserKey = systemName; } } } return initiateListParsing(entryParser, pathname); } /** * private method through which all listFiles() and * initiateListParsing methods pass once a parser is determined. * * @exception 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. * @exception 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; FTPListParseEngine engine = new FTPListParseEngine(parser); if ((socket = openDataConnection(FTPCommand.LIST, getListArguments(pathname))) == null) { return engine; } try { engine.readServerList(socket.getInputStream(), getControlEncoding()); } finally { socket.close(); } completePendingCommand(); return engine; } /** * Get extra arguments to the <code>LIST</code> command */ 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(); } else { return "-a"; } } return pathname; } /*** * Issue the FTP STAT command to the server. * <p> * @return The status information returned by the server. * @exception 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. * @exception 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. * <p> * @return The status information returned by the server. * @exception 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. * @exception 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 <code>YYYYMMDDhhmmss</code> format. * @throws IOException if an I/O error occurs. * */ public String getModificationTime(String pathname) throws IOException { if (FTPReply.isPositiveCompletion(mdtm(pathname))) return getReplyString(); 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 <code>YYYYMMDDhhmmss</code>. 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 <code>YYYYMMDDhhmmss</code> format. * @return true if successfully set, false if not * @throws IOException if an I/O error occurs. * @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a> */ public boolean setModificationTime(String pathname, String timeval) throws IOException { return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval))); } /** * Set the internal buffer size. * * @param bufSize The size of the buffer */ public void setBufferSize(int bufSize) { bufferSize = bufSize; } /** * Retrieve the current internal buffer size. * @return The current buffer size. */ public int getBufferSize() { return bufferSize; } /** * 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. * */ 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 <code>LIST -a</code> 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 * */ public void setListHiddenFiles(boolean listHiddenFiles) { this.listHiddenFiles = listHiddenFiles; } /** * @see #setListHiddenFiles(boolean) * @return the current state * */ public boolean getListHiddenFiles() { return this.listHiddenFiles; } /** * Whether should attempt to use EPSV with IPv4. * Default (if not set) is <code>false</code> * @return true if should attempt EPS */ 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. */ public void setUseEPSVwithIPv4(boolean selected) { this.useEPSVwithIPv4 = selected; } }