//==============================================================================
// FTPClientAdapter.java
//==============================================================================

package tribble.net.ftp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Writer;

import java.lang.Exception;
import java.lang.NullPointerException;
import java.lang.String;
import java.lang.System;
import java.lang.Throwable;

import java.util.ArrayList;


/*******************************************************************************
* Simple FTP client base class.
* Allows clients to establish FTP connections, send and receive files, get
* remote directory listings, etc.
*
* <p/>
* This base class provides a framework upon which to build working FTP client
* implementations.
*
* <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
* <p/>
* <b>References</b>
*
* <p/>
* IETF RFC 959 - File Transfer Protocol (FTP) <br/>
* <a href="http://www.ietf.org/rfc/rfc0959.txt"
*  >www.ietf.org/rfc/rfc0959.txt</a>.
*
* <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
* <dl>
* <dt> <b>Source code:</b> </dt>
*  <dd> Available at:
*   <a href="http://david.tribble.com/src/java/tribble/net/ftp/FTPClientAdapter.java"
*    >http://david.tribble.com/src/java/tribble/net/ftp/FTPClientAdapter.java</a>
*  </dd>
* <dt> <b>Documentation:</b> </dt>
*  <dd> Available at:
*   <a href="http://david.tribble.com/docs/tribble/net/ftp/FTPClientAdapter.html"
*    >http://david.tribble.com/docs/tribble/net/ftp/FTPClientAdapter.html</a>
*  </dd>
* </dl>
*
* <!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
* @version	API 2.0 $Revision: 1.13 $ $Date: 2010/07/12 21:26:02 $
* @since	API 1.0, 2006-04-14
* @author	David R. Tribble (david&#64;tribble.com).
*	<p>
*	Copyright ©2006-2010 by David R. Tribble, all rights reserved.<br/>
*	Permission is granted to any person or entity except those designated
*	by the United States Department of State as a terrorist, or terrorist
*	government or agency, to use and distribute this source code provided
*	that the original copyright notice remains present and unaltered.
*
* @see	FTPClient
*/

public abstract class FTPClientAdapter
    implements FTPClientI
{
    /** Revision information. */
    static final String		REV =
        "@(#)tribble/net/ftp/FTPClientAdapter.java API 2.0 $Revision: 1.13 $ $Date: 2010/07/12 21:26:02 $\n";


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants

    /** System-specific directory path separator char. */
    private static final char	DIRSEP =
        System.getProperty("file.separator").charAt(0);


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Static methods

    /***************************************************************************
    * Determine if a filename is absolute or not.
    *
    * <p>
    * Note that this handles Win32 absolute pathnames without leading drive
    * letters properly (e.g., <tt>"c:\foo.txt"</tt> and <tt>"\foo.txt"</tt> are
    * both absolute pathnames). This corrects an oversight in the standard
    * Java library implementation.
    *
    * @param	file
    * A filename to check.
    *
    * @return
    * True if the <tt>file</tt> is an absolute pathname, otherwise false.
    *
    * @since	1.12, 2010-07-11
    */
    public static boolean isAbsoluteFilename(File file)
    {
        String	fn;
        char	ch;

        // Basic test
        if (file.isAbsolute())
            return true;

        // Win32-specific test,
        //  which is admittedly a kludge to correct the Java library
        if (DIRSEP == '\\')
        {
            fn = file.getPath();
            if (fn.length() > 0)
            {
                ch = fn.charAt(0);
                if (ch == '\\'  ||  ch == '/')
                    return true;
            }
        }

        return false;
    }


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Variables

    /** Enable debug tracing. */
    public PrintStream		m_debugOut;

    /** Remote FTP host name. */
    protected String		m_hostName;

    /** Remote FTP port number. */
    protected int		m_cmdPort =	21;
    protected int		m_dataPort =	0;

    /** I/O time-out (seconds). */
    protected int		m_timeOut =	30;

    /** Data transfer buffer length. */
    protected int		m_bufSize =	8*1024;

    /** User-ID. */
    protected String		m_userID;

    /** User password. */
    protected String		m_password;

    /** Current remote working directory. */
    protected String		m_remoteDir;

    /** Current local working directory. */
    protected File		m_localDir =
        new File(System.getProperty("user.dir"));

    /** Transfer mode is passive. */
    protected boolean		m_passiveMode =	true;

    /** Transfer files as text (ASCII) mode, not binary. */
    protected boolean		m_textMode =	true;

    /** This session has connected to the remote FTP system. */
    protected boolean		m_isConnected;

    /** FTP user is logged into the FTP session. */
    protected boolean		m_isLoggedOn;

    /** Stop/interrupt execution flag. */
    protected volatile boolean	m_stop;

    /** Number of input bytes read. */
    protected volatile long	m_inBytes;

    /** Number of output bytes written. */
    protected volatile long	m_outBytes;


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constructors

    /***************************************************************************
    * Default constructor.
    *
    * @since	1.1, 2006-04-14
    */
    public FTPClientAdapter()
    { }


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Methods

    /***************************************************************************
    * Signal the current FTP operation to be interrupted and to terminate
    * prematurely.
    *
    * <p>
    * Once this method has been called, the interrupt signal can be cleared by
    * calling {@link #resetStop resetStop()}.
    *
    * @see	#resetStop resetStop()
    *
    * @since	1.7, 2006-05-06
    */
    public void stop()
    {
        m_stop = true;
    }


    /***************************************************************************
    * Reset the interrupt signal set by a previous call to {@link #stop stop()}.
    *
    * @see	#stop stop()
    *
    * @since	1.7, 2006-05-06
    */
    public void resetStop()
    {
        m_stop = false;
    }


    /***************************************************************************
    * Retrieve the number of bytes read from the input data port so far.
    *
    * <p>
    * This method can be called from a separate thread while an FTP operation is
    * being performed on this object to check the progress of the operation.
    *
    * @return
    * The number of bytes read from the data port so far during the current FTP
    * operation.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    public long getInputByteCount()
    {
        return m_inBytes;
    }


    /***************************************************************************
    * Retrieve the number of bytes written to the output data port so far.
    *
    * <p>
    * This method can be called from a separate thread while an FTP operation is
    * being performed on this object to check the progress of the operation.
    *
    * @return
    * The number of bytes written to the data port so far during the current FTP
    * operation.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    public long getOutputByteCount()
    {
        return m_outBytes;
    }


    /***************************************************************************
    * Retrieve the remote FTP host name.
    *
    * @return
    * FTP system host name or IP address.
    *
    * @see	#setHost setHost()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public String getHost()
    {
        return m_hostName;
    }


    /***************************************************************************
    * Set the remote FTP host (system) name.
    *
    * @param	host
    * FTP system host name or IP address.
    *
    * @see	#getHost getHost()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void setHost(String host)
    {
        if (m_debugOut != null)
            m_debugOut.println("$ host: " + m_hostName + " -> " + host);

        m_hostName = host;
    }


    /***************************************************************************
    * Retrieve the FTP command port number.
    *
    * @return
    * FTP command port number.
    *
    * @see	#setCommandPort setCommandPort()
    * @see	#getDataPort getDataPort()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public int getCommandPort()
    {
        return m_cmdPort;
    }


    /***************************************************************************
    * Set the remote FTP command port number.
    *
    * @param	port
    * FTP command port number.  (The default FTP port is 21.)
    *
    * @see	#getCommandPort getCommandPort()
    * @see	#setDataPort setDataPort()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void setCommandPort(int port)
    {
        // Set the command port
        if (port < 0)
            port = 0;

        if (m_debugOut != null)
            m_debugOut.println("$ cmdPort: " + m_cmdPort + " -> " + port);

        m_cmdPort = port;
    }


    /***************************************************************************
    * Retrieve the FTP data port number.
    *
    * @return
    * FTP data port number.
    *
    * @see	#setDataPort setDataPort()
    * @see	#getCommandPort getCommandPort()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public int getDataPort()
    {
        return m_dataPort;
    }


    /***************************************************************************
    * Set the remote FTP data port number.
    *
    * @param	port
    * FTP data port number.  Setting this to zero or a negative number causes
    * the data ports to be chosen (randomly) by the local system when operating
    * in non-passive mode.  (The default FTP port is 0.)
    *
    * @see	#getDataPort getDataPort()
    * @see	#setCommandPort setCommandPort()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void setDataPort(int port)
    {
        // Set the data port
        if (port < 0)
            port = 0;

        if (m_debugOut != null)
            m_debugOut.println("$ dataPort: " + m_dataPort + " -> " + port);

        m_dataPort = port;
    }


    /***************************************************************************
    * Retrieve the FTP I/O time-out.
    *
    * @return
    * FTP I/O time-out, in seconds.
    *
    * @see	#setTimeOut setTimeOut()
    * @see	#getCommandPort getCommandPort()
    * @see	#getDataPort getDataPort()
    *
    * @since	1.2, 2006-06-02
    */
    //@Override
    public int getTimeOut()
    {
        return m_timeOut;
    }


    /***************************************************************************
    * Set the remote FTP I/O time-out.
    *
    * @param	secs
    * FTP I/O time-out, in seconds.
    * (The default setting is 30.)
    *
    * @see	#getTimeOut getTimeOut()
    * @see	#setCommandPort setCommandPort()
    * @see	#setDataPort setDataPort()
    *
    * @since	1.2, 2006-06-02
    */
    //@Override
    public void setTimeOut(int secs)
    {
        // Set the socket I/O time-out
        if (secs < 0)
            secs = 0;

        if (m_debugOut != null)
            m_debugOut.println("$ timeOut: " + m_timeOut + " -> " + secs);

        m_timeOut = secs;
    }


    /***************************************************************************
    * Set the size of the FTP data transfer buffer.
    * By default, the internal buffer length is 8K (8,192) bytes.
    *
    * @param	len
    * New buffer size.
    * Values between 1K (1,024) and 16K (16,384) are probably optimal for most
    * applications.
    * Classes that extend this class may choose to ignore this setting.
    *
    * @return
    * The previous buffer size setting.
    *
    * @throws	IllegalArgumentException (unchecked)
    * Thrown if <tt>len</tt> is less than 1.
    *
    * @see	#appendFile(File, String) appendFile()
    * @see	#getFile(String, File) getFile()
    * @see	#putFile(File, String) putFile()
    *
    * @since	1.11, 2008-09-18
    */
    //@Override
    public int setBufferSize(int len)
    {
        int	prev;

        // Sanity check
        if (len < 1)
            throw new IllegalArgumentException("Buffer length too small ("
                + len + ")");

        // Set the new data buffer size
        prev = m_bufSize;
        m_bufSize = len;
        return prev;
    }


    /***************************************************************************
    * Retrieve the user-ID.
    *
    * @see	#setUserID setUserID()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public String getUserID()
    {
        return m_userID;
    }


    /***************************************************************************
    * Set the user name (user-ID).
    *
    * @param	id
    * User-ID.
    *
    * @see	#getUserID getUserID()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void setUserID(String id)
    {
        if (m_debugOut != null)
            m_debugOut.println("$ userID: " + m_userID + " -> " + id);

        m_userID = id;
    }


    /***************************************************************************
    * Retrieve the user password.
    *
    * @return
    * User password.
    *
    * @see	#setPassword setPassword()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public String getPassword()
    {
        return m_password;
    }


    /***************************************************************************
    * Set the user password.
    *
    * @param	pwd
    * User password.
    *
    * @see	#getPassword getPassword()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void setPassword(String pwd)
    {
        if (m_debugOut != null)
            m_debugOut.println("$ setPassword");

        m_password = pwd;
    }


    /***************************************************************************
    * Set this FTP client to operate (or not) in passive data transfer mode.
    * Passive mode allows the remote FTP system to assign the data ports, while
    * non-passive mode allows the client to assign them.
    * (By default, the client operates in passive mode.)
    *
    * @param	flag
    * If true, the data transfer mode is set to passive, otherwise it is set to
    * non-passive (active listen) mode.
    *
    * @return
    * The previous mode setting.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public boolean setPassive(boolean flag)
    {
        boolean		prev;

        // Set the transfer mode
        if (m_debugOut != null)
            m_debugOut.println("$ passive: " + m_passiveMode + " -> " + flag);

        prev = m_passiveMode;
        m_passiveMode = flag;
        return prev;
    }


    /***************************************************************************
    * Connect to the remote FTP system.
    *
    * @throws	IOException
    * Thrown if unable to connect to the remote FTP system.
    *
    * @see	#disconnect disconnect()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void connect()
        throws IOException;


    /***************************************************************************
    * Disconnect from the remote FTP system.
    * Note that this method does not throw {@link IOException}.
    *
    * @see	#connect connect()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void disconnect();


    /***************************************************************************
    * Determine if this client is connected to the remote FTP system.
    *
    * @return
    * True if this client has connected to a remote FTP system, otherwise false.
    *
    * @see	#connect connect()
    * @see	#disconnect disconnect()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public boolean isConnected()
    {
        return m_isConnected;
    }


    /***************************************************************************
    * Log on to the remote FTP system.
    * This uses the last setting of the user-ID and password.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#connect connect()
    * @see	#setUserID setUserID()
    * @see	#setPassword setPassword()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void login()
        throws IOException
    {
        // Log into the remote FTP system
        login(m_userID, m_password);
    }


    /***************************************************************************
    * Log on to the remote FTP system.
    *
    * @param	user
    * FTP user-ID for the remote system.
    *
    * @param	pwd
    * User password.  This can be empty (<tt>""</tt>).
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#login() login()
    * @see	#connect connect()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void login(String user, String pwd)
        throws IOException;


    /***************************************************************************
    * Determine if this client is logged to the remote FTP system.
    *
    * @return
    * True if this client has connected and a user if logged into a remote FTP
    * system, otherwise false.
    *
    * @see	#login() login()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public boolean isLoggedIn()
    {
        return m_isLoggedOn;
    }


    /***************************************************************************
    * Set the transfer mode to text (ASCII) or binary.
    * In text (ASCII) mode, files are transferred as text files, so that newline
    * sequences (CR, LF, or CR/LF) are converted into the local native newline
    * sequence (which is determined by the
    * <tt>System.getProperty("line.separator")</tt> setting).
    *
    * @param	flag
    * If true, the transfer mode is set to text (ASCII), otherwise it is set
    * to binary.
    *
    * @return
    * The previous mode setting.
    *
    * @since	1.9, 2007-07-26
    */
    //@Override
    public boolean setTextMode(boolean flag)
    {
        boolean		prev;

        // Set text (ASCII) or binary mode
        if (m_debugOut != null)
            m_debugOut.println("$ textMode: " + m_textMode + " -> " + flag);

        prev = m_textMode;
        return prev;
    }


    /***************************************************************************
    * Pings the remote FTP system.
    * This sends a "NOOP" FTP command to the remote system and receives its
    * reply.
    *
    * @throws	IOException
    * Thrown if an error occurs, e.g., the FTP connection is broken.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void ping()
        throws IOException;


    /***************************************************************************
    * Retrieve the identity information of the remote FTP system.
    *
    * @param	out
    * Output stream to which the identification information is to be written.
    *
    * @throws	IOException
    * Thrown if an I/O error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public abstract void getSystemInfo(OutputStream out)
        throws IOException;


    /***************************************************************************
    * Retrieve the current status of the remote FTP system.
    *
    * @param	out
    * Output stream to which the status is to be written.
    *
    * @throws	IOException
    * Thrown if an I/O error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public abstract void getStatus(OutputStream out)
        throws IOException;


    /***************************************************************************
    * Get (receive) a help listing of supported FTP commands from the remote FTP
    * system.
    *
    * @param	out
    * Output stream to which the help listing is to be written.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public abstract void getHelp(OutputStream out)
        throws IOException;


    /***************************************************************************
    * Set the working directory on the remote FTP system.
    *
    * @param	dir
    * Remote directory name.
    *
    * @return
    * The new current remote directory name.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#setRemoteDirUp setRemoteDirUp()
    * @see	#getRemoteDir getRemoteDir()
    * @see	#setLocalDir setLocalDir()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public String setRemoteDir(String dir)
        throws IOException
    {
        // Sanity checks
        if (dir == null)
            throw new NullPointerException("Null FTP directory name");
        if (dir.length() == 0)
            throw new FTPException("Empty FTP directory name");

        if (!m_isConnected)
            throw new FTPException("FTP session not connected");
        if (!m_isLoggedOn)
            throw new FTPException("Not logged into FTP session");

        // Change the remote working directory
        if (m_debugOut != null)
            m_debugOut.println("$ remoteDir: " + m_remoteDir + " -> " + dir);

        m_remoteDir = dir;
        return m_remoteDir;
    }


    /***************************************************************************
    * Set the working directory on the remote FTP system to the parent directory
    * of the current working directory.
    * In other words, change the directory to be one level up from the current
    * setting.
    *
    * @return
    * The new current remote directory name.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#getRemoteDir getRemoteDir()
    * @see	#setRemoteDir setRemoteDir()
    * @see	#setLocalDir setLocalDir()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract String setRemoteDirUp()
        throws IOException;


    /***************************************************************************
    * Retrieve the current working directory of the remote FTP system.
    *
    * @return
    * Remote directory name.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#setRemoteDir setRemoteDir()
    * @see	#setRemoteDirUp setRemoteDirUp()
    * @see	#getLocalDir getLocalDir()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public String getRemoteDir()
        throws IOException
    {
        return m_remoteDir;
    }


    /***************************************************************************
    * Set the working directory on the local system.
    * Note that this does not actually change the current working directory for
    * the Java program, but only establishes the default directory prefix to use
    * for local files sent or received by this FTP session.
    *
    * @param	dir
    * Local directory name.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#getLocalDir getLocalDir()
    * @see	#setRemoteDir setRemoteDir()
    * @see	#setRemoteDirUp setRemoteDirUp()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void setLocalDir(String dir)
        throws IOException
    {
        File	fdir;

        // Sanity checks
        if (dir == null)
            throw new NullPointerException("Null FTP directory name");

        // Check that the local directory exists
        fdir = new File(dir);
        if (!fdir.exists())
            throw new FTPException("Local directory does not exist: \""
                + dir + "\"");
        if (!fdir.isDirectory())
            throw new FTPException("Not a directory: \"" + dir + "\"");

        // Set the local directory
        m_localDir = fdir;
    }


    /***************************************************************************
    * Retrieve the working directory of the local system.
    *
    * @return
    * The current local directory name.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @see	#setLocalDir setLocalDir()
    * @see	#getRemoteDir getRemoteDir()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public String getLocalDir()
        throws IOException
    {
        return m_localDir.getPath();
    }


    /***************************************************************************
    * Get (receive) a file from the remote FTP system to the local system.
    *
    * @param	src
    * Remote source filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.
    *
    * @param	dst
    * Local source filename.  If this does not contain a directory prefix, the
    * current local working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#getFile(String, File) getFile()
    * @see	#getFile(String, OutputStream) getFile()
    * @see	#putFile(String, String) putFile()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void getFile(String src, String dst)
        throws IOException
    {
        File	fname;

        // Build a proper filename from the destination name
        fname = new File(dst);
        if (!isAbsoluteFilename(fname))
            fname = new File(m_localDir, dst);
        getFile(src, fname);
    }


    /***************************************************************************
    * Get (receive) a file from the remote FTP system to the local system.
    *
    * @param	src
    * Remote source filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.
    *
    * @param	dst
    * Local source filename.  If this does not contain a directory prefix, the
    * current local working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#getFile(String, String) getFile()
    * @see	#getFile(String, OutputStream) getFile()
    * @see	#putFile(File, String) putFile()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void getFile(String src, File dst)
        throws IOException
    {
        OutputStream	out;

        // Sanity checks
        if (src == null)
            throw new NullPointerException("Null FTP source file");
        if (dst == null)
            throw new NullPointerException("Null FTP target file");

        if (!m_isConnected)
            throw new FTPException("FTP session not connected");
        if (!m_isLoggedOn)
            throw new FTPException("Not logged into FTP session");

        // Open the target file as an output stream
        out = new FileOutputStream(dst);

        try
        {
            // Retrieve (get) the file from the remote FTP system
            getFile(src, out);
        }
        finally
        {
            out.close();
        }
    }


    /***************************************************************************
    * Get (receive) a file from the remote FTP system to the local system.
    *
    * @param	src
    * Remote source filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.
    *
    * @param	out
    * Output stream to write the contents of the file retrieved from the remote
    * FTP system to.  Note that this stream is flushed but is <i>not</i> closed
    * after the contents have been transmitted.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#getFile(String, File) getFile()
    * @see	#getFile(String, String) getFile()
    * @see	#putFile(InputStream, String) putFile()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void getFile(String src, OutputStream out)
        throws IOException;


    /***************************************************************************
    * Put (send) a file from the local system to the remote FTP system.
    *
    * @param	src
    * Local source filename.  If this does not contain a directory prefix, the
    * current local working directory is assumed.
    *
    * @param	dst
    * Remote target filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#putFile(File, String) putFile()
    * @see	#putFile(InputStream, String) putFile()
    * @see	#getFile(String, String) getFile()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void putFile(String src, String dst)
        throws IOException
    {
        File	fname;

        // Build a proper filename from the source name
        fname = new File(src);
        if (!isAbsoluteFilename(fname))
            fname = new File(m_localDir, src);
        putFile(fname, dst);
    }


    /***************************************************************************
    * Put (send) a file from the local system to the remote FTP system.
    *
    * @param	src
    * Local source filename.  If this does not contain a directory prefix, the
    * current local working directory is assumed.
    *
    * @param	dst
    * Remote target filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#putFile(String, String) putFile()
    * @see	#putFile(InputStream, String) putFile()
    * @see	#getFile(String, File) getFile()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public void putFile(File src, String dst)
        throws IOException
    {
        InputStream	in;

        // Sanity checks
        if (src == null)
            throw new NullPointerException("Null FTP source file");
        if (dst == null)
            throw new NullPointerException("Null FTP target file");

        if (!m_isConnected)
            throw new FTPException("FTP session not connected");
        if (!m_isLoggedOn)
            throw new FTPException("Not logged into FTP session");

        if (!src.exists())
            throw new FTPException("FTP source file does not exist: \""
                + src.getPath() + "\"");

        // Open the source file as an input stream
        in = new FileInputStream(src);

        try
        {
            // Store (put) the file to the remote FTP system
            putFile(in, dst);
        }
        finally
        {
            in.close();
        }
    }


    /***************************************************************************
    * Put (send) a file from the local system to the remote FTP system.
    *
    * @param	in
    * Input stream containing the contents of the file to send to the remote FTP
    * system.  Note that this stream is <i>not</i> closed after the contents
    * have been transmitted.
    *
    * @param	dst
    * Remote target filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#putFile(File, String) putFile()
    * @see	#putFile(String, String) putFile()
    * @see	#getFile(String, OutputStream) getFile()
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void putFile(InputStream in, String dst)
        throws IOException;


    /***************************************************************************
    * Append (send) a file from the local system to a file on the remote FTP
    * system.
    *
    * @param	src
    * Local source filename.  If this does not contain a directory prefix, the
    * current local working directory is assumed.
    *
    * @param	dst
    * Remote target filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#appendFile(File, String) appendFile()
    * @see	#appendFile(InputStream, String) appendFile()
    * @see	#getFile(String, String) getFile()
    *
    * @since	1.8, 2007-07-21
    */
    //@Override
    public void appendFile(String src, String dst)
        throws IOException
    {
        File	fname;

        // Build a proper filename from the source name
        fname = new File(src);
        if (!isAbsoluteFilename(fname))
            fname = new File(m_localDir, src);
        appendFile(fname, dst);
    }


    /***************************************************************************
    * Append (send) a file from the local system to a file on the remote FTP
    * system.
    *
    * @param	src
    * Local source filename.  If this does not contain a directory prefix, the
    * current local working directory is assumed.
    *
    * @param	dst
    * Remote target filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#appendFile(String, String) appendFile()
    * @see	#appendFile(InputStream, String) appendFile()
    * @see	#getFile(String, File) getFile()
    *
    * @since	1.8, 2007-07-21
    */
    //@Override
    public void appendFile(File src, String dst)
        throws IOException
    {
        InputStream	in;

        // Sanity checks
        if (src == null)
            throw new NullPointerException("Null FTP source file");
        if (dst == null)
            throw new NullPointerException("Null FTP target file");

        if (!m_isConnected)
            throw new FTPException("FTP session not connected");
        if (!m_isLoggedOn)
            throw new FTPException("Not logged into FTP session");

        if (!src.exists())
            throw new FTPException("FTP source file does not exist: \""
                + src.getPath() + "\"");

        // Open the source file as an input stream
        in = new FileInputStream(src);

        try
        {
            // Store (append) the file to the remote FTP system
            appendFile(in, dst);
        }
        finally
        {
            in.close();
        }
    }


    /***************************************************************************
    * Append (send) a file from the local system to a file on the remote FTP
    * system.
    *
    * @param	in
    * Input stream containing the contents of the file to send to the remote FTP
    * system.  Note that this stream is <i>not</i> closed after the contents
    * have been transmitted.
    *
    * @param	dst
    * Remote target filename.  If this does not contain a directory prefix, the
    * current remote working directory is assumed.  This may be null, in which
    * case the base filename of <tt>src</tt> (without the directory prefix) is
    * used.
    *
    * @throws	IOException
    * Thrown if the file could not be transmitted or if any other error occurs.
    *
    * @see	#appendFile(File, String) appendFile()
    * @see	#appendFile(String, String) appendFile()
    * @see	#getFile(String, OutputStream) getFile()
    *
    * @since	1.8, 2007-07-21
    */
    //@Override
    public abstract void appendFile(InputStream in, String dst)
        throws IOException;


    /***************************************************************************
    * Get (receive) a directory listing from the remote FTP system.
    *
    * @param	path
    * The remote directory or file name to list.  If this is empty
    * (<tt>""</tt>), the current remote working directory is assumed.
    *
    * @param	out
    * Output stream to which the directory listing is to be written.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public void getDirectoryList(String path, OutputStream out)
        throws IOException
    {
        getDirectoryList(path, 0, out);
    }


    /***************************************************************************
    * Get (receive) a directory listing from the remote FTP system.
    *
    * @param	path
    * The remote directory or filename to list.  If this is empty
    * (<tt>""</tt>), the current remote working directory is assumed.
    *
    * @param	max
    * Maximum number of filenames (output lines) to list.  A value of zero (0)
    * specifies that there is no maximum.
    *
    * @param	out
    * Output stream to which the directory listing is to be written.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public abstract void getDirectoryList(String path, int max,
        OutputStream out)
        throws IOException;


    /***************************************************************************
    * Get (receive) a list of filenames in a directory on the remote FTP system.
    *
    * @param	path
    * The remote directory or file name to list.  If this is empty
    * (<tt>""</tt>), the current remote working directory is assumed.
    *
    * @param	out
    * Output stream to which the directory listing is to be written.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public void getDirectoryNames(String path, OutputStream out)
        throws IOException
    {
        getDirectoryNames(path, 0, out);
    }


    /***************************************************************************
    * Get (receive) a list of filenames in a directory on the remote FTP system.
    *
    * @param	path
    * The remote directory or filename to list.  If this is empty
    * (<tt>""</tt>), the current remote working directory is assumed.
    *
    * @param	max
    * Maximum number of filenames to get.  A value of zero (0) specifies that
    * there is no maximum.
    *
    * @param	out
    * Output stream to which the directory listing is to be written.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	API 2.0, 1.13, 2010-07-12
    */
    //@Override
    public abstract void getDirectoryNames(String path, int max,
        OutputStream out)
        throws IOException;


    /***************************************************************************
    * Get (receive) a list of filenames in a directory on the remote FTP system.
    *
    * @param	path
    * The remote directory or file name to list.  If this is empty
    * (<tt>""</tt>), the current remote working directory is assumed.
    *
    * @return
    * A vector of <tt>String</tt>s containing the filenames in the remote
    * directory.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public ArrayList<String> getDirectoryNames(String path)
        throws IOException
    {
        return getDirectoryNames(path, null, 0);
    }


    /***************************************************************************
    * Get (receive) a list of filenames in a directory on the remote FTP system.
    *
    * @param	path
    * The remote directory or filename to list.  If this is empty
    * (<tt>""</tt>), the current remote working directory is assumed.
    *
    * @param	max
    * Maximum number of filenames to list.  A value of zero (0) specifies that
    * there is no maximum.
    *
    * @return
    * A vector of <tt>String</tt>s containing the filenames in the remote
    * directory.  Note that this may contain zero entries.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	1.4, 2007-02-28
    */
    //@Override
    public ArrayList<String> getDirectoryNames(String path, int max)
        throws IOException
    {
        return getDirectoryNames(path, null, 0);
    }


    /***************************************************************************
    * Rename a file or directory on the remote FTP system.
    *
    * @param	from
    * Old (existing) remote file or directory name to rename.
    *
    * @param	to
    * New name to rename the remote file or directory to.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void rename(String from, String to)
        throws IOException;


    /***************************************************************************
    * Remove a file on the remote FTP system.
    *
    * @param	file
    * Name of the file to delete.  If this specifies a relative file name, the
    * file is assumed to be located in the current remote working directory.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void removeFile(String file)
        throws IOException;


    /***************************************************************************
    * Create a directory on the remote FTP system.
    *
    * @param	dir
    * Name of the directory to create.  If this specifies a relative directory
    * name, the directory is created in the current remote working directory.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void createDirectory(String dir)
        throws IOException;


    /***************************************************************************
    * Remove a directory on the remote FTP system.
    *
    * @param	dir
    * Name of the directory to delete.  If this specifies a relative directory
    * name, the directory is assumed to be located in the current remote working
    * directory.
    *
    * @throws	IOException
    * Thrown if an error occurs.
    *
    * @since	1.1, 2006-04-14
    */
    //@Override
    public abstract void removeDirectory(String dir)
        throws IOException;


    /***************************************************************************
    * Finalization.
    *
    * @since	1.1, 2006-04-14
    */
    protected synchronized void finalize()
        throws Throwable
    {
        // Disconnect
        if (m_isConnected)
            disconnect();

        // Cascade
        super.finalize();
    }
}

// End FTPClientAdapter.java
