/*
 * @(#)FtpImplementation.java
 *
 * Copyright (c) 2004 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */

package com.jscape.inet.ftp;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;
import java.util.Vector;

/**
 * Base class for all FTP client implementations.
 */
@SuppressWarnings("unchecked")
public abstract class FtpImplementation {

    protected static final int DEFAULT_TIMEOUT = 30000;
    protected static final boolean DEFAULT_KEEPALIVE = false;
    protected static final int DEFAULT_FTP_PORT = 21;
    protected static final int DEFAULT_SFTP_PORT = 22;
    private static final int DEFAULT_BLOCK_SIZE = 8192;
    private static final int DEFAULT_BUFFER = -1;

    private Vector listeners = new Vector();
    protected Ftp ftp = null;
    protected int timeout = DEFAULT_TIMEOUT;
    protected boolean keepAlive = DEFAULT_KEEPALIVE;
    protected boolean tcpNoDelay = true;
    protected int linger = -1;
    protected int blockSize = DEFAULT_BLOCK_SIZE;
    protected String host = null;
    protected String portAddress = null;
    protected int port = 0;
    protected String user = null;
    protected String password = null;
    protected String account = null;
    protected String encoding = null;
    protected volatile boolean interrupted = false;
    protected String targetFile = null;
    protected String targetPath = null;
    protected boolean debug = false;
    protected PrintStream debugStream = System.out;
    protected boolean autoMode = false;

    // TODO code for S390
    protected String encodingWire = null;
    protected String encodingDisk = null;

    protected boolean autoDetectIpv6 = false;

    private static final int DEFAULT_PROXY_PORT = 1080;
    private static final String DEFAULT_PROXY_TYPE = "SOCKS5";
    protected String proxyUsername = null;
    protected String proxyPassword = null;

    protected String proxyHostname = null;
    protected int proxyPort = DEFAULT_PROXY_PORT;
    protected String proxyType = DEFAULT_PROXY_TYPE;
    private boolean preserveUploadTimestamp = false;
    private boolean preserveDownloadTimestamp = false;
    private TimeZone timeZone = TimeZone.getTimeZone("GMT");  // default timezone 

    

    /**
     * Sets reference to FTP instance.
     *
     * @param ftp reference to FTP instance.
     */
    public synchronized void setFtp(Ftp ftp) {
        if (ftp == null) {
            throw new NullPointerException();
        }

        this.ftp = ftp;
    }

    /**
     * Set the timezone. This is used for file timestamps.
     *
     * @param timeZone
     */
    public synchronized void setTimezone(TimeZone timeZone) {
        this.timeZone = timeZone;
    }

    /**
     * Get the associated timezone. This is used for file timestamps. Default is GMT
     *
     * @return timezone
     */
    public synchronized TimeZone getTimeZone() {
        return this.timeZone;
    }

    /**
     * Set the preserve download timestamp value.
     *
     * @param value
     */
    public synchronized void setPreserveDownloadTimestamp(boolean value) {
        this.preserveDownloadTimestamp = value;
    }

    /**
     * Get preserve download timestamp.
     *
     * @return boolean value
     */
    public synchronized boolean getPreserveDownloadTimestamp() {
        return this.preserveDownloadTimestamp;
    }

    /**
     * Get upload modification time.
     *
     * @return - boolean value
     */
    public synchronized boolean getPreserveUploadTimestamp() {
        return this.preserveUploadTimestamp;
    }

    /**
     * Set upload modification time, replaces the last modified timestamp with that of the local file after upload is complete.
     *
     * @param value - boolean value
     */
    public synchronized void setPreserveUploadTimestamp(boolean value) {
        this.preserveUploadTimestamp = value;
    }

    /**
     * Set auto detect Ipv6 mode.
     *
     * @param autoDetect
     */
    public synchronized void setAutoDetectIpv6(boolean autoDetect) {
        this.autoDetectIpv6 = autoDetect;
    }

    /**
     * Get auto detect Ipv6 mode.
     *
     * @return
     */
    public synchronized boolean getAutoDetectIpv6() {
        return this.autoDetectIpv6;
    }

    

    /**
     * Gets hostname of FTP server.
     *
     * @return hostname
     */
    public synchronized String getHost() {
        return this.host;
    }

    /**
     * Gets account for FTP server.
     * This should not be confused with username.
     *
     * @return the account
     *
     * @see #setUser(String)
     */
    public synchronized String getAccount() {
        return this.account;
    }

    /**
     * Sets transfer mode automatically based on file extension.
     *
     * @param autoMode
     */
    public synchronized void setAuto(boolean autoMode) {
        this.autoMode = autoMode;
    }

    /**
     * Sets hostname of FTP server.
     *
     * @param host the hostname or IP address of FTP server
     */
    public synchronized void setHost(String host) {
        this.host = host;
    }

    /**
     * Sets optional account for FTP server.
     * This should not be confused with username.
     *
     * @param account
     *
     * @see #setUser(String)
     */
    public synchronized void setAccount(String account) {
        this.account = account;
    }

    

    /**
     * Gets port of FTP server.
     *
     * @return port
     */
    public synchronized int getPort() {
        return this.port;
    }

    

    /**
     * Sets port of FTP server.
     *
     * @param port the port of FTP server
     */
    public synchronized void setPort(int port) {
        this.port = port;
    }

    

    /**
     * Gets username of FTP user.
     *
     * @return username
     */
    public synchronized String getUser() {
        return this.user;
    }

    

    /**
     * Sets username of FTP user.
     *
     * @param user the username of FTP user
     */
    public synchronized void setUser(String user) {
        this.user = user;
    }

    /**
     * Sets use EPSV indicator. When it is true then will be sent EPSV.
     *
     * @param useEPSV The use EPSV value
     */
    public abstract void setUseEPSV(boolean useEPSV);

    /**
     * Gets use EPSV value.
     *
     * @return <code>true</code> the passive mode will be sent EPSV command
     */
    public abstract boolean getUseEPSV();

    /**
     * Sets use EPSV indicator. When it is true then will be sent EPSV.
     *
     * @param value The use EPSV value
     */
    public abstract void setUseEPRT(boolean value);

    /**
     * Gets use EPRT value.
     *
     * @return <code>true</code> the passive mode will be sent EPRT command
     */
    public abstract boolean getUseEPRT();

    

    /**
     * Returns current FTP user password.
     *
     * @return current FTP password.
     */
    public synchronized String getPassword() {
        return this.password;
    }

    

    /**
     * Sets password of FTP user.
     *
     * @param pwd the password of FTP user
     */
    public synchronized void setPassword(String pwd) {
        this.password = pwd;
    }

    

    /**
     * Gets the timeout for opening connection to FTP server.
     *
     * @return timeout in milliseconds
     */
    public synchronized int getTimeout() {
        return this.timeout;
    }

    

    /**
     * Sets the timeout for opening connection to FTP server.
     *
     * @param timeout the timeout in milliseconds
     */
    public synchronized void setTimeout(int timeout) {
        this.timeout = (timeout > 0) ? timeout : 0;
    }

    /**
     * Set Linger.
     *
     * @param linger
     */
    public synchronized void setLinger(int linger) {
        this.linger = linger;
    }

    /**
     * Get Linger.
     *
     * @return linger
     */
    public synchronized int getLinger() {
        return linger;
    }

    

    /**
     * Set socket keep alive.
     *
     * @param keepAlive
     */
    public synchronized void setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    /**
     * Get keep alive.
     *
     *  @return keep alive
     */
    public synchronized boolean getKeepAlive() {
        return this.keepAlive;
    }
    
    public boolean getTcpNoDelay() {
        return this.tcpNoDelay;
    }
    
    public void setTcpNoDelay(boolean value) {
        this.tcpNoDelay = value;
    }
    
    /**
     * Gets character encoding used when issuing commands and transferring files.
     *
     * @return the character encoding to be used
     *
     * @deprecated use the getWireEncoding method
     */
    public synchronized String getEncoding() {
        return this.getDiskEncoding();
    }

    

    /**
     * Sets character set used when issuing commands and transferring files.
     *
     * @param encoding the character encoding to be used
     *
     * @deprecated use the setWireEncoding method
     */
    public synchronized void setEncoding(String encoding) {
        this.setDiskEncoding(encoding);

    }

    /**
     * Sets the indicator value which will be used by methods that invokes
     * getFileSize.
     *
     * @param error Error indicator value
     */
    public abstract void setErrorOnSizeCommand(boolean error);

    /**
     * Gets the error on size command indicator.
     *
     * @return <code>true</code> then method which invokes getFileSize will throw
     *         an exception when it caused an error, <code>false</code> the error will be ignored.
     */
    public abstract boolean getErrorOnSizeCommand();

    

    /**
     * Checks if transfer process was interrupted.
     *
     * @return true if transfer process was interrupted.
     */
    public boolean interrupted() {
        return this.interrupted;
    }

    

    /**
     * Interrupts the current data transfer and sets interrupted flag to true.
     */
    public void interrupt() {
        this.interrupted = true;
    }

    

    /**
     * Resets the interrupted flag to false.
     */
    public void reset() {
        this.interrupted = false;
    }

    

    /**
     * Connects to FTP server and performs login.
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void connect()
            throws FtpException;

    public abstract void setDataPort(int dataPort);

    public abstract int getDataPort();

    /**
     * Sets the connectBefore property value
     *
     * @param connect It indicates if the connection will be established before
     *                send the command
     */
    public abstract void setConnectBeforeCommand(boolean connect);

    /**
     * Gets the connectBefore indicator
     *
     * @return <code>true</code> the connection will be establiched before send the command
     */
    public abstract boolean getConnectBeforeCommand();

    /**
     * Gets the data port start range for incoming data connections
     *
     * @return Start data port range for incoming data connections
     */
    public abstract int getDataPortStart();

    /**
     * Gets the data port end range for incoming data connections
     *
     * @return End data port range for incoming data connections
     */
    public abstract int getDataPortEnd();

    /**
     * Sets the start data port range for incomings connections
     *
     * @param dataPort Start data Port range for incomings connections
     */
    public abstract void setDataPortStart(int dataPort);

    /**
     * Sets the end data port range for incomings connections
     *
     * @param dataPort End data Port range for incomings connections
     */
    public abstract void setDataPortEnd(int dataPort);


    /**
     * Connects to FTP server and optionally performs login
     *
     * @param login true to login false otherwise
     *
     * @throws FtpException if an I/O or FTP error occurs
     */
    public abstract void connect(boolean login)
            throws FtpException;

    /**
     * Logs into FTP server after establishing a connection.
     * This method should only be used if login is not performed automatically during connect.
     *
     * @throws FtpException if an I/O or FTP error occurs
     */
    public abstract void login()
            throws FtpException;

    

    /**
     * Changes the password.
     *
     * @param oldPassword The old password.
     * @param newPassword The new password.
     *
     * @throws FtpException FtpException if underlayed I/O or FTP error occurs.
     */
    public abstract void changePassword(String oldPassword,
                                        String newPassword)
            throws FtpException;


    

    /**
     * Quits FTP session and disconnects from FTP server.
     */
    public abstract void disconnect();

    

    /**
     * Checks if client is connected to FTP server.
     *
     * @return <code>true</code> if connected <code>false</code> otherwise
     */
    public abstract boolean isConnected();

    

    /**
     * Issues command to FTP server.
     *
     * @param command the command to issue
     *
     * @return response code
     *
     * @throws FtpException if an I/O or FTP error occurs
     */
    public abstract String issueCommand(String command)
            throws FtpException;

    

    /**
     * Issues command to FTP server.
     *
     * @param command the command to issue
     *
     * @return response code
     *
     * @throws FtpException if an I/O or FTP error occurs
     */
    public abstract String issueCommandCheck(String command)
            throws FtpException;

    /**
     * Looks for the command argument into the server features list to validate if it is supported
     *
     * @param command The feature will be searched
     *
     * @return <code>true</code> if the command is found into the server feature list;
     *         <code>false</code> otherwise
     */
    public abstract boolean isFeatureSupported(String command);

    

    /**
     * Uploads data to FTP server.
     *
     * @param in         data input stream.
     * @param remoteName the filename to store file as on FTP server
     * @param append     <code>true</code> to append to remote file on FTP server,
     *                   <code>false</code> otherwise
     * @param off        file offset.
     * @param total      total data lenght
     * @param unique     To use upload unique file
     *
     * @return remoteFileName response the response after the openConnection for STOU file name
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract String upload(InputStream in,
                                  String remoteName,
                                  boolean append,
                                  long off,
                                  long total,
                                  boolean unique)
            throws FtpException;

    

    /**
     * Downloads file from FTP server and writes contents to provided OutputStream.
     *
     * @param out        data output stream.
     * @param remoteName the filename to store file as on FTP server.
     * @param off        file offset
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void download(OutputStream out,
                                  String remoteName,
                                  long off)
            throws FtpException;

    /**
     * Gets the InputStream from the data connection
     *
     * @param remoteName Remote file name
     * @param off        file offset
     *
     * @return Returns a InputStream from the data connection instance
     *
     * @throws FtpException if FTP error occurs.
     */
    public abstract InputStream getInputStream(
            String remoteName,
            long off)
            throws FtpException;

    /**
     * Gets the OutputStream from the data connection
     *
     * @param fileName The name of the file
     * @param append   <code>true</code> to append to remote file on FTP server,
     *                 <code>false</code> otherwise
     * @param off      file offset
     *
     * @return Returns a OutputStream from the data connection instance
     *
     * @throws FtpException if FTP error occurs.
     */
    public abstract OutputStream getOutputStream(String fileName, boolean append,
                                                 long off)
            throws FtpException;

    /**
     * Uploads the data as file to be created in the current directory
     * under a name unique to that directory
     *
     * @param in       data input stream.
     * @param fileName The file Name
     *
     * @return returns the file name in the ftp server
     *
     * @throws FtpException
     */
    public abstract String uploadUnique(InputStream in, String fileName)
            throws FtpException;

    

    /**
     * Gets filesize of remote file from FTP server as represented in ASCII.
     * This feature is not supported by RFC-959 so may not work on all FTP servers.
     * For larger files it may take some time to return as server will often
     * convert file to ASCII before returning a filesize.
     *
     * @param remoteName the remote file
     *
     * @return filesize in bytes
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract long getFileSize(String remoteName)
            throws FtpException;

    

    /**
     * Gets date/timestamp of remote file from FTP server
     * This feature is not supported by RFC-959 so may not work on all FTP servers.
     *
     * @param remoteName the remote file
     *
     * @return date the date/timestamp of remote file
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract Date getFileTimeStamp(String remoteName)
            throws FtpException;

    //  --------------------------------------------------------------------------

    /**
     * Sets file creation time with a new value.
     * This command is not specified in RFC 959 and it is not support by many Ftp servers.
     *
     * @param remoteName File will be modified its creation time.
     * @param date       The new creation time
     *
     * @throws FtpException If an error occurs.
     */
    public abstract void setFileCreationTime(String remoteName, Date date)
            throws FtpException;

    //  --------------------------------------------------------------------------

    /**
     * Sets the file modification time with a new value.
     * This command is not specified in RFC 959 and it is not support by many Ftp servers.
     *
     * @param remoteName File will be modified its modification time.
     * @param date       The new modification time
     *
     * @throws FtpException If an error occurs
     */
    public abstract void setFileModificationTime(String remoteName, Date date)
            throws FtpException;

    

    /**
     * Deletes remote file from FTP server.
     *
     * @param remoteName name of file to delete
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void deleteFile(String remoteName)
            throws FtpException;

    

    /**
     * Renames remote file on FTP server.
     *
     * @param remoteName name of file to rename
     * @param newName    new name of file
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void renameFile(String remoteName, String newName)
            throws FtpException;

    

    /**
     * Gets directory listing from FTP server.
     *
     * @param filter the filename filter
     *
     * @return a <code>Vector</code> of <code>String</code> objects
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract Vector getDirListing(String filter)
            throws FtpException;

    public abstract Vector getNameListing(String filter)
            throws FtpException;

    

    /**
     * Gets current working directory of FTP server.
     *
     * @return current directory
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract String getDir()
            throws FtpException;

    

    /**
     * Sets current directory on FTP server.
     *
     * @param remoteName the remote directory
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void setDir(String remoteName)
            throws FtpException;

    

    /**
     * Sets current directory on FTP server to parent directory.
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void dirUp()
            throws FtpException;

    

    /**
     * Makes directory on FTP server.
     *
     * @param newName name of directory to make
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void makeDir(String newName)
            throws FtpException;

    

    /**
     * Deletes directory from FTP server.
     *
     * @param remoteName the directory to remove
     * @param recurse    <code>true</code> if it should delete files and
     *                   subdirectories within directory
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract void deleteDir(String remoteName, boolean recurse)
            throws FtpException;

    

    /**
     * Gets file listing of files matching filter criteria.
     *
     * @param filter a filename filter
     *
     * @return a directory listing
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public synchronized String getDirListingAsString(String filter)
            throws FtpException {
        StringBuffer buf = new StringBuffer();
        Vector files = getDirListing(filter);

        for (int i = 0; i < files.size(); ++i) {
            buf.append(files.elementAt(i).toString()).append("\r\n");
        }

        return buf.toString();
    }

    /**
     * Gets the file description for the specified filename.
     *
     * @param filename target filename. May be empty for current diectory
     *
     * @return file description
     *
     * @throws FtpException if an error occurs
     */
    public abstract FtpFile getMachineFileListing(String filename)
            throws FtpException;

    /**
     * Gets file listing of specified directory using MLSD command.
     *
     * @param dirname directory name
     *
     * @return a directory listing
     *
     * @throws FtpException if an error occurs
     */
    public abstract Vector getMachineDirListing(String dirname)
            throws FtpException;

    /**
     * Gets file listing of specified directory using MLSD command.
     *
     * @param dirname
     * @param regex
     *
     * @return
     *
     * @throws FtpException
     */
    public abstract Vector getMachineDirListing(String dirname, String regex)
            throws FtpException;

    /**
     * Returns supported server features.
     *
     * @return an enumeration of features strings
     *
     * @throws FtpException if an error occurs
     */
    public abstract Vector getFeatures()
            throws FtpException;

//        String listing = getDirListingAsString(filter);
//
//        if (listing == null || listing.length() == 0) {
//            return new Vector().elements();
//        }
//
//        try {
//            BufferedReader in = null;
//
//            ByteArrayInputStream bin =
//                    (this.encoding != null)
//                    ? new ByteArrayInputStream(listing.getBytes(this.encoding))
//                    : new ByteArrayInputStream(listing.getBytes());
//
//            in = new BufferedReader(getInputStreamReader(bin));
//
//            FtpFileParser parser = this.ftp.getFtpFileParser();
//
//            if (parser == null
//                || (parser instanceof NTParser
//                    && !Character.isDigit(listing.charAt(0)))) {
//                parser = new UnixParser();
//            }
//
//            Enumeration files = parser.parse(in);
//            fireEvent(new FtpListingEvent(this.ftp, files));
//
//            return files;
//        } catch (IOException ex) {
//            throw new FtpException(ex.getMessage(), ex);
//        }
//    }

    

    /**
     * Gets the system type of remote FTP server.
     *
     * @return system type
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    public abstract String getSystemType()
            throws FtpException;

    

    /**
     * Sends a NOOP (No Operation) command to FTP server.
     *
     * @return the response to the command
     *
     * @throws FtpException if an I/O or FTP error occurs
     */
    public abstract String noop()
            throws FtpException;

    

    /**
     * Returns current transfer mode.
     *
     * @return current transfer mode.
     */
    public abstract int getTransferMode();

    

    /**
     * Sets transfer mode.
     *
     * @param mode desired transfer mode.
     *
     * @throws FtpException if underlayed I/O or FTP error occurs.
     */
    public abstract void setTransferMode(int mode)
            throws FtpException;

    

    /**
     * Tests the passive mode for data connections.
     *
     * @return true if in passive mode.
     */
    public abstract boolean isPassive();

    

    /**
     * Sets the passive mode for data connections.
     *
     * @param passive if true, the transfers will be passive.
     */
    public abstract void setPassive(boolean passive);

    /**
     * Sets a NAT address which will be used after PASV command.
     *
     * @param ipAddress The NAT IP address.
     *
     * @throws FtpException If an error occurs.
     */
    public abstract void setNATAddress(String ipAddress)
            throws FtpException;

    /**
     * Get the NAT Address
     *
     * @return address
     */
    public abstract String getNATAddress();


    /**
     * Sets whether the data will be compressed during the transfer.
     *
     * @param compression Compression indicator.
     */
    public abstract void setCompression(boolean compression);

    /**
     * Gets whether the data is compressed during the transfering. The data will be compressed
     * if the Ftp server support this feature (MODE Z command)
     *
     * @return <code>true</code> data will be compressed, <code>false</code> otherwise.
     */
    public abstract boolean getCompression();

    

    /**
     * Gets response code from last FTP command.
     *
     * @return response code
     */
    public abstract int getResponseCode();

    

    /**
     * Gets blocksize for use in transferring files.
     * Default blocksize is 4096 bytes
     *
     * @return blocksize in bytes
     */
    public synchronized int getBlockTransferSize() {
        return this.blockSize;
    }

    

    /**
     * Sets block size for use in transferring files.
     * Default blocksize is 4096 bytes
     *
     * @param size the blocksize in bytes
     */
    public synchronized void setBlockTransferSize(int size) {
        if (size <= 0) {
            throw new IllegalArgumentException(
                    "wrong block size passed: " + size);
        }

        this.blockSize = size;
    }

    

    /**
     * Gets debugging state.
     *
     * @return <code>true</code> if debugging is on
     */
    public synchronized boolean getDebug() {
        return this.debug;
    }

    

    /**
     * Sets debugging state.
     * All debugPrint statements are reported to debugPrint stream.
     * Default debugPrint stream is System.out
     *
     * @param debug <code>true</code> to turn debugging on
     */
    public synchronized void setDebug(boolean debug) {
        this.debug = debug;
    }

    

    /**
     * Gets PrintStream used in reporting debugPrint statements.
     * Default PrintStream is System.out
     *
     * @return the PrintStream used in reporting debugPrint statements
     */
    public synchronized PrintStream getDebugStream() {
        return this.debugStream;
    }

    

    /**
     * Sets PrintStream used in reporting debugPrint statements.
     * Default PrintStream is System.out
     *
     * @param debugStream the PrintStream to send debugPrint statements to
     */
    public synchronized void setDebugStream(PrintStream debugStream) {
        this.debugStream = debugStream;
    }

    

    /**
     * Removes specified Ftp event listener.
     *
     * @param listener the listener to remove
     */
    public void addListener(FtpListener listener) {
        Vector listeners = (Vector) this.listeners.clone();
        listeners.addElement(listener);

        synchronized (this) {
            this.listeners = listeners;
        }
    }

    

    /**
     * @param listener
     */
    public void removeListener(FtpListener listener) {
        Vector listeners = (Vector) this.listeners.clone();
        listeners.removeElement(listener);

        synchronized (this) {
            this.listeners = listeners;
        }
    }

    /**
     * Get listeners.
     *
     * @return vector
     */
    public synchronized Vector getListeners() {
        return this.listeners;
    }

    /**
     * Set listeners.
     *
     * @param listeners
     */
    public synchronized void setListeners(Vector listeners) {
        this.listeners = listeners;
    }

    

    /**
     * Sets target file name. Used for creating FTP events.
     *
     * @param file target file name.
     */
    public synchronized void setTargetFile(String file) {
        this.targetFile = file;
    }

    

    /**
     * Sets target path. Used for creating FTP events.
     *
     * @param path target path.
     */
    public synchronized void setTargetPath(String path) {
        this.targetPath = path;
    }

    

    /**
     * Fires FTP event.
     *
     * @param ev event to fire.
     */
    protected synchronized void fireEvent(FtpEvent ev) {
        for (Iterator i = this.listeners.iterator(); i.hasNext(); ) {
            FtpListener listener = (FtpListener) i.next();
            ev.accept(listener);
        }
    }

    /**
     * Checks if there are listeners registered to capture events from the Ftp class
     *
     * @return true if listeners found, false otherwise.
     */
    protected synchronized boolean hasListeners() {
        return listeners.size() > 0;
    }

    

    /**
     * Creates <code>InputStreamReader</code> instance for passed input stream.
     *
     * @param in input stream.
     *
     * @return new <code>InputStreamReader</code> instance.
     *
     * @throws FtpException if an I/O error occurs.
     */
    protected synchronized InputStreamReader getInputStreamReader(
            InputStream in)
            throws FtpException {
        try {
            // TODO S390 support      
            return (this.encodingWire != null && this.encodingWire.length() > 0)
                   ? new InputStreamReader(in, this.encodingWire)
                   : new InputStreamReader(in, StandardCharsets.UTF_8);
        } catch (Exception ex) {
            throw new FtpException(ex.getMessage(), ex);
        }
    }

    

    /**
     * Creates <code>OutputStreamWriter</code> instance for passed output stream.
     *
     * @param out output stream.
     *
     * @return new <code>OutputStreamWriter</code> instance.
     *
     * @throws FtpException if an I/O error occurs.
     */
    protected synchronized OutputStreamWriter getOutputStreamWriter(
            OutputStream out)
            throws FtpException {
        try {
            return (this.encodingWire != null && this.encodingWire.length() > 0)
                   ? new OutputStreamWriter(out, this.encodingWire)
                   : new OutputStreamWriter(out);
        } catch (UnsupportedEncodingException ex) {
            throw new FtpException(ex.getMessage(), ex);
        }
    }

    

    /**
     * Prints debug message.
     *
     * @param msg message to print.
     */
    protected synchronized void debugPrint(String msg) {
        if (this.debug && msg != null && msg.trim().length() != 0) {
            if (msg.startsWith("PASS")) {
                this.debugStream.println("PASS ******");
            } else {
                this.debugStream.println(msg);
            }
        }
    }

    

    /**
     * Skips passed bytes number in input stream.
     *
     * @param in  input stream.
     * @param off bytes to skip.
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    protected void skip(InputStream in, long off)
            throws FtpException {
        try {
            in.skip(off);
        } catch (IOException e) {
            throw new FtpException(e.getMessage(), e);
        }
    }

    

    /**
     * Deletes deirectory recursively.
     *
     * @param remoteName directory to delete.
     *
     * @throws FtpException if an I/O or FTP error occurs.
     */
    protected synchronized void deleteDirRecursive(String remoteName)
            throws FtpException {
        String curDir = getDir();
        setDir(remoteName);

        try {
            Vector files = getDirListing("");

            // delete directory content
            for (int i = 0; i < files.size(); ++i) {
                FtpFile file = (FtpFile) files.elementAt(i);

                String name = file.getFilename();

                if (!file.isDirectory()) {
                    deleteFile(name);
                } else if (!name.equals(".") && !name.equals("..")) {
                    deleteDir(name, true);
                }
            }
        } finally {
            setDir(curDir);
        }

        // delete directory itself
        if (!remoteName.equals(".") && !remoteName.equals("..")) {
            deleteDir(remoteName, false);
        }
    }

    // TODO code for S390

    

    /**
     * Gets character encoding used when reading data from disk for ASCII file transfer.
     *
     * @return the character encoding to be used
     */
    public String getDiskEncoding() {
        return this.encodingDisk;
    }

    

    /**
     * Sets character set used when reading data from disk for ASCII file transfer.
     *
     * @param encoding the character encoding to be used
     */
    public void setDiskEncoding(String encoding) {
        this.encodingDisk = encoding;
    }

    /**
     * Gets character encoding used when issuing commands and reading files from FTP server in ASCII mode.
     *
     * @return the character encoding to be used
     */
    public String getWireEncoding() {
        return this.encodingWire;
    }

    

    /**
     * Sets character set used when issuing commands and transferring files in ASCII mode.
     *
     * @param encoding the character encoding to be used
     */
    public void setWireEncoding(String encoding) {
        this.encodingWire = encoding;
    }


    /**
     * Gets the external address to use when using active connections using the PORT command.
     *
     * @return the external ip address
     */
    public String getPortAddress() {
        return portAddress;
    }

    /**
     * Sets the external address to use when using active connections using the PORT command.
     *
     * @param string an ip address
     */
    public void setPortAddress(String string) {
        portAddress = string;
    }

    /**
     * Sets the proxy authentication username and password to use with
     * proxy server.
     * To clear these settings invoke the #clearProxySettings method.
     *
     * @param proxyUsername the proxy username
     * @param proxyPassword the proxy password
     *
     * @see #clearProxySettings
     */
    public void setProxyAuthentication(String proxyUsername,
                                       String proxyPassword) {
        this.proxyUsername = proxyUsername;
        this.proxyPassword = proxyPassword;
    }

    /**
     * Get proxy username.
     *
     * @return username
     */
    public String getProxyUsername() {
        return this.proxyUsername;
    }

    /**
     * Get proxy password.
     *
     * @return password
     */
    public String getProxyPassword() {
        return this.proxyPassword;
    }

    /**
     * Get proxy hostname.
     *
     * @return hostname
     */
    public String getProxyHostname() {
        return this.proxyHostname;
    }

    /**
     * Get proxy port.
     *
     * @return port
     */
    public int getProxyPort() {
        return this.proxyPort;
    }

    /**
     * Get proxy type.
     *
     * @return proxy type
     */
    public String getProxyType() {
        return this.proxyType;
    }

    /**
     * Sets the proxy hostname and port for this FTP connection.
     * To clear these settings invoke the #clearProxySettings method.
     *
     * @param proxyHostname the hostname or ip address of the proxy server
     * @param proxyPort     the port of the proxy server to connect to
     *
     * @see #clearProxySettings
     */
    public void setProxyHost(String proxyHostname, int proxyPort) {
        this.proxyHostname = proxyHostname;
        this.proxyPort = proxyPort;
    }

    /**
     * Sets the proxy type will be used for this FTP connection.
     *
     * @param proxyType The proxy type. Values allowed: HTTP, SOCKS5
     */
    public void setProxyType(String proxyType) {
        this.proxyType = proxyType;
    }

//  --------------------------------------------------------------------------

    /**
     * Clears SOCKS related settings.
     * <p/>
     * <br>
     */
    public void clearProxySettings() {
        this.setProxyAuthentication(null, null);
        this.setProxyHost(null, DEFAULT_PROXY_PORT);
        this.setProxyType(DEFAULT_PROXY_TYPE);
    }

    /**
     * Reads server response
     *
     * @throws FtpException if underlayed I/O or FTP error occurs.
     */
    public abstract void readResponse()
            throws FtpException;

    /**
     * Sets the send buffer size option of the ftp connection.
     *
     * @param sendBufferSize Send buffer size.
     */
    public abstract void setSendBufferSize(int sendBufferSize);

    /**
     * Sets the receive buffer size option of the ftp connection.
     *
     * @param receiveBufferSize Receive buffer size.
     */
    public abstract void setReceiveBufferSize(int receiveBufferSize);
}