/*
* Copyright 2010 netling project <http://netling.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netling.ftp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import org.netling.ProtocolCommandListener;
import org.netling.ProtocolCommandSupport;
import org.netling.SocketClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/***
* FTP provides the basic the functionality necessary to implement your
* own FTP client. It extends org.netling.SocketClient since
* extending TelnetClient was causing unwanted behavior (like connections
* that did not time out properly).
* <p>
* To derive the full benefits of the FTP class requires some knowledge
* of the FTP protocol defined in RFC 959. However, there is no reason
* why you should have to use the FTP class. The
* {@link org.netling.ftp.FTPClient} class,
* derived from FTP,
* implements all the functionality required of an FTP client. The
* FTP class is made public to provide access to various FTP constants
* and to make it easier for adventurous programmers (or those with
* special needs) to interact with the FTP protocol and implement their
* own clients. A set of methods with names corresponding to the FTP
* command names are provided to facilitate this interaction.
* <p>
* You should keep in mind that the FTP server may choose to prematurely
* close a connection if the client has been idle for longer than a
* given time period (usually 900 seconds). The FTP class will detect a
* premature FTP server connection closing when it receives a
* {@link org.netling.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE }
* response to a command.
* When that occurs, the FTP class method encountering that reply will throw
* an {@link org.netling.ftp.FTPConnectionClosedException}
* . <code>FTPConectionClosedException</code>
* is a subclass of <code> IOException </code> and therefore need not be
* caught separately, but if you are going to catch it separately, its
* catch block must appear before the more general <code> IOException </code>
* catch block. When you encounter an
* {@link org.netling.ftp.FTPConnectionClosedException}
* , you must disconnect the connection with
* {@link #disconnect disconnect() } to properly clean up the
* system resources used by FTP. Before disconnecting, you may check the
* last reply code and text with
* {@link #getReplyCode getReplyCode },
* {@link #getReplyString getReplyString },
* and {@link #getReplyStrings getReplyStrings}.
* You may avoid server disconnections while the client is idle by
* periodicaly sending NOOP commands to the server.
* <p>
* Rather than list it separately for each method, we mention here that
* every method communicating with the server and throwing an IOException
* can also throw a
* {@link MalformedServerReplyException}
* , which is a subclass
* of IOException. A MalformedServerReplyException will be thrown when
* the reply received from the server deviates enough from the protocol
* specification that it cannot be interpreted in a useful manner despite
* attempts to be as lenient as possible.
* <p>
* <p>
* @see FTPClient
* @see FTPConnectionClosedException
* @see MalformedServerReplyException
***/
public class FTP extends SocketClient
{
private static final Logger logger = LoggerFactory.getLogger(FTP.class);
/*** The default FTP data port (20). ***/
public static final int DEFAULT_DATA_PORT = 20;
/*** The default FTP control port (21). ***/
public static final int DEFAULT_CONTROL_PORT = 21;
/**
* Enumeration that represents the file type
*/
public enum FileType {
ASCII("A"),
EBCIDIC("E"),
BINARY("I"),
LOCAL("L");
private final String code;
FileType(String code) {
this.code = code;
}
public String code() { return code; }
}
/**
* Enumeration that represents the file text mode
*/
public enum FileFormat {
NON_PRINT_TEXT("N"),
TELNET_TEXT("T"),
CARRIAGE_CONTROL_TEXT("C");
private final String code;
FileFormat(String code) {
this.code = code;
}
public String code() { return code; }
}
/**
* Enumeration that represents a file structure
*/
public enum FileStructure {
/** continuous byte sequence */
FILE("F"),
/** sequence of records */
RECORD("R"),
/** set of independent indexed pages */
PAGE("P");
private final String code;
FileStructure(String code) {
this.code = code;
}
public String code() { return code; }
}
/**
* Enumeration that represents a file transfer mode
*/
public enum FileTransferMode {
STREAM("S"),
BLOCK("B"),
COMPRESSED("C");
private final String code;
FileTransferMode(String code) {
this.code = code;
}
public String code() { return code; }
}
// We have to ensure that the protocol communication is in ASCII
// but we use ISO-8859-1 just in case 8-bit characters cross
// the wire.
/**
* The default character encoding used for communicating over an
* FTP control connection. The default encoding is an
* ASCII-compatible encoding. Some FTP servers expect other
* encodings. You can change the encoding used by an FTP instance
* with {@link #setControlEncoding setControlEncoding}.
*/
public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1";
private final StringBuilder commandBuffer = new StringBuilder();
protected int replyCode;
protected ArrayList<String> replyLines;
protected boolean newReplyString;
protected String replyString;
protected String controlEncoding;
/**
* This is used to signal whether a block of multiline responses beginning
* with xxx must be terminated by the same numeric code xxx
* See section 4.2 of RFC 959 for details.
*/
protected boolean strictMultilineParsing = false;
/**
* Wraps SocketClient._input_ to facilitate the writing of text
* to the FTP control connection. Do not access the control
* connection via SocketClient._input_. This member starts
* with a null value, is initialized in {@link #_connectAction_},
* and set to null in {@link #disconnect}.
*/
protected BufferedReader controlInput;
/**
* Wraps SocketClient._output_ to facilitate the reading of text
* from the FTP control connection. Do not access the control
* connection via SocketClient._output_. This member starts
* with a null value, is initialized in {@link #_connectAction_},
* and set to null in {@link #disconnect}.
*/
protected BufferedWriter controlOutput;
/***
* A ProtocolCommandSupport object used to manage the registering of
* ProtocolCommandListeners and te firing of ProtocolCommandEvents.
***/
protected ProtocolCommandSupport commandSupport;
/***
* The default FTP constructor. Sets the default port to
* <code>DEFAULT_PORT</code> and initializes internal data structures
* for saving FTP reply information.
***/
public FTP()
{
super();
setDefaultPort(DEFAULT_CONTROL_PORT);
replyLines = new ArrayList<String>();
newReplyString = false;
replyString = null;
commandSupport = new ProtocolCommandSupport(this);
controlEncoding = DEFAULT_CONTROL_ENCODING;
}
// The RFC-compliant multiline termination check
boolean strictCheck(String line, String code) {
return (!(line.startsWith(code) && line.charAt(3) == ' '));
}
// The strict check is too strong a condition because of non-conforming ftp
// servers like ftp.funet.fi which sent 226 as the last line of a
// 426 multi-line reply in response to ls /. We relax the condition to
// test that the line starts with a digit rather than starting with
// the code.
boolean lenientCheck(String line) {
return (!(line.length() >= 4 && line.charAt(3) != '-' &&
Character.isDigit(line.charAt(0))));
}
/**
* Retrieve the reply from the server
* @throws IOException
*/
private void internalGetReply() throws IOException
{
int length;
newReplyString = true;
replyLines.clear();
String line = controlInput.readLine();
if (line == null)
throw new FTPConnectionClosedException(
"Connection closed without indication.");
if (logger.isTraceEnabled())
logger.trace("Received reply: {}", line);
// In case we run into an anomaly we don't want fatal index exceptions
// to be thrown.
length = line.length();
if (length < 3)
throw new MalformedServerReplyException(
"Truncated server reply: " + line);
String code = null;
try
{
code = line.substring(0, 3);
replyCode = Integer.parseInt(code);
}
catch (NumberFormatException e)
{
throw new MalformedServerReplyException(
"Could not parse response code.\nServer Reply: " + line);
}
replyLines.add(line);
// Get extra lines if message continues.
if (length > 3 && line.charAt(3) == '-')
{
do
{
line = controlInput.readLine();
if (line == null)
throw new FTPConnectionClosedException(
"Connection closed without indication.");
replyLines.add(line);
// The length() check handles problems that could arise from readLine()
// returning too soon after encountering a naked CR or some other
// anomaly.
}
while ( isStrictMultilineParsing() ? strictCheck(line, code) : lenientCheck(line));
}
if (commandSupport.getListenerCount() > 0) {
commandSupport.fireReplyReceived(replyCode, getReplyString());
}
if (replyCode == FTPReply.SERVICE_NOT_AVAILABLE.code()) {
throw new FTPConnectionClosedException("FTP response 421 received. Server closed connection.");
}
}
/**
* Initiates control connections and gets initial reply.
* Initializes {@link #controlInput} and {@link #controlOutput}.
*/
@Override
protected void onConnect() throws IOException
{
super.onConnect();
controlInput =
new BufferedReader(new InputStreamReader(socket.getInputStream(),
getControlEncoding()));
controlOutput =
new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),
getControlEncoding()));
internalGetReply();
// If we received code 120, we have to fetch completion reply.
if (FTPReply.isPositivePreliminary(replyCode))
internalGetReply();
}
/**
* Sets the character encoding used by the FTP control connection.
* Some FTP servers require that commands be issued in a non-ASCII
* encoding like UTF-8 so that filenames with multi-byte character
* representations (e.g, Big 8) can be specified.
*
* @param encoding The new character encoding for the control connection.
*/
public void setControlEncoding(String encoding) {
controlEncoding = encoding;
}
/**
* @return The character encoding used to communicate over the
* control connection.
*/
public String getControlEncoding() {
return controlEncoding;
}
/***
* Adds a ProtocolCommandListener. Delegates this task to
* {@link #commandSupport _commandSupport_ }.
* <p>
* @param listener The ProtocolCommandListener to add.
***/
public void addProtocolCommandListener(ProtocolCommandListener listener)
{
commandSupport.addProtocolCommandListener(listener);
}
/***
* Removes a ProtocolCommandListener. Delegates this task to
* {@link #commandSupport _commandSupport_ }.
* <p>
* @param listener The ProtocolCommandListener to remove.
***/
public void removeProtocolCommandListener(ProtocolCommandListener listener)
{
commandSupport.removeProtocolCommandListener(listener);
}
/***
* Closes the control connection to the FTP server and sets to null
* some internal data so that the memory may be reclaimed by the
* garbage collector. The reply text and code information from the
* last command is voided so that the memory it used may be reclaimed.
* Also sets {@link #controlInput} and {@link #controlOutput} to null.
* <p>
* @exception IOException If an error occurs while disconnecting.
***/
@Override
public void disconnect() throws IOException
{
super.disconnect();
controlInput = null;
controlOutput = null;
newReplyString = false;
replyString = null;
}
/***
* Sends an FTP command to the server, waits for a reply and returns the
* numerical response code. After invocation, for more detailed
* information, the actual reply text can be accessed by calling
* {@link #getReplyString getReplyString } or
* {@link #getReplyStrings getReplyStrings }.
* <p>
* @param command The text representation of the FTP command to send.
* @param args The arguments to the FTP command. If this parameter is
* set to null, then the command is sent with no argument.
* @return The integer value of the FTP reply code returned by the server
* in response to the command.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int sendCommand(String command, String args) throws IOException {
if (logger.isTraceEnabled())
logger.trace("Sending [command={}, args={}]", command, args);
String message;
commandBuffer.setLength(0);
commandBuffer.append(command);
if (args != null)
{
commandBuffer.append(' ');
commandBuffer.append(args);
}
commandBuffer.append(SocketClient.NETASCII_EOL);
if (controlOutput == null){
throw new IOException("Connection is not open");
}
try{
message = commandBuffer.toString();
controlOutput.write(message);
controlOutput.flush();
}
catch (SocketException e)
{
if (!isConnected() || !socketIsConnected(socket))
{
throw new FTPConnectionClosedException("Connection unexpectedly closed.");
}
else
{
throw e;
}
}
if (commandSupport.getListenerCount() > 0)
commandSupport.fireCommandSent(command, message);
internalGetReply();
return replyCode;
}
/**
* Checks if the socket is connected
*
* @param socket
* @return true if connected
*/
private boolean socketIsConnected(Socket socket)
{
if (socket == null)
{
return false;
}
return socket.isConnected();
}
/***
* Sends an FTP command to the server, waits for a reply and returns the
* numerical response code. After invocation, for more detailed
* information, the actual reply text can be accessed by calling
* {@link #getReplyString getReplyString } or
* {@link #getReplyStrings getReplyStrings }.
* <p>
* @param command The FTPCommand constant corresponding to the FTP command
* to send.
* @param args The arguments to the FTP command. If this parameter is
* set to null, then the command is sent with no argument.
* @return The integer value of the FTP reply code returned by the server
* in response to the command.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int sendCommand(FTPCommand command, String args) throws IOException
{
return sendCommand(command.command(), args);
}
/***
* Sends an FTP command with no arguments to the server, waits for a
* reply and returns the numerical response code. After invocation, for
* more detailed information, the actual reply text can be accessed by
* calling {@link #getReplyString getReplyString } or
* {@link #getReplyStrings getReplyStrings }.
* <p>
* @param command The text representation of the FTP command to send.
* @return The integer value of the FTP reply code returned by the server
* in response to the command.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int sendCommand(String command) throws IOException
{
return sendCommand(command, null);
}
/***
* Sends an FTP command with no arguments to the server, waits for a
* reply and returns the numerical response code. After invocation, for
* more detailed information, the actual reply text can be accessed by
* calling {@link #getReplyString getReplyString } or
* {@link #getReplyStrings getReplyStrings }.
* <p>
* @param command The FTPCommand constant corresponding to the FTP command
* to send.
* @return The integer value of the FTP reply code returned by the server
* in response to the command.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int sendCommand(FTPCommand command) throws IOException
{
return sendCommand(command, null);
}
/***
* Returns the integer value of the reply code of the last FTP reply.
* You will usually only use this method after you connect to the
* FTP server to check that the connection was successful since
* <code> connect </code> is of type void.
* <p>
* @return The integer value of the reply code of the last FTP reply.
***/
public int getReplyCode()
{
return replyCode;
}
/***
* Fetches a reply from the FTP server and returns the integer reply
* code. After calling this method, the actual reply text can be accessed
* from either calling {@link #getReplyString getReplyString } or
* {@link #getReplyStrings getReplyStrings }. Only use this
* method if you are implementing your own FTP client or if you need to
* fetch a secondary response from the FTP server.
* <p>
* @return The integer value of the reply code of the fetched FTP reply.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while receiving the
* server reply.
***/
public int getReply() throws IOException
{
internalGetReply();
return replyCode;
}
/***
* Returns the lines of text from the last FTP server response as an array
* of strings, one entry per line. The end of line markers of each are
* stripped from each line.
* <p>
* @return The lines of text from the last FTP response as an array.
***/
public String[] getReplyStrings()
{
return replyLines.toArray(new String[replyLines.size()]);
}
/***
* Returns the entire text of the last FTP server response exactly
* as it was received, including all end of line markers in NETASCII
* format.
* <p>
* @return The entire text from the last FTP response as a String.
***/
public String getReplyString()
{
final StringBuilder buffer;
if (!newReplyString) {
return replyString;
}
buffer = new StringBuilder(256);
for (String line : replyLines) {
buffer.append(line);
buffer.append(SocketClient.NETASCII_EOL);
}
newReplyString = false;
return (replyString = buffer.toString());
}
/***
* A convenience method to send the FTP USER command to the server,
* receive the reply, and return the reply code.
* <p>
* @param username The username to login under.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int user(String username) throws IOException
{
return sendCommand(FTPCommand.USER, username);
}
/**
* A convenience method to send the FTP PASS command to the server,
* receive the reply, and return the reply code.
* @param password The plain text password of the username being logged into.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
*/
public int pass(String password) throws IOException
{
return sendCommand(FTPCommand.PASS, password);
}
/***
* A convenience method to send the FTP ACCT command to the server,
* receive the reply, and return the reply code.
* <p>
* @param account The account name to access.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int acct(String account) throws IOException
{
return sendCommand(FTPCommand.ACCT, account);
}
/***
* A convenience method to send the FTP ABOR command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int abor() throws IOException
{
return sendCommand(FTPCommand.ABOR);
}
/***
* A convenience method to send the FTP CWD command to the server,
* receive the reply, and return the reply code.
* <p>
* @param directory The new working directory.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int cwd(String directory) throws IOException
{
return sendCommand(FTPCommand.CWD, directory);
}
/***
* A convenience method to send the FTP CDUP command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int cdup() throws IOException
{
return sendCommand(FTPCommand.CDUP);
}
/***
* A convenience method to send the FTP QUIT command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int quit() throws IOException
{
return sendCommand(FTPCommand.QUIT);
}
/***
* A convenience method to send the FTP REIN command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int rein() throws IOException
{
return sendCommand(FTPCommand.REIN);
}
/***
* A convenience method to send the FTP SMNT command to the server,
* receive the reply, and return the reply code.
* <p>
* @param dir The directory name.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int smnt(String dir) throws IOException
{
return sendCommand(FTPCommand.SMNT, dir);
}
/***
* A convenience method to send the FTP PORT command to the server,
* receive the reply, and return the reply code.
* <p>
* @param host The host owning the port.
* @param port The new port.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int port(InetAddress host, int port) throws IOException
{
int num;
StringBuilder info = new StringBuilder(24);
info.append(host.getHostAddress().replace('.', ','));
num = port >>> 8;
info.append(',');
info.append(num);
info.append(',');
num = port & 0xff;
info.append(num);
return sendCommand(FTPCommand.PORT, info.toString());
}
/***
* A convenience method to send the FTP EPRT command to the server,
* receive the reply, and return the reply code.
*
* Examples:
* <code>
* <ul>
* <li>EPRT |1|132.235.1.2|6275|</li>
* <li>EPRT |2|1080::8:800:200C:417A|5282|</li>
* </ul>
* </code>
* <p>
* @see "http://www.faqs.org/rfcs/rfc2428.html"
*
* @param host The host owning the port.
* @param port The new port.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int eprt(InetAddress host, int port) throws IOException
{
int num;
StringBuilder info = new StringBuilder();
String h;
// If IPv6, trim the zone index
h = host.getHostAddress();
num = h.indexOf("%");
if (num > 0)
h = h.substring(0, num);
info.append("|");
if (host instanceof Inet4Address)
info.append("1");
else if (host instanceof Inet6Address)
info.append("2");
info.append("|");
info.append(h);
info.append("|");
info.append(port);
info.append("|");
return sendCommand(FTPCommand.EPRT, info.toString());
}
/***
* A convenience method to send the FTP PASV command to the server,
* receive the reply, and return the reply code. Remember, it's up
* to you to interpret the reply string containing the host/port
* information.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int pasv() throws IOException
{
return sendCommand(FTPCommand.PASV);
}
/***
* A convenience method to send the FTP EPSV command to the server,
* receive the reply, and return the reply code. Remember, it's up
* to you to interpret the reply string containing the host/port
* information.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int epsv() throws IOException
{
return sendCommand(FTPCommand.EPSV);
}
/**
* A convenience method to send the FTP TYPE command for text files
* to the server, receive the reply, and return the reply code.
* @param fileType The type of the file
* @param formatOrByteSize The format of the file.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
*/
public int type(FileType fileType, FileFormat format) throws IOException
{
if (fileType == FileType.LOCAL)
throw new IllegalArgumentException("Cannot call type(FileType,FileFormat) with LOCAL file type: use type(FileType, int) instead");
final StringBuilder arg = new StringBuilder();
arg.append(fileType.code());
arg.append(' ');
arg.append(format.code());
return sendCommand(FTPCommand.TYPE, arg.toString());
}
/**
* A convenience method to send the FTP TYPE command to the server,
* receive the reply, and return the reply code. This method should
* be called for a {@link FileType} of type {@link FileType#LOCAL} only
* <p>
* @param fileType The type of the file (should be {@link FileType#LOCAL})
* @param sizeBytes The size of the file in bytes
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
*/
public int type(FileType fileType, int sizeBytes) throws IOException {
if (fileType != FileType.LOCAL)
throw new IllegalArgumentException("Can only call type(FileType, int) with LOCAL file types");
return sendCommand(FTPCommand.TYPE, String.valueOf(sizeBytes));
}
/**
* A convenience method to send the FTP TYPE command to the server,
* receive the reply, and return the reply code.
* <p>
* @param fileType The type of the file.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
*/
public int type(FileType fileType) throws IOException
{
return sendCommand(FTPCommand.TYPE,
fileType.code());
}
/***
* A convenience method to send the FTP STRU command to the server,
* receive the reply, and return the reply code.
* <p>
* @param structure The structure of the file (one of the
* <code>_STRUCTURE</code> constants).
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int stru(FileStructure structure) throws IOException
{
return sendCommand(FTPCommand.STRU,
structure.code());
}
/***
* A convenience method to send the FTP MODE command to the server,
* receive the reply, and return the reply code.
* <p>
* @param mode The transfer mode to use (one of the
* <code>TRANSFER_MODE</code> constants).
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int mode(FileTransferMode mode) throws IOException
{
return sendCommand(FTPCommand.MODE,
mode.code());
}
/***
* A convenience method to send the FTP RETR command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @param pathname The pathname of the file to retrieve.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int retr(String pathname) throws IOException
{
return sendCommand(FTPCommand.RETR, pathname);
}
/***
* A convenience method to send the FTP STOR command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @param pathname The pathname to use for the file when stored at
* the remote end of the transfer.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int stor(String pathname) throws IOException
{
return sendCommand(FTPCommand.STOR, pathname);
}
/***
* A convenience method to send the FTP STOU command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int stou() throws IOException
{
return sendCommand(FTPCommand.STOU);
}
/***
* A convenience method to send the FTP STOU command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* @param pathname The base pathname to use for the file when stored at
* the remote end of the transfer. Some FTP servers
* require this.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
*/
public int stou(String pathname) throws IOException
{
return sendCommand(FTPCommand.STOU, pathname);
}
/***
* A convenience method to send the FTP APPE command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @param pathname The pathname to use for the file when stored at
* the remote end of the transfer.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int appe(String pathname) throws IOException
{
return sendCommand(FTPCommand.APPE, pathname);
}
/***
* A convenience method to send the FTP ALLO command to the server,
* receive the reply, and return the reply code.
* <p>
* @param bytes The number of bytes to allocate.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int allo(int bytes) throws IOException
{
return sendCommand(FTPCommand.ALLO, Integer.toString(bytes));
}
/**
* A convenience method to send the FTP FEAT command to the server, receive the reply,
* and return the reply code.
* @return The reply code received by the server
* @throws IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
*/
public int feat() throws IOException
{
return sendCommand(FTPCommand.FEAT);
}
/***
* A convenience method to send the FTP ALLO command to the server,
* receive the reply, and return the reply code.
* <p>
* @param bytes The number of bytes to allocate.
* @param recordSize The size of a record.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int allo(int bytes, int recordSize) throws IOException
{
return sendCommand(FTPCommand.ALLO, Integer.toString(bytes) + " R " +
Integer.toString(recordSize));
}
/***
* A convenience method to send the FTP REST command to the server,
* receive the reply, and return the reply code.
* <p>
* @param marker The marker at which to restart a transfer.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int rest(String marker) throws IOException
{
return sendCommand(FTPCommand.REST, marker);
}
/**
*
**/
public int mdtm(String file) throws IOException
{
return sendCommand(FTPCommand.MDTM, file);
}
/**
* A convenience method to send the FTP MFMT command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname The pathname for which mtime is to be changed
* @param timeval Timestamp in <code>YYYYMMDDhhmmss</code> format
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
* @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a>
**/
public int mfmt(String pathname, String timeval) throws IOException
{
return sendCommand(FTPCommand.MFMT, timeval + " " + pathname);
}
/***
* A convenience method to send the FTP RNFR command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname The pathname to rename from.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int rnfr(String pathname) throws IOException
{
return sendCommand(FTPCommand.RNFR, pathname);
}
/***
* A convenience method to send the FTP RNTO command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname The pathname to rename to
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int rnto(String pathname) throws IOException
{
return sendCommand(FTPCommand.RNTO, pathname);
}
/***
* A convenience method to send the FTP DELE command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname The pathname to delete.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int dele(String pathname) throws IOException
{
return sendCommand(FTPCommand.DELE, pathname);
}
/***
* A convenience method to send the FTP RMD command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname The pathname of the directory to remove.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int rmd(String pathname) throws IOException
{
return sendCommand(FTPCommand.RMD, pathname);
}
/***
* A convenience method to send the FTP MKD command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname The pathname of the new directory to create.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int mkd(String pathname) throws IOException
{
return sendCommand(FTPCommand.MKD, pathname);
}
/***
* A convenience method to send the FTP PWD command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int pwd() throws IOException
{
return sendCommand(FTPCommand.PWD);
}
/***
* A convenience method to send the FTP LIST command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int list() throws IOException
{
return sendCommand(FTPCommand.LIST);
}
/***
* A convenience method to send the FTP LIST command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @param pathname The pathname to list.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int list(String pathname) throws IOException
{
return sendCommand(FTPCommand.LIST, pathname);
}
/***
* A convenience method to send the FTP NLST command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int nlst() throws IOException
{
return sendCommand(FTPCommand.NLST);
}
/***
* A convenience method to send the FTP NLST command to the server,
* receive the reply, and return the reply code. Remember, it is up
* to you to manage the data connection. If you don't need this low
* level of access, use {@link org.netling.ftp.FTPClient}
* , which will handle all low level details for you.
* <p>
* @param pathname The pathname to list.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int nlst(String pathname) throws IOException
{
return sendCommand(FTPCommand.NLST, pathname);
}
/***
* A convenience method to send the FTP SITE command to the server,
* receive the reply, and return the reply code.
* <p>
* @param parameters The site parameters to send.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int site(String parameters) throws IOException
{
return sendCommand(FTPCommand.SITE, parameters);
}
/***
* A convenience method to send the FTP SYST command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int syst() throws IOException
{
return sendCommand(FTPCommand.SYST);
}
/***
* A convenience method to send the FTP STAT command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int stat() throws IOException
{
return sendCommand(FTPCommand.STAT);
}
/***
* A convenience method to send the FTP STAT command to the server,
* receive the reply, and return the reply code.
* <p>
* @param pathname A pathname to list.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int stat(String pathname) throws IOException
{
return sendCommand(FTPCommand.STAT, pathname);
}
/***
* A convenience method to send the FTP HELP command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int help() throws IOException
{
return sendCommand(FTPCommand.HELP);
}
/***
* A convenience method to send the FTP HELP command to the server,
* receive the reply, and return the reply code.
* <p>
* @param command The command name on which to request help.
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int help(String command) throws IOException
{
return sendCommand(FTPCommand.HELP, command);
}
/***
* A convenience method to send the FTP NOOP command to the server,
* receive the reply, and return the reply code.
* <p>
* @return The reply code received from the server.
* @exception FTPConnectionClosedException
* If the FTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send FTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending the
* command or receiving the server reply.
***/
public int noop() throws IOException
{
return sendCommand(FTPCommand.NOOP);
}
/**
* Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2.
* @return True if strict, false if lenient
*
*/
public boolean isStrictMultilineParsing() {
return strictMultilineParsing;
}
/**
* Set strict multiline parsing.
* @param strictMultilineParsing
*
*/
public void setStrictMultilineParsing(boolean strictMultilineParsing) {
this.strictMultilineParsing = strictMultilineParsing;
}
/**
* Set the {@link BufferedReader} for control connection commands.
* Useful for testing purposes.
* @param controlInput
*/
void setControlInput(BufferedReader controlInput) {
this.controlInput = controlInput;
}
/**
* Set the {@link BufferedWriter} for control connection commands.
* Useful for testing purposes.
* @param controlOutput
*/
void setControlOutput(BufferedWriter controlOutput) {
this.controlOutput = controlOutput;
}
/**
* Get the control connection input reader
* @return A {@link BufferedReader} instance
*/
BufferedReader getControlInput() {
return controlInput;
}
/**
* Get the control connection output writer
* @return A {@link BufferedWriter} instance
*/
BufferedWriter getControlOutput() {
return controlOutput;
}
}