//============================================================================== // FTPClient.java //============================================================================== package tribble.net.ftp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.Exception; import java.lang.Integer; import java.lang.NullPointerException; import java.lang.String; import java.lang.System; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; /******************************************************************************* * Simple FTP client. * Allows clients to establish FTP connections, send and receive files, get * remote directory listings, etc. * *
* This is a simple, bare-bones, no-nonsense implementation, providing only the * most basic FTP capabilities, and performing only minimal error checking and * recovery. If your FTP server is well behaved, though, this implementation * should meet the basic needs of simple FTP applications. * * *
* Usage * *
* This is a simple program that connects to an FTP server, uploads a file, * downloads a file, then closes the connection to the server: * *
* import tribble.net.ftp.*; * * public class MyFtpClient * { * public static void main(String[] args) * throws Exception * { * {@link FTPClientI} ftp = null; * * try * { * // Connect and login to the FTP server * ftp = new {@link #FTPClient FTPClient}(); * ftp.{@link #setHost setHost}("ftp.domain.net"); * ftp.{@link #connect connect}(); * ftp.{@link #login(String, String) login}("userid", "password"); * * // Upload (put) a text file * ftp.{@link #setTextMode setTextMode}(true); * ftp.{@link #setRemoteDir setRemoteDir}("incoming"); * ftp.{@link #putFile(String, String) putFile}("file.txt", "file.txt"); * * // Download (get) a binary file * ftp.{@link #setRemoteDirUp setRemoteDirUp}(); * ftp.{@link #setRemoteDir setRemoteDir}("outgoing"); * ftp.{@link #setTextMode setTextMode}(false); * ftp.{@link #getFile(String, String) getFile}("file.dat", "file.dat"); * } * catch ({@link FTPException} ex) * { * // An error occurred * System.out.println(ex.getMessage()); * throw ex; * } * finally * { * // Close the FTP connection * if (ftp != null) * ftp.{@link #disconnect disconnect}(); * } * } * }* * *
* References * *
* IETF RFC 959 - File Transfer Protocol (FTP)
* www.ietf.org/rfc/rfc0959.txt.
*
*
*
* Copyright ©2001-2010 by David R. Tribble, all rights reserved.
* Permission is granted to any person or entity except those designated
* by the United States Department of State as a terrorist, or terrorist
* government or agency, to use and distribute this source code provided
* that the original copyright notice remains present and unaltered.
*/
public class FTPClient
extends FTPClientAdapter
implements FTPClientI
{
/** Revision information. */
static final String REV =
"@(#)tribble/net/ftp/FTPClient.java API 2.0 $Revision: 1.29 $ $Date: 2010/07/12 21:18:03 $\n";
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants
/** FTP command line terminator (newline). */
static final String CMD_EOLN =
"\r\n";
/** Local native JVM newline character sequence. */
static final byte[] LOCAL_NEWLINE =
System.getProperty("line.separator").getBytes();
/** Remote newline character sequence (CR/LF pair). */
static final byte[] REMOTE_NEWLINE =
{ '\r', '\n' };
//--------------------------------------
// FTP commands
static final String CMD_ABORT = "ABOR";
static final String CMD_ACCOUNT = "ACCT";
static final String CMD_ALLOCATE = "ALLO";
static final String CMD_APPEND = "APPE";
static final String CMD_CHDIR = "CWD";
static final String CMD_DELETE = "DELE";
static final String CMD_GET = "RETR";
static final String CMD_GETDIR = "PWD";
static final String CMD_HELP = "HELP";
static final String CMD_LISTFILES = "LIST";
static final String CMD_LISTNAMES = "NLST";
static final String CMD_LOGIN = "USER";
static final String CMD_LOGOUT = "QUIT";
static final String CMD_MKDIR = "MKD";
static final String CMD_MODE = "MODE";
static final String CMD_MODE_STREAM = "MODE S";
static final String CMD_MODE_BLOCK = "MODE B";
static final String CMD_MODE_COMPR = "MODE C";
static final String CMD_MOUNT = "SMNT";
static final String CMD_NOP = "NOOP";
static final String CMD_PARAMS = "SITE";
static final String CMD_PASSIVE = "PASV";
static final String CMD_PASSWORD = "PASS";
static final String CMD_PORT = "PORT";
static final String CMD_PUT = "STOR";
static final String CMD_PUT_UNIQ = "STOU";
static final String CMD_REINIT = "REIN";
static final String CMD_RENAME_FROM = "RNFR";
static final String CMD_RENAME_TO = "RNTO";
static final String CMD_RESTART = "REST";
static final String CMD_RMDIR = "RMD";
static final String CMD_STATUS = "STAT";
static final String CMD_STRUCT = "STRU";
static final String CMD_STRUCT_FILE = "STRU F";
static final String CMD_STRUCT_REC = "STRU R";
static final String CMD_STRUCT_PAGE = "STRU P";
static final String CMD_SYSTEM = "SYST";
static final String CMD_TYPE = "TYPE";
static final String CMD_TYPE_ASC = "TYPE A N";
static final String CMD_TYPE_BIN = "TYPE I";
static final String CMD_UPDIR = "CDUP";
//--------------------------------------
// FTP response code groups (first digit, code/100)
static final short RG_OKAY_INC = 1;
static final short RG_OKAY_COMPL = 2;
static final short RG_OKAY_PEND = 3;
static final short RG_FAIL_RETRY = 4;
static final short RG_FAIL = 5;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Static methods
/***************************************************************************
* Test driver.
*
*
* Usage *
* * java tribble.net.ftp.FTPClient host port|- * user|- password [-action...] * * * * @param args * Command line arguments. * * @see FTPClientRun * * @since 1.1, 2001-04-14 */ public static void main(String[] args) throws Exception { FTPClientRun.run(new FTPClient(), args); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Variables /** FTP command input stream. */ InputStream m_cmdIn; /** FTP command output stream. */ OutputStream m_cmdOut; /** FTP data input stream. */ InputStream m_dataIn; /** FTP data output stream. */ OutputStream m_dataOut; /** FTP command stream socket. */ Socket m_cmdSock; /** FTP data stream socket. */ Socket m_dataSock; /** FTP local connection host address. */ InetAddress m_localHost; /** Command I/O buffer. */ byte[] m_cbuf = new byte[2*1024]; /** Data I/O buffer. */ byte[] m_dbuf = new byte[4*1024]; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*************************************************************************** * Default constructor. * * @since 1.1, 2001-04-14 */ public FTPClient() { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Methods /*************************************************************************** * Connect to the remote FTP system. * * @throws IOException * Thrown if unable to connect to the remote FTP system. * * @see #disconnect disconnect() * * @since 1.1, 2001-04-14 */ @Override public void connect() throws IOException { FTPResponse resp; // Sanity checks if (m_hostName == null || m_hostName.length() == 0) throw new FTPException("FTP host name not set"); if (m_debugOut != null) m_debugOut.println("$ connect: " + m_hostName + ":" + m_cmdPort + "/" + m_dataPort); // Close any previous connection if (m_isConnected) disconnect(); m_stop = false; // Establish a new FTP connection m_cmdSock = new Socket(m_hostName, m_cmdPort); m_cmdSock.setSoTimeout(m_timeOut*1000); m_localHost = m_cmdSock.getLocalAddress(); m_cmdIn = m_cmdSock.getInputStream(); m_cmdOut = m_cmdSock.getOutputStream(); m_isConnected = true; if (m_debugOut != null) m_debugOut.println("$ localHost: " + m_localHost.toString() + ":" + m_cmdPort); // Read connection response from the remote FTP system resp = getResponse(); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't connect to FTP system (" + resp.m_code + "): " + m_hostName + ":" + m_cmdPort); } /*************************************************************************** * Disconnect from the remote FTP system. * Note that this method does not throw {@link IOException}. * * @see #connect connect() * * @since 1.1, 2001-04-14 */ @Override public void disconnect() { FTPResponse resp; // Sanity checks if (!m_isConnected) return; // Close the data port I/O streams try { if (m_dataSock != null) closeDataPort(); } catch (IOException ex) { } // Close the FTP connection try { resp = doCommand(CMD_LOGOUT); } catch (IOException ex) { } // Close the FTP connection try { // Shut down the FTP I/O streams m_cmdOut.close(); m_cmdIn.close(); } catch (IOException ex) { } try { // Shut down the FTP I/O streams m_cmdSock.close(); } catch (IOException ex) { } // Clean up m_cmdOut = null; m_cmdIn = null; m_cmdSock = null; m_isLoggedOn = false; m_isConnected = false; m_stop = false; if (m_debugOut != null) m_debugOut.println("$ disconnected: " + m_hostName); } /*************************************************************************** * Log on to the remote FTP system. * * @param user * FTP user-ID for the remote system. * * @param pwd * User password. This can be empty (""). * * @throws IOException * Thrown if an error occurs. * * @see #login() login() * @see #connect connect() * * @since 1.2, 2006-03-15 */ @Override public void login(String user, String pwd) throws IOException { FTPResponse resp; int rc; int i, j; // Sanity checks if (user == null || user.length() == 0) throw new FTPException("Null or empty FTP user-ID"); if (pwd == null) throw new FTPException("Null FTP password"); if (!m_isConnected) throw new FTPException("FTP session not connected"); if (m_debugOut != null) m_debugOut.println("$ login: user='" + user + "'"); // Log into the remote FTP system m_userID = user; resp = doCommand(CMD_LOGIN, user); rc = resp.m_code; if (rc/100 == RG_OKAY_PEND) { // Provide a required password m_password = pwd; resp = doCommand(CMD_PASSWORD, pwd); rc = resp.m_code; if (rc/100 == RG_FAIL) throw new FTPException(rc, "Bad FTP user or password (" + rc + "): \"" + user + "\""); } if (rc/100 != RG_OKAY_COMPL && rc/100 != RG_OKAY_PEND) throw new FTPException(rc, "FTP login failed (" + rc + "): \"" + user + "\""); m_isLoggedOn = true; } /*************************************************************************** * Set the transfer mode to text (ASCII) or binary. * In text (ASCII) mode, files are transferred as text files, so that newline * sequences (CR, LF, or CR/LF) are converted into the local native newline * sequence (which is determined by the * System.getProperty("line.separator") setting). * * @param flag * If true, the transfer mode is set to text (ASCII), otherwise it is set * to binary. * * @return * The previous mode setting. * * @since 1.26, 2007-07-26 */ @Override public boolean setTextMode(boolean flag) { boolean prev; prev = m_textMode; try { FTPResponse resp; // Set the transfer mode on the remote side resp = doCommand(flag ? CMD_TYPE_ASC : CMD_TYPE_BIN); if (resp.m_code/100 == RG_OKAY_COMPL) m_textMode = flag; } catch (IOException ex) { // Mode not set, leave m_textMode unchanged } return prev; } /*************************************************************************** * Set the transfer mode to text (ASCII) or binary. * * @deprecated (since 1.26, 2007-07-26) * Use {@link #setTextMode setTextMode()} instead. * * @param flag * If true, the transfer mode is set to text (ASCII), otherwise it is set * to binary. * * @return * The previous mode setting. * * @since 1.3, 2006-03-15 */ public boolean setAsciiMode(boolean flag) { return setTextMode(flag); } /*************************************************************************** * Ping the remote FTP system. * This sends a "NOOP" FTP command to the remote system and receives its * reply. * * @throws IOException * Thrown if an error occurs, e.g., the FTP connection is broken. * * @since 1.4, 2006-03-16 */ @Override public void ping() throws IOException { FTPResponse resp; // Sanity checks if (!m_isConnected) throw new FTPException("FTP session not connected"); // Send a no-op command to the remote FTP system resp = doCommand(CMD_NOP); } /*************************************************************************** * Retrieve the identity information of the remote FTP system. * * @param out * Output stream to which the identification information is to be written. * * @throws IOException * Thrown if an I/O error occurs. * * @throws FTPStoppedException * Thrown if {@link #stop stop()} is called, which terminates the transfer * (write) operation prematurely. * * @since API 2.0, 1.29, 2010-07-12 */ @Override public void getSystemInfo(OutputStream out) throws IOException { FTPResponse resp; // Sanity checks if (out == null) return; if (!m_isConnected) throw new FTPException("FTP session not connected"); // Get the identification of the remote FTP system resp = doCommand(CMD_SYSTEM); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't get remote system info (" + resp.m_code + ")"); transferCmd(resp, true, out); } /*************************************************************************** * Retrieve the current status of the remote FTP system. * * @param out * Output stream to which the status is to be written. * * @throws IOException * Thrown if an I/O error occurs. * * @throws FTPStoppedException * Thrown if {@link #stop stop()} is called, which terminates the transfer * (write) operation prematurely. * * @since API 2.0, 1.29, 2010-07-12 */ @Override public void getStatus(OutputStream out) throws IOException { FTPResponse resp; // Sanity checks if (out == null) return; if (!m_isConnected) throw new FTPException("FTP session not connected"); // Get the current status of the remote FTP system resp = doCommand(CMD_STATUS); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't get remote system status (" + resp.m_code + ")"); transferCmd(resp, false, out); } /*************************************************************************** * Get (receive) a help listing of supported FTP commands from the remote FTP * system. * * @param out * Output stream to which the help listing is to be written. * * @throws IOException * Thrown if an error occurs. * * @throws FTPStoppedException * Thrown if {@link #stop stop()} is called, which terminates the transfer * (write) operation prematurely. * * @since API 2.0, 1.29, 2010-07-12 */ @Override public void getHelp(OutputStream out) throws IOException { FTPResponse resp; // Sanity checks if (out == null) return; if (!m_isConnected) throw new FTPException("FTP session not connected"); // Get help listing resp = doCommand(CMD_HELP); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't get remote system help (" + resp.m_code + ")"); transferCmd(resp, false, out); } /*************************************************************************** * Set the working directory on the remote FTP system. * * @param dir * Remote directory name. * * @return * The new current remote directory name. * * @throws IOException * Thrown if an error occurs. * * @see #setRemoteDirUp setRemoteDirUp() * @see #getRemoteDir getRemoteDir() * @see #setLocalDir setLocalDir() * * @since 1.1, 2001-04-14 */ @Override public String setRemoteDir(String dir) throws IOException { FTPResponse resp; // Sanity checks if (dir == null) throw new NullPointerException("Null FTP directory name"); if (dir.length() == 0) throw new FTPException("Empty FTP directory name"); if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); // Change the remote working directory resp = doCommand(CMD_CHDIR, dir); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't change remote directory (" + resp.m_code + "): \"" + dir + "\""); m_remoteDir = dir; // Retrieve the new current remote working directory dir = getCurrentDir(resp); if (dir == null) { resp = doCommand(CMD_GETDIR); dir = getCurrentDir(resp); } if (dir != null) m_remoteDir = dir; return m_remoteDir; } /*************************************************************************** * Set the working directory on the remote FTP system to the parent directory * of the current working directory. * In other words, change the directory to be one level up from the current * setting. * * @return * The new current remote directory name. * * @throws IOException * Thrown if an error occurs. * * @see #getRemoteDir getRemoteDir() * @see #setRemoteDir setRemoteDir() * @see #setLocalDir setLocalDir() * * @since 1.9, 2006-04-01 */ @Override public String setRemoteDirUp() throws IOException { FTPResponse resp; String dir; // Sanity checks if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); // Change the remote working directory resp = doCommand(CMD_UPDIR); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't change remote directory up (" + resp.m_code + ")"); // Retrieve the new current remote working directory dir = getCurrentDir(resp); if (dir == null) { resp = doCommand(CMD_GETDIR); dir = getCurrentDir(resp); } if (dir != null) m_remoteDir = dir; return m_remoteDir; } /*************************************************************************** * Retrieve the current working directory of the remote FTP system. * * @return * Remote directory name. * * @throws IOException * Thrown if an error occurs. * * @see #setRemoteDir setRemoteDir() * @see #setRemoteDirUp setRemoteDirUp() * @see #getLocalDir getLocalDir() * * @since 1.1, 2001-04-15 */ @Override public String getRemoteDir() throws IOException { FTPResponse resp; String dir; // Sanity checks if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); // Retrieve the current remote working directory resp = doCommand(CMD_GETDIR); if (resp.m_code/100 != RG_OKAY_COMPL) throw new FTPException(resp.m_code, "Can't get remote directory (" + resp.m_code + ")"); dir = getCurrentDir(resp); if (dir != null) m_remoteDir = dir; return m_remoteDir; } /*************************************************************************** * Retrieve the current working directory of the remote FTP system from the * results of the previous FTP command. * * @return * Current remote directory name, or null if it cannot be retrieved. * * @throws IOException * Thrown if an error occurs. * * @see #setRemoteDir setRemoteDir() * @see #setRemoteDirUp setRemoteDirUp() * @see #getRemoteDir getRemoteDir() * * @since 1.9, 2006-04-01 */ String getCurrentDir(FTPResponse resp) throws IOException { // Retrieve the new current remote working directory // from the results of a previous 'CMD_GETDIR' command if (resp.m_code/100 == RG_OKAY_COMPL) { byte[] line; int i, j, k, o; line = resp.line(0); for (i = 0; i < line.length && line[i] != '"'; i++) continue; for (j = line.length-1; j > i && line[j] != '"'; j--) continue; for (k = i+1, o = i+1; o < j; k++, o++) { if (line[o] == '"' && line[o+1] == '"') o++; line[k] = line[o]; } if (i+1 < k) return (new String(line, i+1, k-(i+1))); } // Failed return null; } /*************************************************************************** * Get (receive) a file from the remote FTP system to the local system. * * @param src * Remote source filename. If this does not contain a directory prefix, the * current remote working directory is assumed. * * @param out * Output stream to write the contents of the file retrieved from the remote * FTP system to. Note that this stream is flushed but is not closed * after the contents have been transmitted. * * @throws IOException * Thrown if the file could not be transmitted or if any other error occurs. * * @throws FTPStoppedException * Thrown if {@link #stop stop()} is called, which terminates the transfer * operation prematurely. * * @see #getFile(String, File) getFile() * @see #getFile(String, String) getFile() * @see #putFile(InputStream, String) putFile() * * @since 1.12, 2006-04-10 */ @Override public void getFile(String src, OutputStream out) throws IOException { FTPResponse resp; InputStream in = null; OutputStream outp; IOException exc = null; // Sanity checks if (src == null) throw new NullPointerException("Null FTP source file"); if (out == null) throw new NullPointerException("Null FTP output stream"); if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); // Open the target file as an output stream if (out instanceof BufferedOutputStream) outp = out; else outp = new BufferedOutputStream(out, m_bufSize); // Retrieve (get) the file from the remote FTP system resp = openDataPort(CMD_GET, src); if (resp.m_code/100 >= RG_FAIL_RETRY) throw new FTPException(resp.m_code, "Can't get remote FTP file (" + resp.m_code + "): " + src); in = new BufferedInputStream(m_dataIn, m_bufSize); transferFile(in, outp, LOCAL_NEWLINE); // Clean up try { in.close(); outp.flush(); } catch (IOException ex) { exc = ex; } // Clean up closeDataPort(); resp = getResponse(); if (exc != null) throw (exc); } /*************************************************************************** * Put (send) a file from the local system to the remote FTP system. * * @param src * Local source filename. If this does not contain a directory prefix, the * current local working directory is assumed. * * @param dst * Remote target filename. If this does not contain a directory prefix, the * current remote working directory is assumed. This may be null, in which * case the base filename of src (without the directory prefix) is * used. * * @throws IOException * Thrown if the file could not be transmitted or if any other error occurs. * * @see #putFile(String, String) putFile() * @see #putFile(InputStream, String) putFile() * @see #getFile(String, File) getFile() * * @since 1.1, 2001-04-14 */ @Override public void putFile(File src, String dst) throws IOException { InputStream in; long len; // Sanity checks if (src == null) throw new NullPointerException("Null FTP source file"); if (dst == null) throw new NullPointerException("Null FTP target file"); if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); if (!src.exists()) throw new FTPException("FTP source file does not exist: \"" + src.getPath() + "\""); // Open the source file as an input stream in = new FileInputStream(src); // Preallocate the target file on the remote system len = src.length(); if (len > 1024) { int obytes; FTPResponse resp; obytes = (len <= Integer.MAX_VALUE ? (int) len : Integer.MAX_VALUE); resp = doCommand(CMD_ALLOCATE, obytes + ""); } try { // Store (put) the file to the remote FTP system putFile(in, dst); } finally { in.close(); } } /*************************************************************************** * Put (send) a file from the local system to the remote FTP system. * * @param in * Input stream containing the contents of the file to send to the remote FTP * system. Note that this stream is not closed after the contents * have been transmitted. * * @param dst * Remote target filename. If this does not contain a directory prefix, the * current remote working directory is assumed. This may be null, in which * case the base filename of src (without the directory prefix) is * used. * * @throws IOException * Thrown if the file could not be transmitted or if any other error occurs. * * @throws FTPStoppedException * Thrown if {@link #stop stop()} is called, which terminates the transfer * operation prematurely. * * @see #putFile(File, String) putFile() * @see #putFile(String, String) putFile() * @see #appendFile(File, String) appendFile() * @see #getFile(String, OutputStream) getFile() * * @since 1.12, 2006-04-10 */ @Override public void putFile(InputStream in, String dst) throws IOException { FTPResponse resp; InputStream inp; IOException exc = null; // Sanity checks if (in == null) throw new NullPointerException("Null FTP input stream"); if (dst == null) throw new NullPointerException("Null FTP target file"); if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); // Open the source file as an input stream if (in instanceof BufferedInputStream) inp = in; else inp = new BufferedInputStream(in, m_bufSize); // Store (put) the file to the remote FTP system resp = openDataPort(CMD_PUT, dst); if (resp.m_code/100 >= RG_FAIL_RETRY) throw new FTPException(resp.m_code, "Can't put file to remote FTP (" + resp.m_code + "): " + dst); try { OutputStream out; // Store (put) the file to the remote FTP system out = new BufferedOutputStream(m_dataOut, m_bufSize); transferFile(inp, out, REMOTE_NEWLINE); out.flush(); out.close(); } catch (IOException ex) { exc = ex; } // Clean up closeDataPort(); resp = getResponse(); if (exc != null) throw (exc); } /*************************************************************************** * Append (send) a file from the local system to a file on the remote FTP * system. * * @param in * Input stream containing the contents of the file to send to the remote FTP * system. Note that this stream is not closed after the contents * have been transmitted. * * @param dst * Remote target filename. If this does not contain a directory prefix, the * current remote working directory is assumed. This may be null, in which * case the base filename of src (without the directory prefix) is * used. * * @throws IOException * Thrown if the file could not be transmitted or if any other error occurs. * * @throws FTPStoppedException * Thrown if {@link #stop stop()} is called, which terminates the transfer * operation prematurely. * * @see #appendFile(File, String) appendFile() * @see #appendFile(String, String) appendFile() * @see #putFile(InputStream, String) putFile() * * @since 1.25, 2007-06-30 */ @Override public void appendFile(InputStream in, String dst) throws IOException { FTPResponse resp; InputStream inp; IOException exc = null; // Sanity checks if (in == null) throw new NullPointerException("Null FTP input stream"); if (dst == null) throw new NullPointerException("Null FTP target file"); if (!m_isConnected) throw new FTPException("FTP session not connected"); if (!m_isLoggedOn) throw new FTPException("Not logged into FTP session"); // Open the source file as an input stream if (in instanceof BufferedInputStream) inp = in; else inp = new BufferedInputStream(in, m_bufSize); // Append (put) the file to the remote FTP system resp = openDataPort(CMD_APPEND, dst); if (resp.m_code/100 >= RG_FAIL_RETRY) throw new FTPException(resp.m_code, "Can't append file to remote FTP (" + resp.m_code + "): " + dst); try { OutputStream out; // Append (put) the file to the remote FTP system out = new BufferedOutputStream(m_dataOut, m_bufSize); transferFile(inp, out, REMOTE_NEWLINE); out.flush(); out.close(); } catch (IOException ex) { exc = ex; } // Clean up closeDataPort(); resp = getResponse(); if (exc != null) throw (exc); } /*************************************************************************** * Transfer (send or receive) a file from one system (local or remote) to the * other system (remote or local). * *
* When transferring text-mode files (i.e., {@link #m_textMode} is true),
* this method handles the translation of newlines, translating CR, LF, and
* CR/LF input sequences into CR/LF output sequences.
*
* @param in
* Input source stream. This may be for a local file or a remote file.
*
* @param out
* Output target stream. This may be for a remote file or a local file.
*
* @param nl
* Newline sequence for the target system. This only applies to text (ASCII)
* mode file transfers.
*
* @return
* Number of bytes written to the output stream.
*
* @throws IOException
* Thrown if the file could not be transmitted or if any other error occurs.
*
* @throws FTPStoppedException
* Thrown if {@link #stop stop()} is called, which terminates the transfer
* operation prematurely.
*
* @see #getFile(String, File) getFile()
* @see #putFile(File, String) putFile()
*
* @since 1.5, 2006-03-18
*/
long transferFile(InputStream in, OutputStream out, byte[] nl)
throws IOException
{
long nlines = 0;
// Copy the source file to the target file
try
{
m_inBytes = 0;
m_outBytes = 0;
if (m_textMode)
{
int ungetCh = -1;
// Text (ASCII) data transfer
for (;;)
{
int ch;
// Check for an interrupt signal
if (m_stop)
throw new FTPStoppedException("FTP transfer stopped");
// Read the next byte from the local/remote source file
if (ungetCh < 0)
ch = in.read();
else
ch = ungetCh;
ungetCh = -1;
if (ch < 0)
break;
m_inBytes++;
// Write the data byte to the remote/local target file
if (ch == '\r')
{
// Translate a CR or CR/LF newline sequence
ch = in.read();
m_inBytes++;
if (ch != '\n')
{
ungetCh = ch;
m_inBytes--;
}
ch = '\n';
}
if (ch == '\n')
{
// Translate a LF, CR, or CR/LF into a CR/LF
for (int i = 0; i < nl.length; i++)
{
out.write(nl[i]);
m_outBytes++;
}
nlines++;
}
else
{
out.write(ch);
m_outBytes++;
}
}
return m_outBytes;
}
else
{
// Binary data transfer
for (;;)
{
int ch;
// Check for an interrupt signal
if (m_stop)
throw new FTPStoppedException("FTP transfer stopped");
// Read the next byte from the local/remote source file
ch = in.read();
if (ch < 0)
break;
m_inBytes++;
// Write the data byte to the remote/local target file
out.write(ch);
m_outBytes++;
}
return m_outBytes;
}
}
finally
{
if (m_debugOut != null)
m_debugOut.println("$ bytes read:" + m_inBytes
+ ", wrote:" + m_outBytes + ", lines:" + nlines);
}
}
/***************************************************************************
* Transfer the contents of the command stream to a local output stream.
* Leading spaces are stripped from the command response lines.
*
* @param resp
* Command response received from the remote FTP system.
*
* @param all
* If true, all response lines including the last one are written, otherwise
* all but the last line are written.
*
* @param out
* Local output stream.
*
* @throws IOException
* Thrown if an I/O error occurs.
*
* @throws FTPStoppedException
* Thrown if {@link #stop stop()} is called, which terminates the transfer
* (write) operation prematurely.
*
* @since API 2.0, 1.29, 2010-07-12
*/
void transferCmd(FTPResponse resp, boolean all, OutputStream out)
throws IOException
{
int len;
// Write the command response to the output stream
len = resp.m_lines.size();
len = (all ? len : len-1);
for (int i = 0; i < len; i++)
{
byte[] line;
int j;
// Check for an interrupt signal
if (m_stop)
throw new FTPStoppedException("FTP transfer stopped");
// Write the next response line
line = resp.line(i);
j = (i == 0 ? 4 : 0);
for ( ; j < line.length && line[j] == ' '; j++)
continue;
for ( ; j < line.length; j++)
out.write((char) (line[j] & 0xFF));
for (j = 0; j < LOCAL_NEWLINE.length; j++)
out.write((char) (LOCAL_NEWLINE[j] & 0xFF));
}
out.flush();
}
/***************************************************************************
* Get (receive) a directory listing from the remote FTP system.
*
* @param path
* The remote directory or filename to list. If this is empty
* (""), the current remote working directory is assumed.
*
* @param max
* Maximum number of filenames (output lines) to list. A value of zero (0)
* specifies that there is no maximum.
*
* @param out
* Output stream to which the directory listing is to be written.
*
* @throws IOException
* Thrown if an error occurs.
*
* @throws FTPStoppedException
* Thrown if {@link #stop stop()} is called, which terminates the listing
* (output) operation prematurely.
*
* @since API 2.0, 1.29, 2010-07-12
*/
@Override
public void getDirectoryList(String path, int max, OutputStream out)
throws IOException
{
// Get the directory listing of the remote directory
doDirectoryCmd(path, true, max, out);
}
/***************************************************************************
* Get (receive) a list of filenames in a directory on the remote FTP system.
*
* @param path
* The remote directory or filename to list. If this is empty
* (""), the current remote working directory is assumed.
*
* @param max
* Maximum number of filenames to get. A value of zero (0) specifies that
* there is no maximum.
*
* @param out
* Output stream to which the directory listing is to be written.
*
* @throws IOException
* Thrown if an error occurs.
*
* @throws FTPStoppedException
* Thrown if {@link #stop stop()} is called, which terminates the listing
* (output) operation prematurely.
*
* @since API 2.0, 1.29, 2010-07-12
*/
@Override
public void getDirectoryNames(String path, int max, OutputStream out)
throws IOException
{
// Get the directory listing of the remote directory
doDirectoryCmd(path, false, max, out);
}
/***************************************************************************
* Get (receive) a directory listing for the current working directory on the
* remote FTP system.
*
* @param path
* The remote directory or filename to list. If this is empty
* (""), the current remote working directory is assumed.
*
* @param full
* If true, a full directory listing is produced, otherwise only filenames
* are received.
*
* @param max
* Maximum number of filenames (or output lines) to get. A value of zero (0)
* specifies that there is no maximum.
*
* @param out
* Output stream to which the directory listing is to be written. The stream
* is flushed but not closed after the filenames are written to it.
*
* @throws IOException
* Thrown if an error occurs.
*
* @throws FTPStoppedException
* Thrown if {@link #stop stop()} is called, which terminates the listing
* (output) operation prematurely.
*
* @since API 2.0, 1.29, 2010-07-12
*/
void doDirectoryCmd(String path, boolean full, int max, OutputStream out)
throws IOException
{
FTPResponse resp;
int n;
// Sanity checks
if (out == null)
return;
if (!m_isConnected)
throw new FTPException("FTP session not connected");
if (!m_isLoggedOn)
throw new FTPException("Not logged into FTP session");
// Get the directory listing of the remote directory
if (path == null)
path = "";
resp = openDataPort((full ? CMD_LISTFILES : CMD_LISTNAMES), path);
if (resp.m_code/100 != RG_OKAY_INC && resp.m_code/100 != RG_OKAY_PEND)
{
closeDataPort();
if (resp.m_code/100 == RG_FAIL_RETRY)
{
// Assume that the remote directory is empty
out.flush();
return;
}
throw new FTPException(resp.m_code,
"Can't get remote directory list (" + resp.m_code + "): \""
+ path + "\"");
}
// Read the directory listing results
n = 0;
for (;;)
{
int len;
// Check for an interrupt signal
if (m_stop)
throw new FTPStoppedException("FTP listing stopped");
// Read the next line from the directory listing
len = readDataLine();
if (len < 0)
break;
// Write the directory entry to the output stream
if (len > 0)
{
for (int j = 0; j < len; j++)
out.write((char) (m_dbuf[j] & 0xFF));
for (int j = 0; j < LOCAL_NEWLINE.length; j++)
out.write((char) (LOCAL_NEWLINE[j] & 0xFF));
n++;
if (n >= max && max > 0)
break;
}
}
out.flush();
// Get the transfer completion response
closeDataPort();
resp = getResponse();
}
/***************************************************************************
* Get (receive) a list of filenames in a directory on the remote FTP system.
*
* @param path
* The remote directory or filename to list. If this is empty
* (""), the current remote working directory is assumed.
*
* @param filt
* Filer to apply to the filenames. Only filenames that are accepted by the
* filter will appear in the returned vector. If this is null, no filtering
* is applied to the filenames. This object's accept() method is
* called for each filename in the directory, being passed a null directory
* (first argument) and the found filename (second argument).
*
* @param max
* Maximum number of filenames to list. A value of zero (0) specifies that
* there is no maximum.
*
* @return
* A vector of Strings containing the filenames in the remote
* directory. Note that this may contain zero entries.
*
* @throws IOException
* Thrown if an error occurs.
*
* @throws FTPStoppedException
* Thrown if {@link #stop stop()} is called, which terminates the listing
* operation prematurely.
*
* @since 1.22, 2007-04-15
*/
//@Override
public ArrayList