/** * * Java FTP client library. * * Copyright (C) 2000-2003 Enterprise Distributed Technologies Ltd * * www.enterprisedt.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Bug fixes, suggestions and comments should be sent to bruce@enterprisedt.com * * Change Log: * * $Log: FTPClient.java,v $ * Revision 1.1.1.1 2005/06/23 15:23:01 smontoro * hipergate backend * * Revision 1.1 2004/02/07 03:15:20 hipergate * v2.0 pre-alpha * * Revision 1.6 2003/05/31 14:53:44 bruceb * 1.2.2 changes * * Revision 1.5 2003/01/29 22:46:08 bruceb * minor changes * * Revision 1.4 2002/11/19 22:01:25 bruceb * changes for 1.2 * * Revision 1.3 2001/10/09 20:53:46 bruceb * Active mode changes * * Revision 1.1 2001/10/05 14:42:03 bruceb * moved from old project * */ package com.enterprisedt.net.ftp; import java.io.IOException; import java.io.LineNumberReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.FileInputStream; import java.io.File; import java.text.SimpleDateFormat; import java.text.ParsePosition; import java.net.InetAddress; import java.util.Date; import java.util.Vector; import java.util.Properties; /** * Supports client-side FTP. Most common * FTP operations are present in this class. * * @author Bruce Blackshaw * @version $Revision: 1.1.1.1 $ * */ public class FTPClient { /** * Revision control id */ private static String cvsId = "@(#)$Id: FTPClient.java,v 1.1.1.1 2005/06/23 15:23:01 smontoro Exp $"; /** * Format to interpret MTDM timestamp */ private SimpleDateFormat tsFormat = new SimpleDateFormat("yyyyMMddHHmmss"); /** * Socket responsible for controlling * the connection */ private FTPControlSocket control = null; /** * Socket responsible for transferring * the data */ private FTPDataSocket data = null; /** * Socket timeout for both data and control. In * milliseconds */ private int timeout = 0; /** * Record of the transfer type - make the default ASCII */ private FTPTransferType transferType = FTPTransferType.ASCII; /** * Record of the connect mode - make the default PASV (as this was * the original mode supported) */ private FTPConnectMode connectMode = FTPConnectMode.PASV; /** * Holds the last valid reply from the server on the control socket */ private FTPReply lastValidReply; /** * Constructor. Creates the control * socket * * @param remoteHost the remote hostname */ public FTPClient(String remoteHost) throws IOException, FTPException { control = new FTPControlSocket(remoteHost, FTPControlSocket.CONTROL_PORT, null, 0); } /** * Constructor. Creates the control * socket * * @param remoteHost the remote hostname * @param controlPort port for control stream */ public FTPClient(String remoteHost, int controlPort) throws IOException, FTPException { control = new FTPControlSocket(remoteHost, controlPort, null, 0); } /** * Constructor. Creates the control * socket * * @param remoteAddr the address of the * remote host */ public FTPClient(InetAddress remoteAddr) throws IOException, FTPException { control = new FTPControlSocket(remoteAddr, FTPControlSocket.CONTROL_PORT, null, 0); } /** * Constructor. Creates the control * socket. Allows setting of control port (normally * set by default to 21). * * @param remoteAddr the address of the * remote host * @param controlPort port for control stream */ public FTPClient(InetAddress remoteAddr, int controlPort) throws IOException, FTPException { control = new FTPControlSocket(remoteAddr, controlPort, null, 0); } /** * Constructor. Creates the control * socket * * @param remoteHost the remote hostname * @param millis the length of the timeout, in milliseconds */ public FTPClient(String remoteHost, PrintWriter log, int timeout) throws IOException, FTPException { control = new FTPControlSocket(remoteHost, FTPControlSocket.CONTROL_PORT, log, timeout); } /** * Constructor. Creates the control * socket * * @param remoteHost the remote hostname * @param controlPort port for control stream * @param millis the length of the timeout, in milliseconds */ public FTPClient(String remoteHost, int controlPort, PrintWriter log, int timeout) throws IOException, FTPException { control = new FTPControlSocket(remoteHost, controlPort, log, timeout); } /** * Constructor. Creates the control * socket * * @param remoteAddr the address of the * remote host * @param millis the length of the timeout, in milliseconds */ public FTPClient(InetAddress remoteAddr, PrintWriter log, int timeout) throws IOException, FTPException { control = new FTPControlSocket(remoteAddr, FTPControlSocket.CONTROL_PORT, log, timeout); } /** * Constructor. Creates the control * socket. Allows setting of control port (normally * set by default to 21). * * @param remoteAddr the address of the * remote host * @param controlPort port for control stream * @param millis the length of the timeout, in milliseconds */ public FTPClient(InetAddress remoteAddr, int controlPort, PrintWriter log, int timeout) throws IOException, FTPException { control = new FTPControlSocket(remoteAddr, controlPort, log, timeout); } /** * Set the TCP timeout on the underlying socket. * * If a timeout is set, then any operation which * takes longer than the timeout value will be * killed with a java.io.InterruptedException. We * set both the control and data connections * * @param millis The length of the timeout, in milliseconds */ public void setTimeout(int millis) throws IOException { this.timeout = millis; control.setTimeout(millis); } /** * Set the connect mode * * @param mode ACTIVE or PASV mode */ public void setConnectMode(FTPConnectMode mode) { connectMode = mode; } /** * Login into an account on the FTP server. This * call completes the entire login process * * @param user user name * @param password user's password */ public void login(String user, String password) throws IOException, FTPException { String response = control.sendCommand("USER " + user); lastValidReply = control.validateReply(response, "331"); response = control.sendCommand("PASS " + password); lastValidReply = control.validateReply(response, "230"); } /** * Supply the user name to log into an account * on the FTP server. Must be followed by the * password() method - but we allow for * * @param user user name * @param password user's password */ public void user(String user) throws IOException, FTPException { String reply = control.sendCommand("USER " + user); // we allow for a site with no password - 230 response String[] validCodes = {"230", "331"}; lastValidReply = control.validateReply(reply, validCodes); } /** * Supplies the password for a previously supplied * username to log into the FTP server. Must be * preceeded by the user() method * * @param user user name * @param password user's password */ public void password(String password) throws IOException, FTPException { String reply = control.sendCommand("PASS " + password); // we allow for a site with no passwords (202) String[] validCodes = {"230", "202"}; lastValidReply = control.validateReply(reply, validCodes); } /** * Set up SOCKS v4/v5 proxy settings. This can be used if there * is a SOCKS proxy server in place that must be connected thru. * Note that setting these properties directs <b>all</b> TCP * sockets in this JVM to the SOCKS proxy * * @param port SOCKS proxy port * @param host SOCKS proxy hostname */ public static void initSOCKS(String port, String host) { Properties props = System.getProperties(); props.put("socksProxyPort", port); props.put("socksProxyHost", host); System.setProperties(props); } /** * Set up SOCKS username and password for SOCKS username/password * authentication. Often, no authentication will be required * but the SOCKS server may be configured to request these. * * @param username the SOCKS username * @param password the SOCKS password */ public static void initSOCKSAuthentication(String username, String password) { Properties props = System.getProperties(); props.put("java.net.socks.username", username); props.put("java.net.socks.password", password); System.setProperties(props); } /** * Get the name of the remote host * * @return remote host name */ String getRemoteHostName() { return control.getRemoteHostName(); } /** * Issue arbitrary ftp commands to the FTP server. * * @param command ftp command to be sent to server * @param validCodes valid return codes for this command */ public void quote(String command, String[] validCodes) throws IOException, FTPException { String reply = control.sendCommand(command); // allow for no validation to be supplied if (validCodes != null && validCodes.length > 0) lastValidReply = control.validateReply(reply, validCodes); } /** * Put a local file onto the FTP server. It * is placed in the current directory. * * @param localPath path of the local file * @param remoteFile name of remote file in * current directory */ public void put(String localPath, String remoteFile) throws IOException, FTPException { put(localPath, remoteFile, false); } /** * Put a stream of data onto the FTP server. It * is placed in the current directory. * * @param srcStream input stream of data to put * @param remoteFile name of remote file in * current directory */ public void put(InputStream srcStream, String remoteFile) throws IOException, FTPException { put(srcStream, remoteFile, false); } /** * Put a local file onto the FTP server. It * is placed in the current directory. Allows appending * if current file exists * * @param localPath path of the local file * @param remoteFile name of remote file in * current directory * @param append true if appending, false otherwise */ public void put(String localPath, String remoteFile, boolean append) throws IOException, FTPException { // get according to set type if (getType() == FTPTransferType.ASCII) { putASCII(localPath, remoteFile, append); } else { putBinary(localPath, remoteFile, append); } validateTransfer(); } /** * Put a stream of data onto the FTP server. It * is placed in the current directory. Allows appending * if current file exists * * @param srcStream input stream of data to put * @param remoteFile name of remote file in * current directory * @param append true if appending, false otherwise */ public void put(InputStream srcStream, String remoteFile, boolean append) throws IOException, FTPException { // get according to set type if (getType() == FTPTransferType.ASCII) { putASCII(srcStream, remoteFile, append); } else { putBinary(srcStream, remoteFile, append); } validateTransfer(); } /** * Validate that the put() or get() was successful */ private void validateTransfer() throws IOException, FTPException { // check the control response String[] validCodes = {"226", "250"}; String reply = control.readReply(); lastValidReply = control.validateReply(reply, validCodes); } /** * Request the server to set up the put * * @param remoteFile name of remote file in * current directory * @param append true if appending, false otherwise */ private void initPut(String remoteFile, boolean append) throws IOException, FTPException { // set up data channel data = control.createDataSocket(connectMode); data.setTimeout(timeout); // send the command to store String cmd = append ? "APPE " : "STOR "; String reply = control.sendCommand(cmd + remoteFile); // Can get a 125 or a 150 String[] validCodes = {"125", "150"}; lastValidReply = control.validateReply(reply, validCodes); } /** * Put as ASCII, i.e. read a line at a time and write * inserting the correct FTP separator * * @param localPath full path of local file to read from * @param remoteFile name of remote file we are writing to * @param append true if appending, false otherwise */ private void putASCII(String localPath, String remoteFile, boolean append) throws IOException, FTPException { // create an inputstream & pass to common method InputStream srcStream = new FileInputStream(localPath); putASCII(srcStream, remoteFile, append); } /** * Put as ASCII, i.e. read a line at a time and write * inserting the correct FTP separator * * @param srcStream input stream of data to put * @param remoteFile name of remote file we are writing to * @param append true if appending, false otherwise */ private void putASCII(InputStream srcStream, String remoteFile, boolean append) throws IOException, FTPException { // need to read line by line ... LineNumberReader in = new LineNumberReader(new InputStreamReader(srcStream)); initPut(remoteFile, append); // get an character output stream to write to ... AFTER we // have the ok to go ahead AND AFTER we've successfully opened a // stream for the local file BufferedWriter out = new BufferedWriter( new OutputStreamWriter(data.getOutputStream())); // write line by line, writing \r\n as required by RFC959 after // each line String line = null; while ((line = in.readLine()) != null) { out.write(line, 0, line.length()); out.write(FTPControlSocket.EOL, 0, FTPControlSocket.EOL.length()); } in.close(); out.flush(); out.close(); // and close the data socket try { data.close(); } catch (IOException ignore) {} } /** * Put as binary, i.e. read and write raw bytes * * @param localPath full path of local file to read from * @param remoteFile name of remote file we are writing to * @param append true if appending, false otherwise */ private void putBinary(String localPath, String remoteFile, boolean append) throws IOException, FTPException { // open input stream to read source file ... do this // BEFORE opening output stream to server, so if file not // found, an exception is thrown InputStream srcStream = new FileInputStream(localPath); putBinary(srcStream, remoteFile, append); } /** * Put as binary, i.e. read and write raw bytes * * @param srcStream input stream of data to put * @param remoteFile name of remote file we are writing to * @param append true if appending, false otherwise */ private void putBinary(InputStream srcStream, String remoteFile, boolean append) throws IOException, FTPException { BufferedInputStream in = new BufferedInputStream(srcStream); initPut(remoteFile, append); // get an output stream BufferedOutputStream out = new BufferedOutputStream( new DataOutputStream(data.getOutputStream())); byte[] buf = new byte[512]; // read a chunk at a time and write to the data socket long size = 0; int count = 0; while ((count = in.read(buf)) > 0) { out.write(buf, 0, count); size += count; } in.close(); // flush and clean up out.flush(); out.close(); // and close the data socket try { data.close(); } catch (IOException ignore) {} // log bytes transferred control.log("Transferred " + size + " bytes to remote host"); } /** * Put data onto the FTP server. It * is placed in the current directory. * * @param data array of bytes * @param remoteFile name of remote file in * current directory */ public void put(byte[] bytes, String remoteFile) throws IOException, FTPException { put(bytes, remoteFile, false); } /** * Put data onto the FTP server. It * is placed in the current directory. Allows * appending if current file exists * * @param data array of bytes * @param remoteFile name of remote file in * current directory * @param append true if appending, false otherwise */ public void put(byte[] bytes, String remoteFile, boolean append) throws IOException, FTPException { initPut(remoteFile, append); // get an output stream BufferedOutputStream out = new BufferedOutputStream( new DataOutputStream(data.getOutputStream())); // write array out.write(bytes, 0, bytes.length); // flush and clean up out.flush(); out.close(); // and close the data socket try { data.close(); } catch (IOException ignore) {} validateTransfer(); } /** * Get data from the FTP server. Uses the currently * set transfer mode. * * @param localPath local file to put data in * @param remoteFile name of remote file in * current directory */ public void get(String localPath, String remoteFile) throws IOException, FTPException { // get according to set type if (getType() == FTPTransferType.ASCII) { getASCII(localPath, remoteFile); } else { getBinary(localPath, remoteFile); } validateTransfer(); } /** * Get data from the FTP server. Uses the currently * set transfer mode. * * @param destStream data stream to write data to * @param remoteFile name of remote file in * current directory */ public void get(OutputStream destStream, String remoteFile) throws IOException, FTPException { // get according to set type if (getType() == FTPTransferType.ASCII) { getASCII(destStream, remoteFile); } else { getBinary(destStream, remoteFile); } validateTransfer(); } /** * Request to the server that the get is set up * * @param remoteFile name of remote file */ private void initGet(String remoteFile) throws IOException, FTPException { // set up data channel data = control.createDataSocket(connectMode); data.setTimeout(timeout); // send the retrieve command String reply = control.sendCommand("RETR " + remoteFile); // Can get a 125 or a 150 String[] validCodes1 = {"125", "150"}; lastValidReply = control.validateReply(reply, validCodes1); } /** * Get as ASCII, i.e. read a line at a time and write * using the correct newline separator for the OS * * @param localPath full path of local file to write to * @param remoteFile name of remote file */ private void getASCII(String localPath, String remoteFile) throws IOException, FTPException { // B.McKeown: // Call initGet() before creating the FileOutputStream. // This will prevent being left with an empty file if a FTPException // is thrown by initGet(). initGet(remoteFile); // B. McKeown: Need to store the local file name so the file can be // deleted if necessary. File localFile = new File(localPath); // create the buffered stream for writing BufferedWriter out = new BufferedWriter( new FileWriter(localPath)); // get an character input stream to read data from ... AFTER we // have the ok to go ahead AND AFTER we've successfully opened a // stream for the local file LineNumberReader in = new LineNumberReader( new InputStreamReader(data.getInputStream())); // B. McKeown: // If we are in active mode we have to set the timeout of the passive // socket. We can achieve this by calling setTimeout() again. // If we are in passive mode then we are merely setting the value twice // which does no harm anyway. Doing this simplifies any logic changes. data.setTimeout(timeout); // read/write a line at a time IOException storedEx = null; String line = null; try { while ((line = in.readLine()) != null) { out.write(line, 0, line.length()); out.newLine(); } } catch (IOException ex) { storedEx = ex; localFile.delete(); } finally { out.close(); } try { in.close(); data.close(); } catch (IOException ignore) {} // if we failed to write the file, rethrow the exception if (storedEx != null) throw storedEx; } /** * Get as ASCII, i.e. read a line at a time and write * using the correct newline separator for the OS * * @param destStream data stream to write data to * @param remoteFile name of remote file */ private void getASCII(OutputStream destStream, String remoteFile) throws IOException, FTPException { initGet(remoteFile); // create the buffered stream for writing BufferedWriter out = new BufferedWriter( new OutputStreamWriter(destStream)); // get an character input stream to read data from ... AFTER we // have the ok to go ahead LineNumberReader in = new LineNumberReader( new InputStreamReader(data.getInputStream())); // B. McKeown: // If we are in active mode we have to set the timeout of the passive // socket. We can achieve this by calling setTimeout() again. // If we are in passive mode then we are merely setting the value twice // which does no harm anyway. Doing this simplifies any logic changes. data.setTimeout(timeout); // read/write a line at a time IOException storedEx = null; String line = null; try { while ((line = in.readLine()) != null) { out.write(line, 0, line.length()); out.newLine(); } } catch (IOException ex) { storedEx = ex; } finally { out.close(); } try { in.close(); data.close(); } catch (IOException ignore) {} // if we failed to write the file, rethrow the exception if (storedEx != null) throw storedEx; } /** * Get as binary file, i.e. straight transfer of data * * @param localPath full path of local file to write to * @param remoteFile name of remote file */ private void getBinary(String localPath, String remoteFile) throws IOException, FTPException { // B.McKeown: // Call initGet() before creating the FileOutputStream. // This will prevent being left with an empty file if a FTPException // is thrown by initGet(). initGet(remoteFile); // B. McKeown: Need to store the local file name so the file can be // deleted if necessary. File localFile = new File(localPath); // create the buffered output stream for writing the file BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(localPath, false)); // get an input stream to read data from ... AFTER we have // the ok to go ahead AND AFTER we've successfully opened a // stream for the local file BufferedInputStream in = new BufferedInputStream( new DataInputStream(data.getInputStream())); // B. McKeown: // If we are in active mode we have to set the timeout of the passive // socket. We can achieve this by calling setTimeout() again. // If we are in passive mode then we are merely setting the value twice // which does no harm anyway. Doing this simplifies any logic changes. data.setTimeout(timeout); // do the retrieving long size = 0; int chunksize = 4096; byte [] chunk = new byte[chunksize]; int count; IOException storedEx = null; // read from socket & write to file in chunks try { while ((count = in.read(chunk, 0, chunksize)) >= 0) { out.write(chunk, 0, count); size += count; } } catch (IOException ex) { storedEx = ex; localFile.delete(); } finally { out.close(); } // close streams try { in.close(); data.close(); } catch (IOException ignore) {} // if we failed to write the file, rethrow the exception if (storedEx != null) throw storedEx; // log bytes transferred control.log("Transferred " + size + " bytes from remote host"); } /** * Get as binary file, i.e. straight transfer of data * * @param destStream stream to write to * @param remoteFile name of remote file */ private void getBinary(OutputStream destStream, String remoteFile) throws IOException, FTPException { initGet(remoteFile); // create the buffered output stream for writing the file BufferedOutputStream out = new BufferedOutputStream(destStream); // get an input stream to read data from ... AFTER we have // the ok to go ahead AND AFTER we've successfully opened a // stream for the local file BufferedInputStream in = new BufferedInputStream( new DataInputStream(data.getInputStream())); // B. McKeown: // If we are in active mode we have to set the timeout of the passive // socket. We can achieve this by calling setTimeout() again. // If we are in passive mode then we are merely setting the value twice // which does no harm anyway. Doing this simplifies any logic changes. data.setTimeout(timeout); // do the retrieving long size = 0; int chunksize = 4096; byte [] chunk = new byte[chunksize]; int count; IOException storedEx = null; // read from socket & write to file in chunks try { while ((count = in.read(chunk, 0, chunksize)) >= 0) { out.write(chunk, 0, count); size += count; } } catch (IOException ex) { storedEx = ex; } finally { out.close(); } // close streams try { in.close(); data.close(); } catch (IOException ignore) {} // if we failed to write to the stream, rethrow the exception if (storedEx != null) throw storedEx; // log bytes transferred control.log("Transferred " + size + " bytes from remote host"); } /** * Get data from the FTP server. Transfers in * whatever mode we are in. Retrieve as a byte array. Note * that we may experience memory limitations as the * entire file must be held in memory at one time. * * @param remoteFile name of remote file in * current directory */ public byte[] get(String remoteFile) throws IOException, FTPException { initGet(remoteFile); // get an input stream to read data from BufferedInputStream in = new BufferedInputStream( new DataInputStream(data.getInputStream())); // B. McKeown: // If we are in active mode we have to set the timeout of the passive // socket. We can achieve this by calling setTimeout() again. // If we are in passive mode then we are merely setting the value twice // which does no harm anyway. Doing this simplifies any logic changes. data.setTimeout(timeout); // do the retrieving int chunksize = 4096; byte [] chunk = new byte[chunksize]; // read chunks into byte [] resultBuf = null; // where we place result ByteArrayOutputStream temp = new ByteArrayOutputStream(chunksize); // temp swap buffer int count; // size of chunk read // read from socket & write to file while ((count = in.read(chunk, 0, chunksize)) >= 0) { temp.write(chunk, 0, count); } temp.close(); // get the bytes from the temp buffer resultBuf = temp.toByteArray(); // close streams try { in.close(); data.close(); } catch (IOException ignore) {} validateTransfer(); return resultBuf; } /** * Run a site-specific command on the * server. Support for commands is dependent * on the server * * @param command the site command to run * @return true if command ok, false if * command not implemented */ public boolean site(String command) throws IOException, FTPException { // send the retrieve command String reply = control.sendCommand("SITE " + command); // Can get a 200 (ok) or 202 (not impl). Some // FTP servers return 502 (not impl) String[] validCodes = {"200", "202", "502"}; lastValidReply = control.validateReply(reply, validCodes); // return true or false? 200 is ok, 202/502 not // implemented if (reply.substring(0, 3).equals("200")) return true; else return false; } /** * List a directory's contents * * @param dirname the name of the directory (<b>not</b> a file mask) * @return a string containing the line separated * directory listing * @deprecated As of FTP 1.1, replaced by {@link #dir(String)} */ public String list(String dirname) throws IOException, FTPException { return list(dirname, false); } /** * List a directory's contents as one string. A detailed * listing is available, otherwise just filenames are provided. * The detailed listing varies in details depending on OS and * FTP server. * * @param dirname the name of the directory(<b>not</b> a file mask) * @param full true if detailed listing required * false otherwise * @return a string containing the line separated * directory listing * @deprecated As of FTP 1.1, replaced by {@link #dir(String,boolean)} */ public String list(String dirname, boolean full) throws IOException, FTPException { String[] list = dir(dirname, full); StringBuffer result = new StringBuffer(); String sep = System.getProperty("line.separator"); // loop thru results and make into one string for (int i = 0; i < list.length; i++) { result.append(list[i]); result.append(sep); } return result.toString(); } /** * List current directory's contents as an array of strings of * filenames. * * @return an array of current directory listing strings */ public String[] dir() throws IOException, FTPException { return dir(null, false); } /** * List a directory's contents as an array of strings of filenames. * * @param dirname name of directory(<b>not</b> a file mask) * @return an array of directory listing strings */ public String[] dir(String dirname) throws IOException, FTPException { return dir(dirname, false); } /** * List a directory's contents as an array of strings. A detailed * listing is available, otherwise just filenames are provided. * The detailed listing varies in details depending on OS and * FTP server. Note that a full listing can be used on a file * name to obtain information about a file * * @param dirname name of directory (<b>not</b> a file mask) * @param full true if detailed listing required * false otherwise * @return an array of directory listing strings */ public String[] dir(String dirname, boolean full) throws IOException, FTPException { // set up data channel data = control.createDataSocket(connectMode); data.setTimeout(timeout); // send the retrieve command String command = full ? "LIST ":"NLST "; if (dirname != null) command += dirname; // some FTP servers bomb out if NLST has whitespace appended command = command.trim(); String reply = control.sendCommand(command); // check the control response. wu-ftp returns 550 if the // directory is empty, so we handle 550 appropriately. Similarly // proFTPD returns 450 String[] validCodes1 = {"125", "150", "450", "550"}; lastValidReply = control.validateReply(reply, validCodes1); // an empty array of files for 450/550 String[] result = new String[0]; // a normal reply ... extract the file list String replyCode = lastValidReply.getReplyCode(); if (!replyCode.equals("450") && !replyCode.equals("550")) { // get an character input stream to read data from . LineNumberReader in = new LineNumberReader( new InputStreamReader(data.getInputStream())); // read a line at a time Vector lines = new Vector(); String line = null; while ((line = in.readLine()) != null) { lines.add(line); } try { in.close(); data.close(); } catch (IOException ignore) {} // check the control response String[] validCodes2 = {"226", "250"}; reply = control.readReply(); lastValidReply = control.validateReply(reply, validCodes2); // empty array is default if (!lines.isEmpty()) result = (String[])lines.toArray(result); } return result; } /** * Gets the latest valid reply from the server * * @return reply object encapsulating last valid server response */ public FTPReply getLastValidReply() { return lastValidReply; } /** * Switch debug of responses on or off * * @param on true if you wish to have responses to * the log stream, false otherwise */ public void debugResponses(boolean on) { control.debugResponses(on); } /** * Set the logging stream, replacing * stdout * * @param log the new logging stream */ public void setLogStream(PrintWriter log) { control.setLogStream(log); } /** * Get the current transfer type * * @return the current type of the transfer, * i.e. BINARY or ASCII */ public FTPTransferType getType() { return transferType; } /** * Set the transfer type * * @param type the transfer type to * set the server to */ public void setType(FTPTransferType type) throws IOException, FTPException { // determine the character to send String typeStr = FTPTransferType.ASCII_CHAR; if (type.equals(FTPTransferType.BINARY)) typeStr = FTPTransferType.BINARY_CHAR; // send the command String reply = control.sendCommand("TYPE " + typeStr); lastValidReply = control.validateReply(reply, "200"); // record the type transferType = type; } /** * Delete the specified remote file * * @param remoteFile name of remote file to * delete */ public void delete(String remoteFile) throws IOException, FTPException { String reply = control.sendCommand("DELE " + remoteFile); lastValidReply = control.validateReply(reply, "250"); } /** * Rename a file or directory * * @param from name of file or directory to rename * @param to intended name */ public void rename(String from, String to) throws IOException, FTPException { String reply = control.sendCommand("RNFR " + from); lastValidReply = control.validateReply(reply, "350"); reply = control.sendCommand("RNTO " + to); lastValidReply = control.validateReply(reply, "250"); } /** * Delete the specified remote working directory * * @param dir name of remote directory to * delete */ public void rmdir(String dir) throws IOException, FTPException { String reply = control.sendCommand("RMD " + dir); // some servers return 257, technically incorrect but // we cater for it ... String[] validCodes = {"250", "257"}; lastValidReply = control.validateReply(reply, validCodes); } /** * Create the specified remote working directory * * @param dir name of remote directory to * create */ public void mkdir(String dir) throws IOException, FTPException { String reply = control.sendCommand("MKD " + dir); lastValidReply = control.validateReply(reply, "257"); } /** * Change the remote working directory to * that supplied * * @param dir name of remote directory to * change to */ public void chdir(String dir) throws IOException, FTPException { String reply = control.sendCommand("CWD " + dir); lastValidReply = control.validateReply(reply, "250"); } /** * Get modification time for a remote file * * @param remoteFile name of remote file * @return modification time of file as a date */ public Date modtime(String remoteFile) throws IOException, FTPException { String reply = control.sendCommand("MDTM " + remoteFile); lastValidReply = control.validateReply(reply, "213"); // parse the reply string ... Date ts = tsFormat.parse(lastValidReply.getReplyText(), new ParsePosition(0)); return ts; } /** * Get the current remote working directory * * @return the current working directory */ public String pwd() throws IOException, FTPException { String reply = control.sendCommand("PWD"); lastValidReply = control.validateReply(reply, "257"); // get the reply text and extract the dir // listed in quotes, if we can find it. Otherwise // just return the whole reply string String text = lastValidReply.getReplyText(); int start = text.indexOf('"'); int end = text.lastIndexOf('"'); if (start >= 0 && end > start) return text.substring(start+1, end); else return text; } /** * Get the type of the OS at the server * * @return the type of server OS */ public String system() throws IOException, FTPException { String reply = control.sendCommand("SYST"); lastValidReply = control.validateReply(reply, "215"); return lastValidReply.getReplyText(); } /** * Get the help text for the specified command * * @param command name of the command to get help on * @return help text from the server for the supplied command */ public String help(String command) throws IOException, FTPException { String reply = control.sendCommand("HELP " + command); String[] validCodes = {"211", "214"}; lastValidReply = control.validateReply(reply, validCodes); return lastValidReply.getReplyText(); } /** * Quit the FTP session * */ public void quit() throws IOException, FTPException { try { String reply = control.sendCommand("QUIT"); String[] validCodes = {"221", "226"}; lastValidReply = control.validateReply(reply, validCodes); } finally { // ensure we clean up the connection control.logout(); control = null; } } }