Package com.enterprisedt.net.ftp

Source Code of com.enterprisedt.net.ftp.FTPControlSocket

/**
*
*  Java FTP client library.
*
*  Copyright (C) 2000-2003 Enterprise Distributed Technologies Ltd
*
*  www.enterprisedt.com
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*  Bug fixes, suggestions and comments should be sent to bruce@enterprisedt.com
*
*  Change Log:
*
*        $Log: FTPControlSocket.java,v $
*        Revision 1.1.1.1  2005/06/23 15:22:59  smontoro
*        hipergate backend
*
*        Revision 1.1  2004/02/07 03:15:20  hipergate
*        v2.0 pre-alpha
*
*        Revision 1.6  2003/05/31 14:53:44  bruceb
*        1.2.2 changes
*
*        Revision 1.5  2003/01/29 22:46:08  bruceb
*        minor changes
*
*        Revision 1.4  2002/11/19 22:01:25  bruceb
*        changes for 1.2
*
*        Revision 1.3  2001/10/09 20:53:46  bruceb
*        Active mode changes
*
*        Revision 1.1  2001/10/05 14:42:04  bruceb
*        moved from old project
*
*
*/

package com.enterprisedt.net.ftp;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;

import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;

/**
*  Supports client-side FTP operations
*
@author             Bruce Blackshaw
*      @version        $Revision: 1.1.1.1 $
*
*/
public class FTPControlSocket {

     /**
      *  Revision control id
      */
     private static String cvsId = "@(#)$Id: FTPControlSocket.java,v 1.1.1.1 2005/06/23 15:22:59 smontoro Exp $";

     /**
      *   Standard FTP end of line sequence
      */
     static final String EOL = "\r\n";

     /**
      *   The control port number for FTP
      */
     static final int CONTROL_PORT = 21;
    
     /**
      *   Used to flag messages
      */
     private static final String DEBUG_ARROW = "---> ";
    
     /**
      *   Start of password message
      */
     private static final String PASSWORD_MESSAGE = DEBUG_ARROW + "PASS";

     /**
      *   Controls if responses sent back by the
      *   server are sent to assigned output stream
      */
     private boolean debugResponses = false;

     /**
      *  Output stream debug is written to,
      *  stdout by default
      */
     private PrintWriter log = new PrintWriter(System.out);

     /**
      *  The underlying socket.
      */
     private Socket controlSock = null;

     /**
      *  The write that writes to the control socket
      */
     private Writer writer = null;

     /**
      *  The reader that reads control data from the
      *  control socket
      */
     private BufferedReader reader = null;

     /**
      *   Constructor. Performs TCP connection and
      *   sets up reader/writer. Allows different control
      *   port to be used
      *
      *   @param   remoteHost   Remote hostname
      *   @param   controlPort  port for control stream
      *   @param   millis       the length of the timeout, in milliseconds
      *   @param   log          the new logging stream
      */
     public FTPControlSocket(String remoteHost, int controlPort,
                             PrintWriter log, int timeout)
         throws IOException, FTPException {

         setLogStream(log);

         // ensure we get debug from initial connection sequence
         debugResponses(true);
         controlSock = new Socket(remoteHost, controlPort);
         setTimeout(timeout);
         initStreams();
         validateConnection();

         // switch off debug - user can switch on from this point
         debugResponses(false);
     }

     /**
      *   Constructor. Performs TCP connection and
      *   sets up reader/writer. Allows different control
      *   port to be used
      *
      *   @param   remoteAddr   Remote inet address
      *   @param   controlPort  port for control stream
      *   @param   millis       the length of the timeout, in milliseconds
      *   @param   log          the new logging stream
      */
     public FTPControlSocket(InetAddress remoteAddr, int controlPort,
                             PrintWriter log, int timeout)
         throws IOException, FTPException {
        
         setLogStream(log);

         // ensure we get debug from initial connection sequence
         debugResponses(true);
         controlSock = new Socket(remoteAddr, controlPort);
         setTimeout(timeout);
         initStreams();
         validateConnection();

         // switch off debug - user can switch on from this point
         debugResponses(false);
     }


     /**
      *   Checks that the standard 220 reply is returned
      *   following the initiated connection
      */
     private void validateConnection()
         throws IOException, FTPException {

         String reply = readReply();
         validateReply(reply, "220");
     }


     /**
      *  Obtain the reader/writer streams for this
      *  connection
      */
     private void initStreams()
         throws IOException {

         // input stream
         InputStream is = controlSock.getInputStream();
         reader = new BufferedReader(new InputStreamReader(is));

         // output stream
         OutputStream os = controlSock.getOutputStream();
         writer = new OutputStreamWriter(os);
     }


     /**
      *  Get the name of the remote host
      *
      *  @return  remote host name
      */
     String getRemoteHostName() {
         InetAddress addr = controlSock.getInetAddress();
         return addr.getHostName();
     }


    /**
     *   Set the TCP timeout on the underlying control socket.
     *
     *   If a timeout is set, then any operation which
     *   takes longer than the timeout value will be
     *   killed with a java.io.InterruptedException.
     *
     *   @param millis The length of the timeout, in milliseconds
     */
    void setTimeout(int millis)
        throws IOException {

        if (controlSock == null)
            throw new IllegalStateException(
                        "Failed to set timeout - no control socket");

        controlSock.setSoTimeout(millis);
    }


     /**
      *  Quit this FTP session and clean up.
      */
     public void logout()
         throws IOException {

         if (log != null) {
           log.flush();
           log = null;
         }

         IOException ex = null;
         try {
             writer.close();
         }
         catch (IOException e) {
             ex = e;
         }
         try {
             reader.close();
         }
         catch (IOException e) {
             ex = e;
         }
         try {
             controlSock.close();
         }
         catch (IOException e) {
             ex = e;
         }
         if (ex != null)
             throw ex;
     }


     /**
      *  Request a data socket be created on the
      *  server, connect to it and return our
      *  connected socket.
      *
      *  @param  active   if true, create in active mode, else
      *                   in passive mode
      *  @return  connected data socket
      */
     FTPDataSocket createDataSocket(FTPConnectMode connectMode)
         throws IOException, FTPException {

        if (connectMode == FTPConnectMode.ACTIVE) {
            return new FTPDataSocket(createDataSocketActive());
        }
        else { // PASV
            return new FTPDataSocket(createDataSocketPASV());
        }
     }


     /**
      *  Request a data socket be created on the Client
      *  client on any free port, do not connect it to yet.
      *
      *  @return  not connected data socket
      */
     ServerSocket createDataSocketActive()
         throws IOException, FTPException {

        // use any available port
        ServerSocket socket = new ServerSocket(0);

        // get the local address to which the control socket is bound.
        InetAddress localhost =  controlSock.getLocalAddress();

        // send the PORT command to the server
        setDataPort(localhost, (short)socket.getLocalPort());

        return socket;
     }



    /**
     *  Helper method to convert a byte into an unsigned short value
     *
     *  @param  value   value to convert
     *  @return  the byte value as an unsigned short
     */
    private short toUnsignedShort(byte value) {
        return ( value < 0 )
            ? (short) (value + 256)
            : (short) value;
     }

    /**
     *  Convert a short into a byte array
     *
     *  @param  value   value to convert
     *  @return  a byte array
     */
    protected byte[] toByteArray (short value) {

        byte[] bytes = new byte[2];
        bytes[0] = (byte) (value >> 8);     // bits 1- 8
        bytes[1] = (byte) (value & 0x00FF); // bits 9-16
        return bytes;
    }


    /**
     *  Sets the data port on the server, i.e. sends a PORT
     *  command
     *
     *  @param  host    the local host the server will connect to
     *  @param  portNo  the port number to connect to
     */
    private void setDataPort(InetAddress host, short portNo)
        throws IOException, FTPException {

        byte[] hostBytes = host.getAddress();
        byte[] portBytes = toByteArray(portNo);

        // assemble the PORT command
        String cmd = new StringBuffer ("PORT ")
            .append (toUnsignedShort (hostBytes[0])) .append (",")
            .append (toUnsignedShort (hostBytes[1])) .append (",")
            .append (toUnsignedShort (hostBytes[2])) .append (",")
            .append (toUnsignedShort (hostBytes[3])) .append (",")
            .append (toUnsignedShort (portBytes[0])) .append (",")
            .append (toUnsignedShort (portBytes[1])) .toString ();

        // send command and check reply
        String reply = sendCommand(cmd);
        validateReply(reply, "200");
     }


     /**
      *  Request a data socket be created on the
      *  server, connect to it and return our
      *  connected socket.
      *
      *  @return  connected data socket
      */
     Socket createDataSocketPASV()
         throws IOException, FTPException {

         // PASSIVE command - tells the server to listen for
         // a connection attempt rather than initiating it
         String reply = sendCommand("PASV");
         validateReply(reply, "227");

         // The reply to PASV is in the form:
         // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
         // where h1..h4 are the IP address to connect and
         // p1,p2 the port number
         // Example:
         // 227 Entering Passive Mode (128,3,122,1,15,87).
         // NOTE: PASV command in IBM/Mainframe returns the string
         // 227 Entering Passive Mode 128,3,122,1,15,87  (missing
         // brackets)

         // extract the IP data string from between the brackets
         int startIP = reply.indexOf('(');
         int endIP = reply.indexOf(')');

         // allow for IBM missing brackets around IP address
         if (startIP < 0 && endIP < 0) {
             startIP = reply.toUpperCase().lastIndexOf("MODE") + 4;
             endIP = reply.length();
         }
                 
         String ipData = reply.substring(startIP+1,endIP);
         int parts[] = new int[6];

         int len = ipData.length();
         int partCount = 0;
         StringBuffer buf = new StringBuffer();

         // loop thru and examine each char
         for (int i = 0; i < len && partCount <= 6; i++) {

             char ch = ipData.charAt(i);
             if (Character.isDigit(ch))
                 buf.append(ch);
             else if (ch != ',') {
                 throw new FTPException("Malformed PASV reply: " + reply);
             }

             // get the part
             if (ch == ',' || i+1 == len) { // at end or at separator
                 try {
                     parts[partCount++] = Integer.parseInt(buf.toString());
                     buf.setLength(0);
                 }
                 catch (NumberFormatException ex) {
                     throw new FTPException("Malformed PASV reply: " + reply);
                 }
             }
         }

         // assemble the IP address
         // we try connecting, so we don't bother checking digits etc
         String ipAddress = parts[0] + "."+ parts[1]+ "." +
             parts[2] + "." + parts[3];

         // assemble the port number
         int port = (parts[4] << 8) + parts[5];

         // create the socket
         return new Socket(ipAddress, port);
     }



     /**
      *  Send a command to the FTP server and
      *  return the server's reply
      *
      *  @return  reply to the supplied command
      */
     String sendCommand(String command)
         throws IOException {

       log(DEBUG_ARROW + command);

         // send it
         writer.write(command + EOL);
         writer.flush();

         // and read the result
         return readReply();
     }

     /**
      *  Read the FTP server's reply to a previously
      *  issued command. RFC 959 states that a reply
      *  consists of the 3 digit code followed by text.
      *  The 3 digit code is followed by a hyphen if it
      *  is a muliline response, and the last line starts
      *  with the same 3 digit code.
      *
      *  @return  reply string
      */
     String readReply()
         throws IOException {

         String firstLine = reader.readLine();
         if (firstLine == null || firstLine.length() == 0)
             throw new IOException("Unexpected null reply received");

         StringBuffer reply = new StringBuffer(firstLine);

     log(reply.toString());
         String replyCode = reply.toString().substring(0, 3);

         // check for multiline response and build up
         // the reply
         if (reply.charAt(3) == '-') {

             boolean complete = false;
             while (!complete) {
                 String line = reader.readLine();
                 if (line == null)
                     throw new IOException("Unexpected null reply received");

           log(line);

                 if (line.length() > 3 &&
                     line.substring(0, 3).equals(replyCode) &&
                     line.charAt(3) == ' ') {
                     reply.append(line.substring(3));
                     complete = true;
                 }
                 else { // not the last line
                     reply.append(" ");
                     reply.append(line);
                 }
             } // end while
         } // end if
         return reply.toString();
     }


     /**
      *  Validate the response the host has supplied against the
      *  expected reply. If we get an unexpected reply we throw an
      *  exception, setting the message to that returned by the
      *  FTP server
      *
      *  @param   reply              the entire reply string we received
      *  @param   expectedReplyCode  the reply we expected to receive
      *
      */
     FTPReply validateReply(String reply, String expectedReplyCode)
         throws IOException, FTPException {

         // all reply codes are 3 chars long
         String replyCode = reply.substring(0, 3);
         String replyText = reply.substring(4);
         FTPReply replyObj = new FTPReply(replyCode, replyText);
        
         if (replyCode.equals(expectedReplyCode))
             return replyObj;

         // if unexpected reply, throw an exception
         throw new FTPException(replyText, replyCode);        
     }

     /**
      *  Validate the response the host has supplied against the
      *  expected reply. If we get an unexpected reply we throw an
      *  exception, setting the message to that returned by the
      *  FTP server
      *
      *  @param   reply               the entire reply string we received
      *  @param   expectedReplyCodes  array of expected replies
      *  @return  an object encapsulating the server's reply
      *
      */
     FTPReply validateReply(String reply, String[] expectedReplyCodes)
         throws IOException, FTPException {

         // all reply codes are 3 chars long
         String replyCode = reply.substring(0, 3);
         String replyText = reply.substring(4);

         FTPReply replyObj = new FTPReply(replyCode, replyText);

         for (int i = 0; i < expectedReplyCodes.length; i++)
             if (replyCode.equals(expectedReplyCodes[i]))
                 return replyObj;

         // got this far, not recognised
         throw new FTPException(replyText, replyCode);
     }


     /**
      *  Switch debug of responses on or off
      *
      *  @param  on  true if you wish to have responses to
      *              stdout, false otherwise
      */
     void debugResponses(boolean on) {
         debugResponses = on;
     }

     /**
      *  Set the logging stream, replacing
      *  stdout. If null log supplied, logging is
      *  switched off
      *
      *  @param log  the new logging stream
      */
     void setLogStream(PrintWriter log) {
         if (log != null)
             this.log = log;
         else
       debugResponses = false;
     }
    
    
     /**
      *  Log a message, if logging is set up
      *
      *  @param msg  message to log
      */
     void log(String msg) {
        if (debugResponses && log != null) {
           if (!msg.startsWith(PASSWORD_MESSAGE))
               log.println(msg);
            else
                log.println(PASSWORD_MESSAGE + " ********");
        }
     }
         
}


TOP

Related Classes of com.enterprisedt.net.ftp.FTPControlSocket

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.