For our internal use I found and updated a *very* simple but seemingly complete
one-class FTP java implementation � la UrlConnection, see attachment.

If seen proper, this probably could be donated to Jakarta.

If anyone sees some bugs/quirks/shortcomings with this implementation,
I would be glad about feedback.

--
:) Christoph Reck


Thomas H�sler wrote:
Dear all

I am looking for an easy to use Net Library. I am mainly interested in an
implementation of the FTP Protocol. When looking through the jakarta sources
I found the net components in the sandbox projects. But I don't find a build
of them.
Is there no build available or am I looking at the wrong place??? Could
anyone help me to find a build of those components?

Regards
Tom

------------------------------------------------------------------
Thomas H�sler * TietoEnator Consulting AG
Industriestrasse 19 * 8304 Wallisellen * Switzerland
Tel. +41 (43) 233 45 28 * Fax +41 (43) 233 45 10
Mobile +41 (78) 622 03 36
<mailto:[EMAIL PROTECTED]>
http://www.tietoenator.ch



--
:) Christoph Reck
//--------------------------------------------------------------------
/*
 *  $RCSfile: FtpConnection.java,v $
 *
 *  $Revision: 1.2 $
 *  $Date: 2002/03/05 10:09:29 $
 *
 *  Copyright DLR 2002
 */
//--------------------------------------------------------------------

package de.dlr.dfd.dims.gen.utils;

import java.net.*;
import java.io.*;
import java.util.*;

/**
 * Low-Level worker class that handles FTP protocol (RFC 0959).<p>
 *
 * Code from <a href="http://forum.java.sun.com/thread.jsp?forum=31&thread=179383";
 * >http://forum.java.sun.com/thread.jsp?forum=31&thread=179383<a>.
 *
 * @created   February 5, 2002
 * @author    reck
 */
public class FtpConnection
{
  /** (-1) Default MessageNo **/
  public final static int COMMAND_IGNORED = -1;
  /** (-99) Used when return message cannot be parsed **/
  public final static int UNKNOWN_ERROR = -99;
  /** 
   ** (110) Restart marker reply. In this case, the text is exact and not left to
   ** the particular implementation; it must read: MARK yyyy = mmmm Where yyyy is
   ** User-process data stream marker, and mmmm server's equivalent marker (note
   ** the spaces between markers and "=").
   */
  public final static int RESTART_MARKER_REPLY = 110;
  /** (120) Service ready in nnn minutes. **/
  public final static int SERVICE_NOT_READY = 120;
  /** (125) Data connection already open; transfer starting. **/
  public final static int TRANSFER_STARTING = 125;
  /** (150) File status okay; about to open data connection. **/
  public final static int FILE_STATUS_OK = 150;
  /** (200) Command okay. **/
  public final static int COMMAND_OK = 200;
  /** (202) Command not implemented, superfluous at this site. **/
  public final static int COMMAND_SUPERFLUOUS = 202;
  /** (211) System status, or system help reply. **/
  public final static int SYSTEM_STATUS = 211;
  /** (212) Directory status. **/
  public final static int DIRECTORY_STATUS = 212;
  /** (213) File status. **/
  public final static int FILE_STATUS = 213;
  /** 
   ** (214) Help message. On how to use the server or the meaning of a particular
   ** non-standard command. This reply is useful only to the human user. 
   **/
  public final static int HELP_MESSAGE = 214;
  /** 
   ** (215) NAME system type. Where NAME is an official system name from the list
   ** in the Assigned Numbers document. 
   **/
  public final static int SYSTEM_TYPE = 215;
  /** (220) Service ready for new user. **/
  public final static int SERVICE_READY = 220;
  /** (221) Service closing control connection. Logged out if appropriate. **/
  public final static int SERVICE_CLOSING = 221;
  /** (225) Data connection open; no transfer in progress. **/
  public final static int DATA_CONNECTION_OPEN = 225;
  /** 
   ** (226) Closing data connection. Requested file action successful (for
   ** example, file transfer or file abort). 
   **/
  public final static int DATA_CONNECTION_CLOSED = 226;
  /** (227) Entering Passive Mode (h1,h2,h3,h4,p1,p2). **/
  public final static int PASSIVE_MODE = 227;
  /** (230) User logged in, proceed. **/
  public final static int USER_LOGGED_IN = 230;
  /** (250) Requested file action okay, completed. **/
  public final static int FILE_ACTION_OK = 250;
  /** (257) "PATHNAME" created. **/
  public final static int PATH_OK = 257;
  /** (331) User name okay, need password. **/
  public final static int USER_OK_SEND_PASSWORD = 331;
  /** (332) Need account for login. **/
  public final static int NEED_ACCOUNT_FOR_LOGIN = 332;
  /** (350) Requested file action pending further information. **/
  public final static int FILE_ACTION_PENDING = 350;
  /** 
   ** (421) Service not available, closing control connection. This may be a
   ** reply to any command if the service knows it must shut down. 
   **/
  public final static int SERVICE_NOT_AVAILABLE = 421;
  /** (425) Can't open data connection. **/
  public final static int DATA_CONNECTION_NOT_OPEN = 425;
  /** (426) Connection closed; transfer aborted. **/
  public final static int TRANSFER_ABORTED = 426;
  /** (450) Requested file action not taken. File unavailable (e.g., file busy). **/
  public final static int FILE_NOT_AVAILABLE = 450;
  /** (451) Requested action aborted: local error in processing. **/
  public final static int FILE_ABORT_LOCAL_ERROR = 451;
  /** (452) Requested action not taken. Insufficient storage space in system. **/
  public final static int FILE_ABORT_NO_SPACE = 452;
  /** 
   ** (500) Syntax error, command unrecognized. This may include errors such as
   ** command line too long. 
   **/
  public final static int SYNTAX_ERROR = 500;
  /** (501) Syntax error in parameters or arguments. **/
  public final static int SYNTAX_ERROR_PARAM = 501;
  /** (502) Command not implemented. **/
  public final static int COMMAND_NOT_IMPLEMENTED = 502;
  /** (503) Bad sequence of commands. **/
  public final static int BAD_COMMAND_SEQUENCE = 503;
  /** (504) Command not implemented for that parameter. **/
  public final static int PARAM_NOT_IMPLEMENTED = 504;
  /** (530) Not logged in. **/
  public final static int NOT_LOGGED_IN = 530;
  /** (532) Need account for storing files. **/
  public final static int NEED_ACCOUNT_FOR_STORAGE = 532;
  /** 
   ** (550) Requested action not taken. File unavailable (e.g., file not found,
   ** no access). 
   **/
  public final static int FILE_NOT_FOUND = 550;
  /** (551) Requested action aborted: page type unknown. **/
  public final static int PAGE_TYPE_UNKNOWN = 551;
  /** 
   ** (552) Requested file action aborted. Exceeded storage allocation (for
   ** current directory or dataset). 
   **/
  public final static int FILE_ABORT_SPACE_ALLOC = 552;
  /** (553) Requested action not taken. File name not allowed. **/
  public final static int INVALID_FILE_NAME = 553;
  /** (A) ASCII File Transfer Type **/
  public final static String ASCII_TRANSFER_TYPE = "A";
  /** (I) Binary File Transfer Type **/
  public final static String BINARY_TRANSFER_TYPE = "I";
  /** (21) Default Server Port **/
  public final static int FTP_PORT = 21;
  /** Command channel socket. **/
  protected Socket socket = null;
  /** Command channel input reader. **/
  protected BufferedReader commIn = null;
  /** Command channel output writer. **/
  protected PrintWriter commOut = null;
  /** Data channel socket. **/
  protected Socket dataSocket = null;
  /** Host FTP server name or IP address. **/
  protected String hostAddress = null;
  /** Host FTP server port. Defaults to {@link #FTP_PORT}. **/
  protected int hostPort = FTP_PORT;
  /** Host server port for passive data transfer. Defaults to (-1). **/
  protected int dataPort = -1;
  protected URL hostURL = null;
  protected File localDirectory = null;
  protected String remoteDirectory = null;
  protected boolean connected = false;
  protected String errorMessage = null;
  protected String lastMessage = null;
  /** Last message number. Parsed from lastMessage. Defaults to {@link
   ** #COMMAND_IGNORED}. 
   **/
  protected int lastMessageNo = COMMAND_IGNORED;
  /** Current transfer type. Defaults to {@link #ASCII_TRANSFER_TYPE}. **/
  protected String currentTransferType = ASCII_TRANSFER_TYPE;
  /** Transfer block size in bytes. Defaults to (1024). **/
  protected int transferBlockSize = 1024;
  /** Determines if ftp command channel traffic should be sent to System.out. **/
  protected boolean debug = false;

  /**
   * Empty constructor.
   */
  public FtpConnection()
  {
  }

  /**
   * Class constructor specifying the host by URL.
   *
   * @param hostURL  Passed to {@link #setHostURL(URL) setHostURL()}.
   */
  public FtpConnection( URL hostURL )
  {
    setHostURL( hostURL );
  }

  /**
   * Class constructor specifying the host connection via parameters.
   *
   * @param hostAddress  Passed to {@link #setHostAddress(String) setHostAddress()}.
   * @param hostPort     Passed to {@link #setHostPort(int) setHostPort()}.
   */
  public FtpConnection( String hostAddress,
                        int    hostPort )
  {
    setHostAddress( hostAddress );
    setHostPort( hostPort );
  }

  /**
   * @param debug  {@link #debug}
   */
  public void setDebug( boolean debug )
  {
    this.debug = debug;
  }

  /**
   * Sets {@link #hostAddress}. Calls {@link #disconnect()}.
   *
   * @param hostAddress  {@link #hostAddress}
   */
  public void setHostAddress( String hostAddress )
  {
    disconnect();
    this.hostAddress = hostAddress;
  }

  /**
   * Sets {@link #hostPort}. Port values less than one are ignored. Calls {@link
   * #disconnect()}.
   *
   * @param hostPort  {@link #hostPort}
   */
  public void setHostPort( int hostPort )
  {
    disconnect();
    if( hostPort > 0 )
    {
      this.hostPort = hostPort;
    }
  }

  /**
   * Sets host params via URL. Only accepts URL with protocol of ftp. Sets
   * {@link #hostAddress} via {@link #setHostAddress(String) setHostAddress()}.
   * Sets {@link #hostPort} via {@link #setHostPort(int) setHostPort()}. If URL
   * is not accepted then {@link #hostAddress} is set to null and {@link
   * #hostPort} is set to {@link #FTP_PORT}<br>
   * <br>
   * Note: User information within the URL is ignored due to the fact that the
   * Java 1.1 version of the URL class does not support it.
   *
   * @param hostURL
   */
  public void setHostURL( URL hostURL )
  {
    disconnect();
    if( (hostURL != null) && hostURL.getProtocol().equals( "ftp" ) )
    {
      setHostAddress( hostURL.getHost() );
      setHostPort( hostURL.getPort() );
    }
    else
    {
      hostAddress = null;
      hostPort = FTP_PORT;
    }
    this.hostURL = hostURL;
  }

  /**
   * @param localDirectory  {@link #localDirectory}
   */
  public void setLocalDirectory( File localDirectory )
  {
    this.localDirectory = localDirectory;
  }

  /**
   * Updates {@link #remoteDirectory} and calls {@link #setDirectory(String)
   * setDirectory()} if connected. Does nothing if passed remoteDirectory is
   * same as the current {@link #remoteDirectory}.
   *
   * @param remoteDirectory  {@link #remoteDirectory}
   * @return                 True if successfully updated.
   */
  public boolean setRemoteDirectory( String remoteDirectory )
  {
    errorMessage = null;
    if( !connected )
    {
      this.remoteDirectory = remoteDirectory;
      return true;
    }
    else if( (remoteDirectory != null) && 
             !remoteDirectory.equals(this.remoteDirectory) )
    {
      return setDirectory( remoteDirectory );
    }
    else if( (remoteDirectory != null) && 
              remoteDirectory.equals(this.remoteDirectory) )
    {
      return true;
    }
    return false;
  }

  /**
   * Sets {@link #currentTransferType} and sends TYPE command if connected. Sets
   * {@link #errorMessage} for errors or if transferType is not {@link
   * #ASCII_TRANSFER_TYPE} or {@link #BINARY_TRANSFER_TYPE}.
   *
   * @param transferType  {@link #currentTransferType}
   * @return              True if successfully updated.
   */
  public boolean setTransferType( String transferType )
  {
    try
    {
      if( transferType.equals( ASCII_TRANSFER_TYPE ) &&
          transferType.equals( BINARY_TRANSFER_TYPE ) )
      {
        errorMessage = "Transfer Type Not Supported";
      }
      else if( !connected ||
               (sendCommand( "TYPE " + transferType ) == COMMAND_OK) )
      {
        currentTransferType = transferType;
        return true;
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    return false;
  }

  /**
   * Sets {@link #transferBlockSize}. Value ignored if less than one (1).
   *
   * @param transferBlockSize  {@link #transferBlockSize}
   * @return                   True if successfully updated.
   */
  public boolean setTransferBlockSize( int transferBlockSize )
  {
    if( transferBlockSize > 0 )
    {
      this.transferBlockSize = transferBlockSize;
      return true;
    }
    return false;
  }

  /**
   * @return   {@link #hostAddress}
   */
  public String getHostAddress()
  {
    return hostAddress;
  }

  /**
   * @return   {@link #hostPort}
   */
  public int getHostPort()
  {
    return hostPort;
  }

  /**
   * @return   {@link #hostURL}
   */
  public URL getHostURL()
  {
    return hostURL;
  }

  /**
   * @return   {@link #localDirectory}
   */
  public File getLocalDirectory()
  {
    return localDirectory;
  }

  /**
   * @return   {@link #remoteDirectory}
   */
  public String getRemoteDirectory()
  {
    return remoteDirectory;
  }

  /**
   * @return   {@link #errorMessage}
   */
  public String getErrorMessage()
  {
    return errorMessage;
  }

  /**
   * @return   {@link #lastMessage}
   */
  public String getLastMessage()
  {
    return lastMessage;
  }

  /**
   * @return   {@link #lastMessageNo}
   */
  public int getLastMessageNo()
  {
    return lastMessageNo;
  }

  /**
   * @return   {@link #transferBlockSize}
   */
  public int getTransferBlockSize()
  {
    return transferBlockSize;
  }

  /**
   * @return   {@link #connected}
   */
  public boolean isConnected()
  {
    return connected;
  }

  /**
   * Gets contents of the {@link #remoteDirectory} using the LIST command.
   *
   * @return   {@link String} array of detailed directory information
   */
  public String[] getList()
  {
    return getList( "LIST", null );
  }

  /**
   * Gets contents of the specified remote directory using the LIST command.
   * Directory name can be absolute of relative path.
   *
   * @param directoryName
   * @return               {@link String} array of detailed directory
   *      information
   */
  public String[] getList( String directoryName )
  {
    return getList( "LIST", directoryName );
  }

  /**
   * Gets contents of the {@link #remoteDirectory} using the NLST command.
   *
   * @return   {@link String} array of file and directory names
   */
  public String[] getNameList()
  {
    return getList( "NLST", null );
  }

  /**
   * Gets contents of the specified remote directory using the NLST command.
   * Directory name can be absolute of relative path.
   *
   * @param directoryName
   * @return               {@link String} array of file and directory names
   */
  public String[] getNameList( String directoryName )
  {
    return getList( "NLST", directoryName );
  }

  /**
   * Connects o host ftp server. Calls {@link #connect(String,String,String)}
   * with a null account.
   *
   * @param username     The username for the login.
   * @param password     The password for the login.
   * @return   True if no errors occurred.
   */
  public boolean connect(String username, String password)
  {
    return connect(username, password, null);
  }

  /**
   * Connects to host ftp server. Opens {@link #socket}, {@link #commIn}, and
   * {@link #commOut}. Sends username, password, account}, and {@link 
   * #currentTransferType}. Sets {@link #connected} to true if no errors 
   * occurred. Calls {@link #setDirectory(String) setDirectory()} if 
   * {@link #remoteDirectory} has been specified prior to connection, 
   * otherwise calls {@link #getCurrentDirectory()}. Sets {@link #errorMessage} 
   * for errors.
   *
   * @param username     The username for the login.
   * @param password     The password for the login.
   * @param account      The account for the login.
   * @return   True if no errors occurred.
   */
  public boolean connect(String username, String password, String account)
  {
    errorMessage = null;
    if( connected )
    {
      return true;
    }
    else if( hostAddress == null )
    {
      errorMessage = "Host Address Not Set";
    }
    else if( hostPort < 1 )
    {
      errorMessage = "Port " + Integer.toString( hostPort ) + " is not valid";
    }
    else if( username == null )
    {
      errorMessage = "Username Not Set";
    }
    else
    {
      try
      {
        socket = new Socket( hostAddress, hostPort );
        commIn = new BufferedReader( new InputStreamReader( socket.getInputStream() ) 
);
        commOut = new PrintWriter( socket.getOutputStream() );
        if( readCommandChannel() != SERVICE_READY )
        {
          errorMessage = lastMessage;
        }
        else if( (sendCommand("USER " + username) != USER_OK_SEND_PASSWORD) &&
                 (lastMessageNo != USER_LOGGED_IN) &&
                 (lastMessageNo != NEED_ACCOUNT_FOR_LOGIN) )
        {
          errorMessage = lastMessage;
        }
        else if( password == null
           && lastMessageNo == USER_OK_SEND_PASSWORD )
        {
          errorMessage = "Password Required For Account " + username;
        }
        else if( (password != null) && 
                 (lastMessageNo == USER_OK_SEND_PASSWORD) &&
                 (sendCommand("PASS " + password) != USER_LOGGED_IN) &&
                 (lastMessageNo != NEED_ACCOUNT_FOR_LOGIN) &&
                 (lastMessageNo != COMMAND_SUPERFLUOUS) )
        {
          errorMessage = lastMessage;
        }
        else if( (account == null) && (lastMessageNo == NEED_ACCOUNT_FOR_LOGIN) )
        {
          errorMessage = "Account Required For Login";
        }
        else if( (account != null) && 
                 (sendCommand("ACCT " + account) != USER_LOGGED_IN) &&
                 (lastMessageNo != COMMAND_SUPERFLUOUS) )
        {
          errorMessage = lastMessage;
        }
        if( errorMessage == null )
        {
          if( remoteDirectory != null )
          {
            setDirectory( remoteDirectory );
          }
          else
          {
            getCurrentDirectory();
          }
          if( currentTransferType.equals( ASCII_TRANSFER_TYPE ) )
          {
            sendCommand( "TYPE " + currentTransferType );
          }
          connected = true;
          return true;
        }
        disconnect();
      }
      catch( IOException e )
      {
        errorMessage = e.getMessage();
      }
    }
    return false;
  }

  /**
   * Sends re-initialize command (REIN). Calls {@link #getCurrentDirectory()}.
   * Sets {@link #errorMessage} for errors.
   *
   * @return   True if no errors occurred.
   */
  public boolean reInitialize()
  {
    if( connected )
    {
      try
      {
        if( sendCommand("REIN") == SERVICE_READY )
        {
          getCurrentDirectory();
          return true;
        }
        errorMessage = lastMessage;
      }
      catch( IOException e )
      {
        errorMessage = e.getMessage();
      }
    }
    return false;
  }

  /**
   * Sends quit command (QUIT) and disconnects from ftp server. Closes {@link
   * #dataSocket}, {@link #commIn}, {@link #commOut}, and {@link #socket}. Sets
   * {@link #connected} to false.
   */
  public void disconnect()
  {
    if( connected )
    {
      if( dataSocket != null )
      {
        try
        {
          dataSocket.close();
          dataSocket = null;
        }
        catch( IOException e )
        {
        }
      }
      if( commIn != null )
      {
        try
        {
          sendCommand( "QUIT" );
        }
        catch( IOException e )
        {
        }
        if( commIn != null )
        {
          try
          {
            commIn.close();
            commIn = null;
          }
          catch( IOException e )
          {
          }
        }
      }
      if( commOut != null )
      {
        commOut.close();
        commOut = null;
      }
      if( socket != null )
      {
        try
        {
          socket.close();
          socket = null;
        }
        catch( IOException e )
        {
        }
      }
      connected = false;
    }
  }

  /**
   * Sends change directory up (CDUP) to change current directory to parent
   * directory. Calls {@link #getCurrentDirectory()}. Sets {@link #errorMessage}
   * for errors.
   *
   * @return   True if no errors occurred.
   */
  public boolean changeDirectoryUp()
  {
    errorMessage = null;
    if( connected )
    {
      try
      {
        if( (sendCommand( "CDUP" ) == COMMAND_OK) ||
            (lastMessageNo == FILE_ACTION_OK) )
        {
          getCurrentDirectory();
          return true;
        }
        errorMessage = lastMessage;
      }
      catch( IOException e )
      {
        errorMessage = e.getMessage();
      }
    }
    return false;
  }

  /**
   * Adds passed subDirectory to {@link #remoteDirectory} and calls {@link
   * #setRemoteDirectory(String) setRemoteDirectory()}.
   *
   * @param subDirectory  Name of directory in the current directory to change
   *      to.
   * @return              True if no errors occurred.
   */
  public boolean changeDirectoryDown( String subDirectory )
  {
    errorMessage = null;
    if( connected && (subDirectory != null) && (remoteDirectory != null) )
    {
      if( remoteDirectory.endsWith( "/" ) )
      {
        setRemoteDirectory( remoteDirectory + subDirectory );
      }
      else
      {
        setRemoteDirectory( remoteDirectory + "/" + subDirectory );
      }
    }
    return false;
  }

  /**
   * Convenience method for {@link #setRemoteDirectory(String)
   * setRemoteDirectory()}.
   *
   * @param directoryName  {@link #remoteDirectory}
   * @return               Value from setRemoteDirectory()
   */
  public boolean changeDirectory( String directoryName )
  {
    return setRemoteDirectory( directoryName );
  }

  /**
   * Sends file using the current transfer type. Sets {@link #errorMessage} if
   * file is null, file does not exist, directory is passed, or not {@link
   * #connected}.
   *
   * @param file  File to transfer
   * @return      True if no errors occurred.
   */
  public boolean sendFile( File file )
  {
    errorMessage = null;
    if( file == null )
    {
      errorMessage = "File Null";
    }
    else if( !file.exists() )
    {
      errorMessage = "File " + file.toString() + " does not exist";
    }
    else if( !file.isFile() )
    {
      errorMessage = file.toString() + " is not a file";
    }
    else if( !connected )
    {
      errorMessage = "Not Connected";
    }
    else if( openDataSocket() )
    {
      FileInputStream fileInput = null;
      OutputStream outputStream = null;
      byte[] data = new byte[transferBlockSize];
      try
      {
        //Have to go by file.length() because some file systems let you read beyond the
        // end of the file to the size the file takes up on the file system (Win2K).
        long bytesRemaining = file.length();
        fileInput = new FileInputStream( file );
        outputStream = dataSocket.getOutputStream();
        if( (sendCommand( "STOR " + file.getName() ) != TRANSFER_STARTING) &&
            (lastMessageNo != FILE_STATUS_OK) )
        {
          errorMessage = lastMessage;
        }
        else
        {
          while( bytesRemaining > 0L )
          {
            if( bytesRemaining < ( new Integer( transferBlockSize ) ).longValue() )
            {
              data = new byte[( new Long( bytesRemaining ) ).intValue()];
            }
            bytesRemaining -= ( new Integer( fileInput.read(data) ) ).longValue();
            outputStream.write( data );
            //SEND TO LISTNER HERE
          }
        }
        outputStream.flush();
        outputStream.close();
        fileInput.close();
        if( (errorMessage == null) &&
            (readCommandChannel() != DATA_CONNECTION_CLOSED) &&
            (lastMessageNo != FILE_STATUS_OK) )
        {
          errorMessage = lastMessage;
        }
        closeDataSocket();
        if( errorMessage == null )
        {
          return true;
        }
      }
      catch( IOException e )
      {
        errorMessage = e.getMessage();
      }
      if( outputStream != null )
      {
        try
        {
          outputStream.close();
        }
        catch( IOException e )
        {
        }
      }
      if( fileInput != null )
      {
        try
        {
          fileInput.close();
        }
        catch( IOException e )
        {
        }
      }
    }
    closeDataSocket();
    return false;
  }

  /**
   * Calls {@link #setTransferType(String) setTransferType()} with {@link
   * #ASCII_TRANSFER_TYPE} and calls {@link #sendFile(File) sendFile()} with
   * passed file.
   *
   * @param file  Passed to {@link #sendFile(File) sendFile()}.
   * @return      True if no errors occurred.
   */
  public boolean sendAsciiFile( File file )
  {
    if( !setTransferType( ASCII_TRANSFER_TYPE ) )
    {
      return false;
    }
    return sendFile( file );
  }

  /**
   * Calls {@link #setTransferType(String) setTransferType()} with {@link
   * #BINARY_TRANSFER_TYPE} and calls {@link #sendFile(File) sendFile()} with
   * passed file.
   *
   * @param file  Passed to {@link #sendFile(File) sendFile()}.
   * @return      True if no errors occurred.
   */
  public boolean sendBinaryFile( File file )
  {
    if( !setTransferType( BINARY_TRANSFER_TYPE ) )
    {
      return false;
    }
    return sendFile( file );
  }

  /**
   * Retrieves file from the {@link #remoteDirectory} to the {@link
   * #localDirectory} using the {@link #currentTransferType}. Sets {@link
   * #errorMessage} if file is null, {@link #localDirectory} is not set, {@link
   * #localDirectory} does not exist, {@link #localDirectory} is not a
   * directory, or not currently {@link #connected}. Sets {@link #errorMessage}
   * if destination file already exists and overWrite not specified.
   *
   * @param fileName   Name of file in {@link #remoteDirectory}.
   * @param overWrite  Set true if existing file in {@link #localDirectory}
   *      should be overwritten.
   * @return           True if no errors occurred.
   */
  public boolean receiveFile( String fileName,
                              boolean overWrite )
  {
    errorMessage = null;
    if( fileName == null )
    {
      errorMessage = "File Null";
    }
    else if( (localDirectory == null) || !localDirectory.exists() ||
             !localDirectory.isDirectory() )
    {
      errorMessage = "Invalid Local Directory";
    }
    else if( !connected )
    {
      errorMessage = "Not Connected";
    }
    else if( openDataSocket() )
    {
      File file = new File( localDirectory, fileName );
      if( file.exists() && !overWrite )
      {
        errorMessage = "Local File Already Exists";
      }
      else
      {
        FileOutputStream fileOutput = null;
        InputStream inputStream = null;
        byte[] data = new byte[transferBlockSize];
        try
        {
          fileOutput = new FileOutputStream( file );
          inputStream = dataSocket.getInputStream();
          if( sendCommand( "RETR " + file.getName() ) != TRANSFER_STARTING )
          {
            errorMessage = lastMessage;
          }
          else
          {
            while( inputStream.read( data ) > 0 )
            {
              fileOutput.write( data );
              //SEND TO LISTNER HERE
            }
          }
          fileOutput.flush();
          fileOutput.close();
          inputStream.close();
          if( (errorMessage == null) &&
              (readCommandChannel() != DATA_CONNECTION_CLOSED) &&
              (lastMessageNo != FILE_STATUS_OK) )
          {
            errorMessage = lastMessage;
          }
          closeDataSocket();
          if( errorMessage == null )
          {
            return true;
          }
        }
        catch( IOException e )
        {
          errorMessage = e.getMessage();
        }
        if( inputStream != null )
        {
          try
          {
            inputStream.close();
          }
          catch( IOException e )
          {
          }
        }
        if( fileOutput != null )
        {
          try
          {
            fileOutput.close();
          }
          catch( IOException e )
          {
          }
        }
      }
    }
    closeDataSocket();
    return false;
  } // end receiveFile()

  /**
   * Calls {@link #setTransferType(String) setTransferType()} with {@link
   * #ASCII_TRANSFER_TYPE} and calls {@link #receiveFile(String,boolean)
   * receiveFile()}.
   *
   * @param fileName   Passed to {@link #receiveFile(String,boolean)
   *      receiveFile()}.
   * @param overWrite  Passed to {@link #receiveFile(String,boolean)
   *      receiveFile()}.
   * @return           True if no errors occurred.
   */
  public boolean receiveAsciiFile( String fileName,
                                   boolean overWrite )
  {
    if( !currentTransferType.equals( ASCII_TRANSFER_TYPE ) &&
        !setTransferType( ASCII_TRANSFER_TYPE ) )
    {
      return false;
    }
    return receiveFile( fileName, overWrite );
  }

  /**
   * Calls {@link #setTransferType(String) setTransferType()} with {@link
   * #BINARY_TRANSFER_TYPE} and calls {@link #receiveFile(String,boolean)
   * receiveFile()}.
   *
   * @param fileName   Passed to {@link #receiveFile(String,boolean)
   *      receiveFile()}.
   * @param overWrite  Passed to {@link #receiveFile(String,boolean)
   *      receiveFile()}.
   * @return           True if no errors occurred.
   */
  public boolean receiveBinaryFile( String fileName,
                                    boolean overWrite )
  {
    if( !currentTransferType.equals( BINARY_TRANSFER_TYPE ) &&
        !setTransferType( BINARY_TRANSFER_TYPE ) )
    {
      return false;
    }
    return receiveFile( fileName, overWrite );
  }

  /**
   * Sends file rename commands (RNFR + RNTO) for the {@link #remoteDirectory}.
   * Sets {@link #errorMessage} for errors.
   *
   * @param oldFileName
   * @param newFileName
   * @return             True if no errors occurred.
   */
  public boolean renameFile( String oldFileName,
                             String newFileName )
  {
    try
    {
      if( !connected )
      {
        errorMessage = "Not Connected";
      }
      else if( (oldFileName == null) || (newFileName == null) )
      {
        errorMessage = "File Name Null";
      }
      else if( sendCommand( "RNFR " + oldFileName ) != FILE_ACTION_PENDING )
      {
        errorMessage = lastMessage;
      }
      else if( sendCommand( "RNTO " + newFileName ) != FILE_ACTION_OK )
      {
        errorMessage = lastMessage;
      }
      else
      {
        return true;
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    return false;
  }

  /**
   * Sends delete file command (DELE). Sets {@link #errorMessage} for errors.
   *
   * @param fileName  File to delete in {@link #remoteDirectory}.
   * @return          True if no errors occurred.
   */
  public boolean deleteFile( String fileName )
  {
    try
    {
      if( !connected )
      {
        errorMessage = "Not Connected";
      }
      else if( fileName == null )
      {
        errorMessage = "File Name Null";
      }
      else if( sendCommand( "DELE " + fileName ) != FILE_ACTION_OK )
      {
        errorMessage = lastMessage;
      }
      else
      {
        return true;
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    return false;
  }

  /**
   * Sends remove directory command (RMD). Directory name can be absolute of
   * relative path. Sets {@link #errorMessage} if not connected or directory
   * name is null.
   *
   * @param directoryName  Directory to remove.
   * @return               True if no errors occurred.
   */
  public boolean removeDirectory( String directoryName )
  {
    try
    {
      if( !connected )
      {
        errorMessage = "Not Connected";
      }
      else if( directoryName == null )
      {
        errorMessage = "Directory Name Null";
      }
      else if( sendCommand( "RMD " + directoryName ) != FILE_ACTION_OK )
      {
        errorMessage = lastMessage;
      }
      else
      {
        return true;
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    return false;
  }

  /**
   * Sends make directory command (MKD). Directory name can be absolute of
   * relative path. Sets {@link #errorMessage} if not connected or directory
   * name is null.
   *
   * @param directoryName  Directory to create.
   * @return               True if no errors occurred.
   */
  public boolean makeDirectory( String directoryName )
  {
    try
    {
      if( !connected )
      {
        errorMessage = "Not Connected";
      }
      else if( directoryName == null )
      {
        errorMessage = "Directory Name Null";
      }
      else if( sendCommand( "MKD " + directoryName ) != PATH_OK )
      {
        errorMessage = lastMessage;
      }
      else
      {
        return true;
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    return false;
  }

  /**
   * Sends change directory command (CWD). Directory name can be absolute of
   * relative path.
   *
   * @param directory  Remote directory to change to.
   * @return           True if no errors occurred.
   */
  protected boolean setDirectory( String directory )
  {
    try
    {
      if( sendCommand( "CWD " + directory ) != FILE_ACTION_OK )
      {
        errorMessage = lastMessage;
        return false;
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
      return false;
    }
    remoteDirectory = directory;
    return true;
  }

  /**
   * Sends passive mode command (PASV) and sets {@link #dataPort}.
   *
   * @return
   */
  protected boolean setPassiveMode()
  {
    try
    {
      if( sendCommand( "PASV" ) == PASSIVE_MODE )
      {
        StringTokenizer st = new StringTokenizer( lastMessage, "," );
        st.nextToken();
        st.nextToken();
        st.nextToken();
        st.nextToken();
        dataPort = ( Integer.parseInt( st.nextToken() ) * 256 )
           + Integer.parseInt( st.nextToken( ")" ).substring( 1 ) );
        return true;
      }
      errorMessage = lastMessage;
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    return false;
  }

  /**
   * Gets contents of the specified remote directory using the specified
   * command. Directory name can be absolute of relative path. {@link
   * #remoteDirectory} is used if specified directory is null. Sets {@link
   * #errorMessage} if not connected.
   *
   * @param command        Ftp command to retrieve directory listing (LIST or
   *      NLST).
   * @param directoryName  Remote directory to list (can be null).
   * @return               {@link String} array of directory contents or null if
   *      error occurred.
   */
  protected String[] getList( String command,
                              String directoryName )
  {
    if( !connected )
    {
      errorMessage = "Not Connected";
      return null;
    }
    else if( !openDataSocket() )
    {
      return null;
    }
    else if( !setTransferType( ASCII_TRANSFER_TYPE ) )
    {
      return null;
    }
    Vector names = new Vector();
    InputStreamReader inputStreamReader = null;
    BufferedReader bufferedReader = null;
    String fullCommand = command;
    if( directoryName != null )
    {
      fullCommand = fullCommand + " " + directoryName;
    }
    try
    {
      inputStreamReader = new InputStreamReader( dataSocket.getInputStream() );
      bufferedReader = new BufferedReader( inputStreamReader );
      if( sendCommand( fullCommand ) == TRANSFER_STARTING )
      {
        String line = bufferedReader.readLine();
        while( line != null )
        {
          names.addElement( line );
          line = bufferedReader.readLine();
        }
        bufferedReader.close();
        inputStreamReader.close();
        String[] nameList = new String[names.size()];
        for( int x = 0; x < names.size(); x++ )
        {
          nameList[x] = ( String ) names.elementAt( x );
        }
        closeDataSocket();
        return nameList;
      }
      errorMessage = lastMessage;
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
    if( bufferedReader != null )
    {
      try
      {
        bufferedReader.close();
      }
      catch( IOException e )
      {
      }
    }
    if( inputStreamReader != null )
    {
      try
      {
        inputStreamReader.close();
      }
      catch( IOException e )
      {
      }
    }
    closeDataSocket();
    return null;
  }

  /**
   * Retrieves current directory (PWD) and sets {@link #remoteDirectory}.
   */
  protected void getCurrentDirectory()
  {
    try
    {
      if( sendCommand( "PWD" ) != PATH_OK )
      {
        errorMessage = lastMessage;
      }
      else
      {
        StringTokenizer st = new StringTokenizer( lastMessage, "\"" );
        st.nextToken();
        remoteDirectory = st.nextToken();
      }
    }
    catch( IOException e )
    {
      errorMessage = e.getMessage();
    }
  }

  /**
   * Reads from {@link #commIn} and sets {@link #lastMessage} and {@link
   * #lastMessageNo}. Sends {@link #lastMessage} to System.out if {@link #debug}
   * is true. Calls {@link #disconnect()} if return from server is {@link
   * #SERVICE_NOT_AVAILABLE}.
   *
   * @return                 {@link #lastMessageNo} if successful; {@link
   *      #UNKNOWN_ERROR} if parse error; {@link #COMMAND_IGNORED} if not
   *      connected.
   * @exception IOException
   */
  protected int readCommandChannel()
     throws IOException
  {
    if( commIn != null )
    {
      lastMessage = commIn.readLine();
      try
      {
        lastMessageNo = Integer.parseInt( lastMessage.substring( 0, 3 ) );
      }
      catch( NumberFormatException e )
      {
        lastMessageNo = UNKNOWN_ERROR;
      }
      //PUT LISTENER CALL HERE
      if( debug )
      {
        System.out.println( lastMessage );
      }
      if( lastMessageNo == SERVICE_NOT_AVAILABLE )
      {
        disconnect();
      }
      return lastMessageNo;
    }
    return COMMAND_IGNORED;
  }

  /**
   * Sends command to {@link #commOut} and calls {@link #readCommandChannel()}.
   * Sends command to System.out if {@link #debug} is true.
   *
   * @param command          Command string to send to server.
   * @return                 Value from {@link #readCommandChannel()} or {@link
   *      #COMMAND_IGNORED} if not connected.
   * @exception IOException
   */
  protected int sendCommand( String command )
     throws IOException
  {
    if( commOut != null )
    {
      if( debug )
      {
        System.out.println( command );
      }
      commOut.println( command );
      commOut.flush();
      //PUT LISTNER CALL HERE
      return readCommandChannel();
    }
    return COMMAND_IGNORED;
  }

  /**
   * Opens the {@link #dataSocket} using {@link #hostAddress} and {@link
   * #dataPort}. Calls {@link #closeDataSocket()}.
   *
   * @return   True if socket successfully created.
   */
  protected boolean openDataSocket()
  {
    closeDataSocket();
    if( connected && setPassiveMode() )
    {
      try
      {
        dataSocket = new Socket( hostAddress, dataPort );
        return true;
      }
      catch( IOException e )
      {
        errorMessage = e.getMessage();
      }
    }
    return false;
  }

  /**
   * Closes {@link #dataSocket} if not null.
   */
  protected void closeDataSocket()
  {
    if( dataSocket != null )
    {
      try
      {
        dataSocket.close();
        dataSocket = null;
      }
      catch( IOException e )
      {
      }
    }
  }

} // end FtpConnection
--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to