/* * 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; } }