/*
* ftp4j - A pure Java FTP client library
*
* Copyright (C) 2008-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* This program 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 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see
* client.rename("oldname", "newname"); // This one renames
*
*
*
* client.rename("the/old/path/oldname", "/a/new/path/newname"); // This one moves
*
*
* @param oldPath
* The current path of the file (or directory).
* @param newPath
* The new path for the file (or directory).
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
*/
public void rename(String oldPath, String newPath)
throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Sends the RNFR command.
communication.sendFTPCommand("RNFR " + oldPath);
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 350) {
throw new FTPException(r);
}
// Sends the RNFR command.
communication.sendFTPCommand("RNTO " + newPath);
r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
}
}
/**
* This method deletes a remote file.
*
* @param path
* The path to the file.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
*/
public void deleteFile(String path) throws IllegalStateException,
IOException, FTPIllegalReplyException, FTPException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Sends the DELE command.
communication.sendFTPCommand("DELE " + path);
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
}
}
/**
* This method deletes a remote directory.
*
* @param path
* The path to the directory.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
*/
public void deleteDirectory(String path) throws IllegalStateException,
IOException, FTPIllegalReplyException, FTPException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Sends the RMD command.
communication.sendFTPCommand("RMD " + path);
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
}
}
/**
* This method creates a new remote directory in the current working one.
*
* @param directoryName
* The name of the new directory.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
*/
public void createDirectory(String directoryName)
throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Sends the MKD command.
communication.sendFTPCommand("MKD " + directoryName);
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
}
}
/**
* This method calls the HELP command on the remote server, returning a list
* of lines with the help contents.
*
* @return The help contents, splitted by line.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
*/
public String[] help() throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Sends the HELP command.
communication.sendFTPCommand("HELP");
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
return r.getMessages();
}
}
/**
* This method returns the remote server status, as the result of a FTP STAT
* command.
*
* @return The remote server status, splitted by line.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
*/
public String[] serverStatus() throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Sends the STAT command.
communication.sendFTPCommand("STAT");
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
return r.getMessages();
}
}
/**
* This method lists the entries of the current working directory parsing
* the reply to a FTP LIST command.
*
* The response to the LIST command is parsed through the FTPListParser
* objects registered on the client. The distribution of ftp4j contains some
* standard parsers already registered on every FTPClient object created. If
* they don't work in your case (a FTPListParseException is thrown), you can
* build your own parser implementing the FTPListParser interface and add it
* to the client by calling its addListParser() method.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The list() method will break with a
* FTPAbortedException.
*
* @param fileSpec
* A file filter string. Depending on the server implementation,
* wildcard characters could be accepted.
* @return The list of the files (and directories) in the current working
* directory.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @throws FTPListParseException
* If none of the registered parsers can handle the response
* sent by the server.
* @see FTPListParser
* @see FTPClient#addListParser(FTPListParser)
* @see FTPClient#getListParsers()
* @see FTPClient#abortCurrentDataTransfer(boolean)
* @see FTPClient#listNames()
* @since 1.2
*/
public FTPFile[] list(String fileSpec) throws IllegalStateException,
IOException, FTPIllegalReplyException, FTPException,
FTPDataTransferException, FTPAbortedException,
FTPListParseException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// ASCII, please!
communication.sendFTPCommand("TYPE A");
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
// Prepares the connection for the data transfer.
FTPDataTransferConnectionProvider provider = openDataTransferChannel();
// MLSD or LIST command?
boolean mlsdCommand;
if (mlsdPolicy == MLSD_IF_SUPPORTED) {
mlsdCommand = mlsdSupported;
} else if (mlsdPolicy == MLSD_ALWAYS) {
mlsdCommand = true;
} else {
mlsdCommand = false;
}
String command = mlsdCommand ? "MLSD" : "LIST";
// Adds the file/directory selector.
if (fileSpec != null && fileSpec.length() > 0) {
command += " " + fileSpec;
}
// Prepares the lines array.
ArrayList lines = new ArrayList();
// Local abort state.
boolean wasAborted = false;
// Sends the command.
communication.sendFTPCommand(command);
try {
Socket dtConnection;
try {
dtConnection = provider.openDataTransferConnection();
} finally {
provider.dispose();
}
// Change the operation status.
synchronized (abortLock) {
ongoingDataTransfer = true;
aborted = false;
consumeAborCommandReply = false;
}
// Fetch the list from the data transfer connection.
NVTASCIIReader dataReader = null;
try {
// Opens the data transfer connection.
dataTransferInputStream = dtConnection.getInputStream();
// MODE Z enabled?
if (modezEnabled) {
dataTransferInputStream = new InflaterInputStream(dataTransferInputStream);
}
// Let's do it!
dataReader = new NVTASCIIReader(dataTransferInputStream, mlsdCommand ? "UTF-8" : pickCharset());
String line;
while ((line = dataReader.readLine()) != null) {
if (line.length() > 0) {
lines.add(line);
}
}
} catch (IOException e) {
synchronized (abortLock) {
if (aborted) {
throw new FTPAbortedException();
} else {
throw new FTPDataTransferException(
"I/O error in data transfer", e);
}
}
} finally {
if (dataReader != null) {
try {
dataReader.close();
} catch (Throwable t) {
;
}
}
try {
dtConnection.close();
} catch (Throwable t) {
;
}
// Set to null the instance-level input stream.
dataTransferInputStream = null;
// Change the operation status.
synchronized (abortLock) {
wasAborted = aborted;
ongoingDataTransfer = false;
aborted = false;
}
}
} finally {
r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 150 && r.getCode() != 125) {
throw new FTPException(r);
}
// Consumes the result reply of the transfer.
r = communication.readFTPReply();
if (!wasAborted && r.getCode() != 226) {
throw new FTPException(r);
}
// ABOR command response (if needed).
if (consumeAborCommandReply) {
communication.readFTPReply();
consumeAborCommandReply = false;
}
}
// Build an array of lines.
int size = lines.size();
String[] list = new String[size];
for (int i = 0; i < size; i++) {
list[i] = (String) lines.get(i);
}
// Parse the list.
FTPFile[] ret = null;
if (mlsdCommand) {
// Forces the MLSDListParser.
MLSDListParser parser = new MLSDListParser();
ret = parser.parse(list);
} else {
// Is there any already successful parser?
if (parser != null) {
// Yes, let's try with it.
try {
ret = parser.parse(list);
} catch (FTPListParseException e) {
// That parser doesn't work anymore.
parser = null;
}
}
// Is there an available result?
if (ret == null) {
// Try to parse the list with every available parser.
for (Iterator i = listParsers.iterator(); i.hasNext();) {
FTPListParser aux = (FTPListParser) i.next();
try {
// Let's try!
ret = aux.parse(list);
// This parser smells good!
parser = aux;
// Leave the loop.
break;
} catch (FTPListParseException e) {
// Let's try the next one.
continue;
}
}
}
}
if (ret == null) {
// None of the parsers can handle the list response.
throw new FTPListParseException();
} else {
// Return the parsed list.
return ret;
}
}
}
/**
* This method lists the entries of the current working directory parsing
* the reply to a FTP LIST command.
*
* The response to the LIST command is parsed through the FTPListParser
* objects registered on the client. The distribution of ftp4j contains some
* standard parsers already registered on every FTPClient object created. If
* they don't work in your case (a FTPListParseException is thrown), you can
* build your own parser implementing the FTPListParser interface and add it
* to the client by calling its addListParser() method.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The list() method will break with a
* FTPAbortedException.
*
* @return The list of the files (and directories) in the current working
* directory.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @throws FTPListParseException
* If none of the registered parsers can handle the response
* sent by the server.
* @see FTPListParser
* @see FTPClient#addListParser(FTPListParser)
* @see FTPClient#getListParsers()
* @see FTPClient#abortCurrentDataTransfer(boolean)
* @see FTPClient#listNames()
*/
public FTPFile[] list() throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException,
FTPDataTransferException, FTPAbortedException, FTPListParseException {
return list(null);
}
/**
* This method lists the entries of the current working directory with a FTP
* NLST command.
*
* The response consists in an array of string, each one reporting the name
* of a file or a directory placed in the current working directory. For a
* more detailed directory listing procedure look at the list() method.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The listNames() method will break with a
* FTPAbortedException.
*
* @return The list of the files (and directories) in the current working
* directory.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @throws FTPListParseException
* If none of the registered parsers can handle the response
* sent by the server.
* @see FTPClient#abortCurrentDataTransfer(boolean)
* @see FTPClient#list()
*/
public String[] listNames() throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException,
FTPDataTransferException, FTPAbortedException, FTPListParseException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// ASCII, please!
communication.sendFTPCommand("TYPE A");
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
// Prepares the lines array.
ArrayList lines = new ArrayList();
// Local abort state.
boolean wasAborted = false;
// Prepares the connection for the data transfer.
FTPDataTransferConnectionProvider provider = openDataTransferChannel();
// Send the NLST command.
communication.sendFTPCommand("NLST");
try {
Socket dtConnection;
try {
dtConnection = provider.openDataTransferConnection();
} finally {
provider.dispose();
}
// Change the operation status.
synchronized (abortLock) {
ongoingDataTransfer = true;
aborted = false;
consumeAborCommandReply = false;
}
// Fetch the list from the data transfer connection.
NVTASCIIReader dataReader = null;
try {
// Opens the data transfer connection.
dataTransferInputStream = dtConnection.getInputStream();
// MODE Z enabled?
if (modezEnabled) {
dataTransferInputStream = new InflaterInputStream(dataTransferInputStream);
}
// Let's do it!
dataReader = new NVTASCIIReader(dataTransferInputStream, pickCharset());
String line;
while ((line = dataReader.readLine()) != null) {
if (line.length() > 0) {
lines.add(line);
}
}
} catch (IOException e) {
synchronized (abortLock) {
if (aborted) {
throw new FTPAbortedException();
} else {
throw new FTPDataTransferException(
"I/O error in data transfer", e);
}
}
} finally {
if (dataReader != null) {
try {
dataReader.close();
} catch (Throwable t) {
;
}
}
try {
dtConnection.close();
} catch (Throwable t) {
;
}
// Set to null the instance-level input stream.
dataTransferInputStream = null;
// Change the operation status.
synchronized (abortLock) {
wasAborted = aborted;
ongoingDataTransfer = false;
aborted = false;
}
}
} finally {
r = communication.readFTPReply();
if (r.getCode() != 150 && r.getCode() != 125) {
throw new FTPException(r);
}
// Consumes the result reply of the transfer.
r = communication.readFTPReply();
if (!wasAborted && r.getCode() != 226) {
throw new FTPException(r);
}
// ABOR command response (if needed).
if (consumeAborCommandReply) {
communication.readFTPReply();
consumeAborCommandReply = false;
}
}
// Build an array.
int size = lines.size();
String[] list = new String[size];
for (int i = 0; i < size; i++) {
list[i] = (String) lines.get(i);
}
return list;
}
}
/**
* This method uploads a file to the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param file
* The file to upload.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void upload(File file) throws IllegalStateException,
FileNotFoundException, IOException, FTPIllegalReplyException,
FTPException, FTPDataTransferException, FTPAbortedException {
upload(file, 0, null);
}
/**
* This method uploads a file to the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param file
* The file to upload.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void upload(File file, FTPDataTransferListener listener)
throws IllegalStateException, FileNotFoundException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
upload(file, 0, listener);
}
/**
* This method uploads a file to the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param file
* The file to upload.
* @param restartAt
* The restart point (number of bytes already uploaded). Use
* {@link FTPClient#isResumeSupported()} to check if the server
* supports resuming of broken data transfers.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void upload(File file, long restartAt) throws IllegalStateException,
FileNotFoundException, IOException, FTPIllegalReplyException,
FTPException, FTPDataTransferException, FTPAbortedException {
upload(file, restartAt, null);
}
/**
* This method uploads a file to the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param file
* The file to upload.
* @param restartAt
* The restart point (number of bytes already uploaded). Use
* {@link FTPClient#isResumeSupported()} to check if the server
* supports resuming of broken data transfers.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void upload(File file, long restartAt,
FTPDataTransferListener listener) throws IllegalStateException,
FileNotFoundException, IOException, FTPIllegalReplyException,
FTPException, FTPDataTransferException, FTPAbortedException {
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath());
}
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
} catch (IOException e) {
throw new FTPDataTransferException(e);
}
try {
upload(file.getName(), inputStream, restartAt, restartAt, listener);
} catch (IllegalStateException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (FTPIllegalReplyException e) {
throw e;
} catch (FTPException e) {
throw e;
} catch (FTPDataTransferException e) {
throw e;
} catch (FTPAbortedException e) {
throw e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable t) {
;
}
}
}
}
/**
* This method uploads a content to the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param fileName
* The name of the remote file.
* @param inputStream
* The source of data.
* @param restartAt
* The restart point (number of bytes already uploaded). Use
* {@link FTPClient#isResumeSupported()} to check if the server
* supports resuming of broken data transfers.
* @param streamOffset
* The offset to skip in the stream.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void upload(String fileName, InputStream inputStream,
long restartAt, long streamOffset, FTPDataTransferListener listener)
throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Select the type of contents.
int tp = type;
if (tp == TYPE_AUTO) {
tp = detectType(fileName);
}
if (tp == TYPE_TEXTUAL) {
communication.sendFTPCommand("TYPE A");
} else if (tp == TYPE_BINARY) {
communication.sendFTPCommand("TYPE I");
}
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
// Prepares the connection for the data transfer.
FTPDataTransferConnectionProvider provider = openDataTransferChannel();
// REST command (if supported and/or requested).
if (restSupported || restartAt > 0) {
boolean done = false;
try {
communication.sendFTPCommand("REST " + restartAt);
r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 350 && ((r.getCode() != 501 && r.getCode() != 502) || restartAt > 0)) {
throw new FTPException(r);
}
done = true;
} finally {
if (!done) {
provider.dispose();
}
}
}
// Local abort state.
boolean wasAborted = false;
// Send the STOR command.
communication.sendFTPCommand("STOR " + fileName);
try {
Socket dtConnection;
try {
dtConnection = provider.openDataTransferConnection();
} finally {
provider.dispose();
}
// Change the operation status.
synchronized (abortLock) {
ongoingDataTransfer = true;
aborted = false;
consumeAborCommandReply = false;
}
// Upload the stream.
try {
// Skips.
inputStream.skip(streamOffset);
// Opens the data transfer connection.
dataTransferOutputStream = dtConnection.getOutputStream();
// MODE Z enabled?
if (modezEnabled) {
dataTransferOutputStream = new DeflaterOutputStream(dataTransferOutputStream);
}
// Listeners.
if (listener != null) {
listener.started();
}
// Let's do it!
if (tp == TYPE_TEXTUAL) {
Reader reader = new InputStreamReader(inputStream);
Writer writer = new OutputStreamWriter(
dataTransferOutputStream, pickCharset());
char[] buffer = new char[SEND_AND_RECEIVE_BUFFER_SIZE];
int l;
while ((l = reader.read(buffer)) != -1) {
writer.write(buffer, 0, l);
writer.flush();
if (listener != null) {
listener.transferred(l);
}
}
} else if (tp == TYPE_BINARY) {
byte[] buffer = new byte[SEND_AND_RECEIVE_BUFFER_SIZE];
int l;
while ((l = inputStream.read(buffer)) != -1) {
dataTransferOutputStream.write(buffer, 0, l);
dataTransferOutputStream.flush();
if (listener != null) {
listener.transferred(l);
}
}
}
} catch (IOException e) {
synchronized (abortLock) {
if (aborted) {
if (listener != null) {
listener.aborted();
}
throw new FTPAbortedException();
} else {
if (listener != null) {
listener.failed();
}
throw new FTPDataTransferException(
"I/O error in data transfer", e);
}
}
} finally {
// Closing stream and data connection.
if (dataTransferOutputStream != null) {
try {
dataTransferOutputStream.close();
} catch (Throwable t) {
;
}
}
try {
dtConnection.close();
} catch (Throwable t) {
;
}
// Set to null the instance-level input stream.
dataTransferOutputStream = null;
// Change the operation status.
synchronized (abortLock) {
wasAborted = aborted;
ongoingDataTransfer = false;
aborted = false;
}
}
} finally {
// Data transfer command reply.
r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 150 && r.getCode() != 125) {
throw new FTPException(r);
}
// Consumes the result reply of the transfer.
r = communication.readFTPReply();
if (!wasAborted && r.getCode() != 226) {
throw new FTPException(r);
}
// ABOR command response (if needed).
if (consumeAborCommandReply) {
communication.readFTPReply();
consumeAborCommandReply = false;
}
}
// Listener notification.
if (listener != null) {
listener.completed();
}
}
}
/**
* This method appends the contents of a local file to an existing file on
* the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param file
* The local file whose contents will be appended to the remote
* file.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
* @since 1.6
*/
public void append(File file) throws IllegalStateException,
FileNotFoundException, IOException, FTPIllegalReplyException,
FTPException, FTPDataTransferException, FTPAbortedException {
append(file, null);
}
/**
* This method uploads a file to the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param file
* The local file whose contents will be appended to the remote
* file.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
* @since 1.6
*/
public void append(File file, FTPDataTransferListener listener)
throws IllegalStateException, FileNotFoundException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath());
}
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
} catch (IOException e) {
throw new FTPDataTransferException(e);
}
try {
append(file.getName(), inputStream, 0, listener);
} catch (IllegalStateException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (FTPIllegalReplyException e) {
throw e;
} catch (FTPException e) {
throw e;
} catch (FTPDataTransferException e) {
throw e;
} catch (FTPAbortedException e) {
throw e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable t) {
;
}
}
}
}
/**
* This method appends data to an existing file on the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param fileName
* The name of the remote file.
* @param inputStream
* The source of data.
* @param streamOffset
* The offset to skip in the stream.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
* @since 1.6
*/
public void append(String fileName, InputStream inputStream,
long streamOffset, FTPDataTransferListener listener)
throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Select the type of contents.
int tp = type;
if (tp == TYPE_AUTO) {
tp = detectType(fileName);
}
if (tp == TYPE_TEXTUAL) {
communication.sendFTPCommand("TYPE A");
} else if (tp == TYPE_BINARY) {
communication.sendFTPCommand("TYPE I");
}
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
// Local abort state.
boolean wasAborted = false;
// Prepares the connection for the data transfer.
FTPDataTransferConnectionProvider provider = openDataTransferChannel();
// Send the STOR command.
communication.sendFTPCommand("APPE " + fileName);
try {
Socket dtConnection;
try {
dtConnection = provider.openDataTransferConnection();
} finally {
provider.dispose();
}
// Change the operation status.
synchronized (abortLock) {
ongoingDataTransfer = true;
aborted = false;
consumeAborCommandReply = false;
}
// Upload the stream.
try {
// Skips.
inputStream.skip(streamOffset);
// Opens the data transfer connection.
dataTransferOutputStream = dtConnection.getOutputStream();
// MODE Z enabled?
if (modezEnabled) {
dataTransferOutputStream = new DeflaterOutputStream(dataTransferOutputStream);
}
// Listeners.
if (listener != null) {
listener.started();
}
// Let's do it!
if (tp == TYPE_TEXTUAL) {
Reader reader = new InputStreamReader(inputStream);
Writer writer = new OutputStreamWriter(
dataTransferOutputStream, pickCharset());
char[] buffer = new char[SEND_AND_RECEIVE_BUFFER_SIZE];
int l;
while ((l = reader.read(buffer)) != -1) {
writer.write(buffer, 0, l);
writer.flush();
if (listener != null) {
listener.transferred(l);
}
}
} else if (tp == TYPE_BINARY) {
byte[] buffer = new byte[SEND_AND_RECEIVE_BUFFER_SIZE];
int l;
while ((l = inputStream.read(buffer)) != -1) {
dataTransferOutputStream.write(buffer, 0, l);
dataTransferOutputStream.flush();
if (listener != null) {
listener.transferred(l);
}
}
}
} catch (IOException e) {
synchronized (abortLock) {
if (aborted) {
if (listener != null) {
listener.aborted();
}
throw new FTPAbortedException();
} else {
if (listener != null) {
listener.failed();
}
throw new FTPDataTransferException(
"I/O error in data transfer", e);
}
}
} finally {
// Closing stream and data connection.
if (dataTransferOutputStream != null) {
try {
dataTransferOutputStream.close();
} catch (Throwable t) {
;
}
}
try {
dtConnection.close();
} catch (Throwable t) {
;
}
// Set to null the instance-level input stream.
dataTransferOutputStream = null;
// Change the operation status.
synchronized (abortLock) {
wasAborted = aborted;
ongoingDataTransfer = false;
aborted = false;
}
}
} finally {
r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 150 && r.getCode() != 125) {
throw new FTPException(r);
}
// Consumes the result reply of the transfer.
r = communication.readFTPReply();
if (!wasAborted && r.getCode() != 226) {
throw new FTPException(r);
}
// ABOR command response (if needed).
if (consumeAborCommandReply) {
communication.readFTPReply();
consumeAborCommandReply = false;
}
}
// Notifies the listener.
if (listener != null) {
listener.completed();
}
}
}
/**
* This method downloads a remote file from the server to a local file.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param remoteFileName
* The name of the file to download.
* @param localFile
* The local file.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void download(String remoteFileName, File localFile)
throws IllegalStateException, FileNotFoundException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
download(remoteFileName, localFile, 0, null);
}
/**
* This method downloads a remote file from the server to a local file.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param remoteFileName
* The name of the file to download.
* @param localFile
* The local file.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void download(String remoteFileName, File localFile,
FTPDataTransferListener listener) throws IllegalStateException,
FileNotFoundException, IOException, FTPIllegalReplyException,
FTPException, FTPDataTransferException, FTPAbortedException {
download(remoteFileName, localFile, 0, listener);
}
/**
* This method resumes a download operation from the remote server to a
* local file.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param remoteFileName
* The name of the file to download.
* @param localFile
* The local file.
* @param restartAt
* The restart point (number of bytes already downloaded). Use
* {@link FTPClient#isResumeSupported()} to check if the server
* supports resuming of broken data transfers.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void download(String remoteFileName, File localFile, long restartAt)
throws IllegalStateException, FileNotFoundException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
download(remoteFileName, localFile, restartAt, null);
}
/**
* This method resumes a download operation from the remote server to a
* local file.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param remoteFileName
* The name of the file to download.
* @param localFile
* The local file.
* @param restartAt
* The restart point (number of bytes already downloaded). Use
* {@link FTPClient#isResumeSupported()} to check if the server
* supports resuming of broken data transfers.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws FileNotFoundException
* If the supplied file cannot be found.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void download(String remoteFileName, File localFile, long restartAt,
FTPDataTransferListener listener) throws IllegalStateException,
FileNotFoundException, IOException, FTPIllegalReplyException,
FTPException, FTPDataTransferException, FTPAbortedException {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(localFile, restartAt > 0);
} catch (IOException e) {
throw new FTPDataTransferException(e);
}
try {
download(remoteFileName, outputStream, restartAt, listener);
} catch (IllegalStateException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (FTPIllegalReplyException e) {
throw e;
} catch (FTPException e) {
throw e;
} catch (FTPDataTransferException e) {
throw e;
} catch (FTPAbortedException e) {
throw e;
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
;
}
}
}
}
/**
* This method resumes a download operation from the remote server.
*
* Calling this method blocks the current thread until the operation is
* completed. The operation could be interrupted by another thread calling
* abortCurrentDataTransfer(). The method will break with a
* FTPAbortedException.
*
* @param fileName
* The name of the remote file.
* @param outputStream
* The destination stream of data read during the download.
* @param restartAt
* The restart point (number of bytes already downloaded). Use
* {@link FTPClient#isResumeSupported()} to check if the server
* supports resuming of broken data transfers.
* @param listener
* The listener for the operation. Could be null.
* @throws IllegalStateException
* If the client is not connected or not authenticated.
* @throws IOException
* If an I/O error occurs.
* @throws FTPIllegalReplyException
* If the server replies in an illegal way.
* @throws FTPException
* If the operation fails.
* @throws FTPDataTransferException
* If a I/O occurs in the data transfer connection. If you
* receive this exception the transfer failed, but the main
* connection with the remote FTP server is in theory still
* working.
* @throws FTPAbortedException
* If operation is aborted by another thread.
* @see FTPClient#abortCurrentDataTransfer(boolean)
*/
public void download(String fileName, OutputStream outputStream,
long restartAt, FTPDataTransferListener listener)
throws IllegalStateException, IOException,
FTPIllegalReplyException, FTPException, FTPDataTransferException,
FTPAbortedException {
synchronized (lock) {
// Is this client connected?
if (!connected) {
throw new IllegalStateException("Client not connected");
}
// Is this client authenticated?
if (!authenticated) {
throw new IllegalStateException("Client not authenticated");
}
// Select the type of contents.
int tp = type;
if (tp == TYPE_AUTO) {
tp = detectType(fileName);
}
if (tp == TYPE_TEXTUAL) {
communication.sendFTPCommand("TYPE A");
} else if (tp == TYPE_BINARY) {
communication.sendFTPCommand("TYPE I");
}
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
// Prepares the connection for the data transfer.
FTPDataTransferConnectionProvider provider = openDataTransferChannel();
// REST command (if supported and/or requested).
if (restSupported || restartAt > 0) {
boolean done = false;
try {
communication.sendFTPCommand("REST " + restartAt);
r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 350 && ((r.getCode() != 501 && r.getCode() != 502) || restartAt > 0)) {
throw new FTPException(r);
}
done = true;
} finally {
if (!done) {
provider.dispose();
}
}
}
// Local abort state.
boolean wasAborted = false;
// Send the RETR command.
communication.sendFTPCommand("RETR " + fileName);
try {
Socket dtConnection;
try {
dtConnection = provider.openDataTransferConnection();
} finally {
provider.dispose();
}
// Change the operation status.
synchronized (abortLock) {
ongoingDataTransfer = true;
aborted = false;
consumeAborCommandReply = false;
}
// Download the stream.
try {
// Opens the data transfer connection.
dataTransferInputStream = dtConnection.getInputStream();
// MODE Z enabled?
if (modezEnabled) {
dataTransferInputStream = new InflaterInputStream(dataTransferInputStream);
}
// Listeners.
if (listener != null) {
listener.started();
}
// Let's do it!
if (tp == TYPE_TEXTUAL) {
Reader reader = new InputStreamReader(
dataTransferInputStream, pickCharset());
Writer writer = new OutputStreamWriter(outputStream);
char[] buffer = new char[SEND_AND_RECEIVE_BUFFER_SIZE];
int l;
while ((l = reader.read(buffer, 0, buffer.length)) != -1) {
writer.write(buffer, 0, l);
writer.flush();
if (listener != null) {
listener.transferred(l);
}
}
} else if (tp == TYPE_BINARY) {
byte[] buffer = new byte[SEND_AND_RECEIVE_BUFFER_SIZE];
int l;
while ((l = dataTransferInputStream.read(buffer, 0,
buffer.length)) != -1) {
outputStream.write(buffer, 0, l);
if (listener != null) {
listener.transferred(l);
}
}
}
} catch (IOException e) {
synchronized (abortLock) {
if (aborted) {
if (listener != null) {
listener.aborted();
}
throw new FTPAbortedException();
} else {
if (listener != null) {
listener.failed();
}
throw new FTPDataTransferException(
"I/O error in data transfer", e);
}
}
} finally {
// Closing stream and data connection.
if (dataTransferInputStream != null) {
try {
dataTransferInputStream.close();
} catch (Throwable t) {
;
}
}
try {
dtConnection.close();
} catch (Throwable t) {
;
}
// Set to null the instance-level input stream.
dataTransferInputStream = null;
// Change the operation status.
synchronized (abortLock) {
wasAborted = aborted;
ongoingDataTransfer = false;
aborted = false;
}
}
} finally {
r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.getCode() != 150 && r.getCode() != 125) {
throw new FTPException(r);
}
// Consumes the result reply of the transfer.
r = communication.readFTPReply();
if (!wasAborted && r.getCode() != 226) {
throw new FTPException(r);
}
// ABOR command response (if needed).
if (consumeAborCommandReply) {
communication.readFTPReply();
consumeAborCommandReply = false;
}
}
// Notifies the listener.
if (listener != null) {
listener.completed();
}
}
}
/**
* This method detects the type for a file transfer.
*/
private int detectType(String fileName) throws IOException,
FTPIllegalReplyException, FTPException {
int start = fileName.lastIndexOf('.') + 1;
int stop = fileName.length();
if (start > 0 && start < stop - 1) {
String ext = fileName.substring(start, stop);
ext = ext.toLowerCase();
if (textualExtensionRecognizer.isTextualExt(ext)) {
return TYPE_TEXTUAL;
} else {
return TYPE_BINARY;
}
} else {
return TYPE_BINARY;
}
}
/**
* This method opens a data transfer channel.
*/
private FTPDataTransferConnectionProvider openDataTransferChannel()
throws IOException, FTPIllegalReplyException, FTPException,
FTPDataTransferException {
// MODE Z?
if (modezSupported && compressionEnabled) {
if (!modezEnabled) {
// Sends the MODE Z command.
communication.sendFTPCommand("MODE Z");
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.isSuccessCode()) {
modezEnabled = true;
}
}
} else {
if (modezEnabled) {
// Sends the MODE S command.
communication.sendFTPCommand("MODE S");
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (r.isSuccessCode()) {
modezEnabled = false;
}
}
}
// Active or passive?
if (passive) {
return openPassiveDataTransferChannel();
} else {
return openActiveDataTransferChannel();
}
}
/**
* This method opens a data transfer channel in active mode.
*/
private FTPDataTransferConnectionProvider openActiveDataTransferChannel()
throws IOException, FTPIllegalReplyException, FTPException,
FTPDataTransferException {
// Create a FTPDataTransferServer object.
FTPDataTransferServer server = new FTPDataTransferServer() {
public Socket openDataTransferConnection()
throws FTPDataTransferException {
Socket socket = super.openDataTransferConnection();
if (dataChannelEncrypted) {
try {
socket = ssl(socket, socket.getInetAddress().getHostName(), socket.getPort());
} catch (IOException e) {
try {
socket.close();
} catch (Throwable t) {
}
throw new FTPDataTransferException(e);
}
}
return socket;
}
};
int port = server.getPort();
int p1 = port >>> 8;
int p2 = port & 0xff;
int[] addr = pickLocalAddress();
// Send the port command.
communication.sendFTPCommand("PORT " + addr[0] + "," + addr[1] + "," + addr[2] + "," +
addr[3] + "," + p1 + "," + p2);
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
// Disposes.
server.dispose();
// Closes the already open connection (if any).
try {
Socket aux = server.openDataTransferConnection();
aux.close();
} catch (Throwable t) {
;
}
// Throws the exception.
throw new FTPException(r);
}
return server;
}
/**
* This method opens a data transfer channel in passive mode.
*/
private FTPDataTransferConnectionProvider openPassiveDataTransferChannel()
throws IOException, FTPIllegalReplyException, FTPException,
FTPDataTransferException {
// Send the PASV command.
communication.sendFTPCommand("PASV");
// Read the reply.
FTPReply r = communication.readFTPReply();
touchAutoNoopTimer();
if (!r.isSuccessCode()) {
throw new FTPException(r);
}
// Use a regexp to extract the remote address and port.
String addressAndPort = null;
String[] messages = r.getMessages();
for (int i = 0; i < messages.length; i++) {
Matcher m = PASV_PATTERN.matcher(messages[i]);
if (m.find()) {
int start = m.start();
int end = m.end();
addressAndPort = messages[i].substring(start, end);
break;
}
}
if (addressAndPort == null) {
// The remote server has not sent the coordinates for the
// data transfer connection.
throw new FTPIllegalReplyException();
}
// Parse the string extracted from the reply.
StringTokenizer st = new StringTokenizer(addressAndPort, ",");
int b1 = Integer.parseInt(st.nextToken());
int b2 = Integer.parseInt(st.nextToken());
int b3 = Integer.parseInt(st.nextToken());
int b4 = Integer.parseInt(st.nextToken());
int p1 = Integer.parseInt(st.nextToken());
int p2 = Integer.parseInt(st.nextToken());
final String pasvHost = b1 + "." + b2 + "." + b3 + "." + b4;
final int pasvPort = (p1 << 8) | p2;
FTPDataTransferConnectionProvider provider = new FTPDataTransferConnectionProvider() {
public Socket openDataTransferConnection() throws FTPDataTransferException {
// Establish the connection.
Socket dtConnection;
try {
String selectedHost = connector.getUseSuggestedAddressForDataConnections() ? pasvHost : host;
dtConnection = connector.connectForDataTransferChannel(selectedHost, pasvPort);
if (dataChannelEncrypted) {
dtConnection = ssl(dtConnection, selectedHost, pasvPort);
}
} catch (IOException e) {
throw new FTPDataTransferException("Cannot connect to the remote server", e);
}
return dtConnection;
}
public void dispose() {
// nothing to do
}
};
return provider;
}
/**
* If there's any ongoing data transfer operation, this method aborts it.
*
* @param sendAborCommand
* If true the client will negotiate the abort procedure with the
* server, through the standard FTP ABOR command. Otherwise the
* open data transfer connection will be closed without any
* advise has sent to the server.
* @throws IOException
* If the ABOR command cannot be sent due to any I/O error. This
* could happen only if force is false.
* @throws FTPIllegalReplyException
* If the server reply to the ABOR command is illegal. This
* could happen only if force is false.
*/
public void abortCurrentDataTransfer(boolean sendAborCommand)
throws IOException, FTPIllegalReplyException {
synchronized (abortLock) {
if (ongoingDataTransfer && !aborted) {
if (sendAborCommand) {
communication.sendFTPCommand("ABOR");
touchAutoNoopTimer();
consumeAborCommandReply = true;
}
if (dataTransferInputStream != null) {
try {
dataTransferInputStream.close();
} catch (Throwable t) {
;
}
}
if (dataTransferOutputStream != null) {
try {
dataTransferOutputStream.close();
} catch (Throwable t) {
;
}
}
aborted = true;
}
}
}
/**
* Returns the name of the charset that should be used in textual
* transmissions.
*
* @return The name of the charset that should be used in textual
* transmissions.
*/
private String pickCharset() {
if (charset != null) {
return charset;
} else if (utf8Supported) {
return "UTF-8";
} else {
return System.getProperty("file.encoding");
}
}
/**
* Picks the local address for an active data transfer operation.
*
* @return The local address as a 4 integer values array.
* @throws IOException
* If an unexpected I/O error occurs while trying to resolve the
* local address.
*/
private int[] pickLocalAddress() throws IOException {
// Forced address?
int[] ret = pickForcedLocalAddress();
// Auto-detect?
if (ret == null) {
ret = pickAutoDetectedLocalAddress();
}
// Returns.
return ret;
}
/**
* If a local address for active data transfers has been supplied through
* the {@link FTPKeys#ACTIVE_DT_HOST_ADDRESS}, it returns it as a 4 elements
* integer array; otherwise it returns null.
*
* @return The forced local address, or null.
*/
private int[] pickForcedLocalAddress() {
int[] ret = null;
String aux = System.getProperty(FTPKeys.ACTIVE_DT_HOST_ADDRESS);
if (aux != null) {
boolean valid = false;
StringTokenizer st = new StringTokenizer(aux, ".");
if (st.countTokens() == 4) {
valid = true;
int[] arr = new int[4];
for (int i = 0; i < 4; i++) {
String tk = st.nextToken();
try {
arr[i] = Integer.parseInt(tk);
} catch (NumberFormatException e) {
arr[i] = -1;
}
if (arr[i] < 0 || arr[i] > 255) {
valid = false;
break;
}
}
if (valid) {
ret = arr;
}
}
if (!valid) {
// warning to the developer
logger.warning("WARNING: invalid value \"" + aux
+ "\" for the " + FTPKeys.ACTIVE_DT_HOST_ADDRESS
+ " system property. The value should "
+ "be in the x.x.x.x form.");
}
}
return ret;
}
/**
* Auto-detects the local network address, and returns it in the form of a 4
* elements integer array.
*
* @return The detected local address.
* @throws IOException
* If an unexpected I/O error occurs while trying to resolve the
* local address.
*/
private int[] pickAutoDetectedLocalAddress() throws IOException {
InetAddress addressObj = InetAddress.getLocalHost();
byte[] addr = addressObj.getAddress();
int b1 = addr[0] & 0xff;
int b2 = addr[1] & 0xff;
int b3 = addr[2] & 0xff;
int b4 = addr[3] & 0xff;
int[] ret = { b1, b2, b3, b4 };
return ret;
}
public String toString() {
synchronized (lock) {
StringBuffer buffer = new StringBuffer();
buffer.append(getClass().getName());
buffer.append(" [connected=");
buffer.append(connected);
if (connected) {
buffer.append(", host=");
buffer.append(host);
buffer.append(", port=");
buffer.append(port);
}
buffer.append(", connector=");
buffer.append(connector);
buffer.append(", security=");
switch (security) {
case SECURITY_FTP:
buffer.append("SECURITY_FTP");
break;
case SECURITY_FTPS:
buffer.append("SECURITY_FTPS");
break;
case SECURITY_FTPES:
buffer.append("SECURITY_FTPES");
break;
}
buffer.append(", authenticated=");
buffer.append(authenticated);
if (authenticated) {
buffer.append(", username=");
buffer.append(username);
buffer.append(", password=");
StringBuffer buffer2 = new StringBuffer();
for (int i = 0; i < password.length(); i++) {
buffer2.append('*');
}
buffer.append(buffer2);
buffer.append(", restSupported=");
buffer.append(restSupported);
buffer.append(", utf8supported=");
buffer.append(utf8Supported);
buffer.append(", mlsdSupported=");
buffer.append(mlsdSupported);
buffer.append(", mode=modezSupported");
buffer.append(modezSupported);
buffer.append(", mode=modezEnabled");
buffer.append(modezEnabled);
}
buffer.append(", transfer mode=");
buffer.append(passive ? "passive" : "active");
buffer.append(", transfer type=");
switch (type) {
case TYPE_AUTO:
buffer.append("TYPE_AUTO");
break;
case TYPE_BINARY:
buffer.append("TYPE_BINARY");
break;
case TYPE_TEXTUAL:
buffer.append("TYPE_TEXTUAL");
break;
}
buffer.append(", textualExtensionRecognizer=");
buffer.append(textualExtensionRecognizer);
FTPListParser[] listParsers = getListParsers();
if (listParsers.length > 0) {
buffer.append(", listParsers=");
for (int i = 0; i < listParsers.length; i++) {
if (i > 0) {
buffer.append(", ");
}
buffer.append(listParsers[i]);
}
}
FTPCommunicationListener[] communicationListeners = getCommunicationListeners();
if (communicationListeners.length > 0) {
buffer.append(", communicationListeners=");
for (int i = 0; i < communicationListeners.length; i++) {
if (i > 0) {
buffer.append(", ");
}
buffer.append(communicationListeners[i]);
}
}
buffer.append(", autoNoopTimeout=");
buffer.append(autoNoopTimeout);
buffer.append("]");
return buffer.toString();
}
}
/**
* Starts the auto-noop timer thread.
*/
private void startAutoNoopTimer() {
if (autoNoopTimeout > 0) {
autoNoopTimer = new AutoNoopTimer();
autoNoopTimer.start();
}
}
/**
* Stops the auto-noop timer thread.
*
* @since 1.5
*/
private void stopAutoNoopTimer() {
if (autoNoopTimer != null) {
autoNoopTimer.interrupt();
autoNoopTimer = null;
}
}
/**
* Resets the auto noop timer.
*/
private void touchAutoNoopTimer() {
if (autoNoopTimer != null) {
nextAutoNoopTime = System.currentTimeMillis() + autoNoopTimeout;
}
}
/**
* The auto noop timer thread.
*/
private class AutoNoopTimer extends Thread {
public void run() {
synchronized (lock) {
if (nextAutoNoopTime <= 0 && autoNoopTimeout > 0) {
nextAutoNoopTime = System.currentTimeMillis() + autoNoopTimeout;
}
while (!Thread.interrupted() && autoNoopTimeout > 0) {
// Sleep till the next NOOP.
long delay = nextAutoNoopTime - System.currentTimeMillis();
if (delay > 0) {
try {
lock.wait(delay);
} catch (InterruptedException e) {
break;
}
}
// Is it really time to NOOP?
if (System.currentTimeMillis() >= nextAutoNoopTime) {
// Yes!
try {
noop();
} catch (Throwable t) {
; // ignore...
}
}
}
}
}
}
}