/* * Copyright 2012 jMethods, Inc. * * 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. */ package com.myjavaworld.ftp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.InetAddress; import java.net.NoRouteToHostException; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import javax.net.SocketFactory; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.swing.event.EventListenerList; /** * This class represents a Control Connection as specified in the FTP protocol * specification. For more details refer to RFC 959. * * @author Sai Pullabhotla, psai [at] jMethods [dot] com * @version 2.0 */ public class ControlConnection implements FTPConstants { /** * <code>FTPClient</code> object that created this * <code>ControlConnection</code>. */ protected FTPClient client = null; /** * A Socket that represents a connection to the remote host. */ protected Socket socket = null; /** * A Reader object for receiving replies from the remote host. */ protected BufferedReader reader = null; /** * A Writer object that sends commands to the remote host. */ protected PrintStream writer = null; /** * List of registered listeners that are willing to get notifications about * the activity of this <code>ControlConnection</code>. */ protected EventListenerList listenerList = null; /** * Constructs a <code>ControlConnection</code> object. * * @param client * The <code>FTPClient</code> that created this control * connection. */ public ControlConnection(FTPClient client) { super(); this.client = client; listenerList = client.getListenerList(); } /** * Connects to the given remote host on the default FTP port as defined in * <code>FTPConstants.DEFAULT_PORT</code>. * * @param host * Host name or IP address of the remote host. * @exception ConnectionException * if unable to connect to the specified host. * @exception FTPException */ public void connect(String host) throws ConnectionException, FTPException { connect(host, DEFAULT_PORT); } /** * Connects to the specified remote host on the specified port number. * * @param host * Host name or IP address of the remote host. * @param port * Port number to connect to. * @exception ConnectionException * If unable to connect to the specified host. * @exception FTPException */ public void connect(String host, int port) throws ConnectionException, FTPException { try { SocketFactory factory = new CustomSocketFactory(client); socket = factory.createSocket(host, port); reader = new BufferedReader(new InputStreamReader( socket.getInputStream())); writer = new PrintStream(socket.getOutputStream(), true); } catch (UnknownHostException exp) { throw new ConnectionException(exp.toString()); } catch (NoRouteToHostException exp) { throw new ConnectionException(exp.toString()); } catch (SocketException exp) { throw new ConnectionException(exp.toString()); } catch (IOException exp) { throw new ConnectionException(exp.toString()); } try { socket.setSoTimeout(client.getTimeout()); socket.setKeepAlive(true); } catch (SocketException exp) { // We will ignore this exception as it is not very important even // if we failed to set the time out. stderr("Could not set timeout for the socket. Original exception " + "below. \n" + exp.getMessage()); } String reply = getReply(); if (reply.charAt(0) == '5' || reply.charAt(0) == '4') { throw new FTPException(reply); } } /** * Sends the given command <code>command</code>, to the remote host over * this <code>ControlConnection</code>. All commands will be appended with * telnet end of line (\r\n) characters before sending to the remote host. * * @param command * Command to send to the remote host. * @exception ConnectionException * if a network or IO error occurs while sending the command. */ public void sendCommand(String command) throws ConnectionException { try { writer.print(command + EOL); // writer.flush(); if (command.startsWith("PASS ")) { fireCommandSent(new ControlConnectionEvent(client, "PASS **********")); } else { fireCommandSent(new ControlConnectionEvent(client, command)); } if (writer.checkError()) { throw new IOException("Could not send command: " + command); } } catch (IOException exp) { throw new ConnectionException(exp.toString()); } } // public void sendData(int b) throws ConnectionException { // try { // writer.write(b); // if (writer.checkError()) { // throw new IOException( // "An error occurred while sending the byte " + b); // } // } // catch (IOException exp) { // throw new ConnectionException(exp.toString()); // } // } // // public void sendUrgentData(int b) throws ConnectionException { // try { // socket.sendUrgentData(b); // } // catch (IOException exp) { // throw new ConnectionException(exp.toString()); // } // } /** * reads a single FTP response from the remote host. If the reponse is a * multi-line reponse, all lines will be read until the response is * completely retrieved. * * @return response from the remote host. * @exception ConnectionException * if a network or IO error occurs while reading the * response. */ public synchronized String getReply() throws ConnectionException { String line = null; try { line = reader.readLine(); if (line == null) { throw new IOException("Connection Dropped. "); } String replyCode = "000"; replyCode = line.substring(0, 3); StringBuffer buffer = new StringBuffer(); buffer.append(line); if (line.charAt(3) == '-') { do { buffer.append(EOL); line = reader.readLine(); buffer.append(line); } while (!line.startsWith(replyCode + " ")); } String reply = buffer.toString(); fireReplyReceived(new ControlConnectionEvent(client, reply)); return reply; } catch (StringIndexOutOfBoundsException exp) { line = "000 Invalid Response Received from your FTP server. " + "The actual response is: [" + line + "]"; fireReplyReceived(new ControlConnectionEvent(client, line)); return line; } catch (IOException exp) { throw new ConnectionException(exp.toString()); } } /** * Executes the given command <code>command</code>. This method is similar * to calling the <code>sendCommand</code> and <code>getReply</code> methods * sequentially. * * @param command * The command to be sent to the remote host. * @return Reply from the remote host. * @exception ConnectionException * if a network or IO error occurs. */ public String executeCommand(String command) throws ConnectionException { sendCommand(command); return getReply(); } /** * Closes this <code>ControlConnection</code> by closing the socket to the * remote host and its associated strems. * * @exception IOException * if a network or IO error occurs while closing this * <code>ControlConnection</code>. */ public void close() throws IOException { if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } if (socket != null) { socket.close(); } reader = null; writer = null; socket = null; } /** * Returns the remote host's <code>InetAddress</code>. * * @return <code>InetAddress</code> of the remote host. */ public InetAddress getRemoteAddress() { return socket.getInetAddress(); } /** * Returns the fully qualified domain name of the remote host. * * @return Fully qualified domain name of the remote host. */ public String getRemoteHost() { return socket.getInetAddress().getHostName(); } /** * Returns the IP address of the remote host in xxx.xxx.xxx.xxx format. * * @return IP address of the remote host. */ public String getRemoteIPAddress() { return socket.getInetAddress().getHostAddress(); } /** * Returns the remote port number to which this * <code>ControlConnection</code> is connected to. * * @return Remote port number to which this <code>ControlConnection</code> * is connected to. */ public int getRemotePort() { return socket.getPort(); } /** * Returns the <code>InetAddress</code> of the local host. * * @return <code>InetAddress</code> of the local host. */ public InetAddress getLocalAddress() { return socket.getLocalAddress(); } /** * Returns the host name of the local host. * * @return Host name of the local host. */ public String getLocalHost() { return socket.getLocalAddress().getHostName(); } /** * Returns the IP address of the local host. * * @return IP address of the local host. */ public String getLocalIPAddress() { return socket.getLocalAddress().getHostAddress(); } /** * Retruns the local port number to which this <code>ControlConnection * </code> is connected to. * * @return Local port. */ public int getLocalPort() { return socket.getLocalPort(); } /** * Tells whether or not this <code>ControlConnection</code> is secured. * * @return <code>true</code>, if this connection is secured. * <code>false</code>, otherwise. */ public boolean isSecured() { if (socket != null) { if (socket instanceof SSLSocket) { return true; } } return false; } /** * Returns the SSLSession associated with this connection. * * @return SSL Session associated with this connection. Returns null, if * this connection is not a secured connection. */ public SSLSession getSSLSession() { if (!isSecured()) { return null; } return ((SSLSocket) socket).getSession(); } /** * Used to notify registered listeners that a command was sent to the remote * host. The command is wrapped in the <code>ControlConnectionEvent * </code> object. * * @param evt * <code>ControlConnectionEvent</code>. */ protected void fireCommandSent(ControlConnectionEvent evt) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ControlConnectionListener.class) { ((ControlConnectionListener) listeners[i + 1]).commandSent(evt); } } } /** * Used to notify the registered listeners that a reply was received from * the remote host. * * @param evt * <code>ControlConnectionEvent</code>. */ protected void fireReplyReceived(ControlConnectionEvent evt) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ControlConnectionListener.class) { ((ControlConnectionListener) listeners[i + 1]) .replyReceived(evt); } } } /** * Prints a given string to the standard output. * * @param message * The message to print. */ protected void stdout(String message) { System.out.println(message); } /** * Prints the given string to standard error. * * @param message * The message to print. */ protected void stderr(String message) { System.err.println(message); } }