Logo Search packages:      
Sourcecode: jftp version File versions  Download package

FtpConnection.java

/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package net.sf.jftp.net;


//***now all config files are imported
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.JLabel;
import javax.swing.JOptionPane;

import net.sf.jftp.config.LoadSet;
import net.sf.jftp.config.SaveSet;
import net.sf.jftp.config.Settings;
import net.sf.jftp.system.StringUtils;
import net.sf.jftp.system.logging.Log;


/**
 * All control transactions are made here.
 * See doc/FtpDownload.java for examples and note
 * that this class is event-driven and not Exception-based!
 * You might want to take a look at ConnectionListener, too.
 *
 * @author  David Hansmann, hansmann.d@debitel.net
 * @version 1.30+, June 2003
 */
00052 public class FtpConnection implements BasicConnection, FtpConstants
{
    private static final boolean TESTMODE = false;

    // everything starting with this will be treated
    // as a negative response
    private final static String NEGATIVE = "5";
    private final static String NEGATIVE2 = "4";

    // everything starting with this will be treated
    // as a positive response
    private final static String POSITIVE = "2";

    // for resuming
    private final static String PROCEED = "3"; //"350";
    private final static char MORE_LINES_APPENDED = '-';

    /** Internal download constant */
00070     public final static String DOWNLOAD = "DOWNLOAD";

    /** Internal upload constant */
00073     public final static String UPLOAD = "UPLOAD";

    /** Internal abort command constant */
00076     public final static String ABOR = "ABOR";

    //*** 1.44: if LIST is not set, then load it from advanced settings
    //***       And if the file for it is not found, then create it
    //***       with LIST -laL as the default
    public static String LIST_DEFAULT = "LIST -laL";
    public static String LIST = "default";
    public final static String ASCII = "A";
    public final static String BINARY = "I";
    public final static String EBCDIC = "E";
    public final static String L8 = "L 8";
    public final static String STREAM = "S";
    public final static String BLOCKED = "B";
    public final static String COMPRESSED = "C";
    private static boolean useStream = true;
    private static boolean useBlocked = true;
    private static boolean useCompressed = true;

    // active ftp ports
    private static int porta = 5; // 5 * 256 + 1 = 1281
    private static int portb = 1;

    //***

    /**
    *        Used to determine the type of transfer.
    */
00103     public boolean hasUploaded = false;
    public boolean work = true;
    private boolean msg = true;
    private boolean ok = true;
    private String pwd = "";
    private String initCWD = Settings.defaultDir;
    private String[] loginAck = new String[]
                                {
                                    FTP331_USER_OK_NEED_PASSWORD,
                                    FTP230_LOGGED_IN
                                };
    private String osType = "";
    private String dataType;
    private FtpTransfer lastTransfer = null;
    private boolean modeStreamSet = false;
    private DataConnection dcon = null;
    private Vector listeners = new Vector();
    private ConnectionHandler handler = new ConnectionHandler();

    // directory downloaded to
    private String localPath = null;

    /** the host */
00126     private String host = "";

    /** the user */
00129     private String username;

    /** the password */
00132     private String password;

    /** the port */
00135     private int port = 21;

    /** the InputStream of the control connection */
00138     private BufferedReader in;

    /** the OutputStream of the control connection */
00141     private JConnection jcon;

    /** we are disconnected */
00144     private boolean connected = false;
    private boolean shortProgress = false;
    private boolean isDirUpload = false;
    private String baseFile;
    private int fileCount;
    private String typeNow = "";
    private String crlf = null;

    /**
    * May contain date listing
    */
00155     public Vector dateVector = new Vector();
    public Vector currentListing = new Vector();
    public Vector currentFiles = new Vector();
    public Vector currentSizes = new Vector();
    public Vector currentPerms = new Vector();

    /**
    * Create an instance with a given host
    *
    * @param host The host to connect to
    */
00166     public FtpConnection(String host)
    {
        this.host = host;

        File f = new File(".");
        setLocalPath(f.getAbsolutePath());
    }

    /**
    * Create an instance with a given host, port and initial remote working directory
    *
    * @param host The host to connect to
    * @param port The port used for the control connection, usually 21
    * @param initCWD The initial remote working directory
    */
00181     public FtpConnection(String host, int port, String initCWD)
    {
        this.host = host;
        this.port = port;
        this.initCWD = initCWD;

        File f = new File(".");
        setLocalPath(f.getAbsolutePath());
    }

    /**
    * Connect to the server an log in.
    *
    * Does also fire directory update and connection initialized event if successful or
    * connection failed event if failed.
    *
    * @param username The username
    * @param password  The password
    * @param initCWD The initial remote working directory
    * @param crlfx \n, \r, \r\n, CR, LF or CRLF - line break style of the server
    * @return  WRONG_LOGIN_DATA, OFFLINE, GENERIC_FAILED or LOGIN_OK status code
    */
00203     public FtpConnection(String host, int port, String initCWD, String crlfx)
    {
        this.host = host;
        this.port = port;
        this.initCWD = initCWD;
      
        if(crlfx != null) {
            if(crlfx.equals("\n") || crlfx.equals("\r") || crlfx.equals("\r\n")) this.crlf = crlfx;
            crlfx = crlfx.trim().toUpperCase();
            if(crlfx.equals("LF")) this.crlf = "\n";
            else if(crlfx.equals("CR")) this.crlf = "\r";
            else if(crlfx.equals("CRLF")) this.crlf = "\r\n";
        }
        //Log.out("\n\nCRLF:---"+crlf+"---\n\n");

        File f = new File(".");
        setLocalPath(f.getAbsolutePath());
    }

    /**
    * Connect to the server an log in.
    *
    * Does also fire directory update and connection initialized event if successful or
    * connection failed event if failed.
    *
    * @param username The username
    * @param password  The password
    * @return  WRONG_LOGIN_DATA, OFFLINE, GENERIC_FAILED or LOGIN_OK status code
    */
00232     public int login(String username, String password)
    {
        this.username = username;
        this.password = password;

        int status = LOGIN_OK;

        if(msg)
        {
            Log.debug("Connecting to " + host);
        }

        jcon = new JConnection(host, port);

        if(jcon.isThere())
        {
            in = jcon.getReader();

            if(getLine(POSITIVE) == null)//FTP220_SERVICE_READY) == null)
            {
                ok = false;
                Log.debug("Server closed Connection, maybe too many users...");
                status = OFFLINE;
            }

            if(msg)
            {
                Log.debug("Connection established...");
            }

            jcon.send(USER + " " + username);

            if(!getLine(loginAck).startsWith(POSITIVE))//FTP230_LOGGED_IN))
            {
                jcon.send(PASS + " " + password);

                if(success(POSITIVE))//FTP230_LOGGED_IN))
                {
                    if(msg)
                    {
                        Log.debug("Logged in...");
                    }
                }
                else
                {
                    Log.debug("Wrong password!");
                    ok = false;
                    status = WRONG_LOGIN_DATA;
                }
            }

            // end if(jcon)...
        }
        else
        {
            if(msg)
            {
                Log.debug("FTP not available!");
                ok = false;
                status = GENERIC_FAILED;
            }
        }

        if(ok)
        {
            connected = true;
            system();

            //if(getOsType().indexOf("MVS") < 0) 
            binary();

            if(initCWD.trim().equals(Settings.defaultDir) ||
                   !chdirNoRefresh(initCWD))
            {
                //System.out.println("default dir...");
                //if(!chdirNoRefresh(getPWD()))
                //{
                //System.out.println("FtpConnection should not do this...");
                //      if(!chdirNoRefresh("~"))
                //      {
                //            chdirNoRefresh("/");
                //      }
                //}
                updatePWD();
            }

            //Array of length 6 created, as that is maximum LoadSet size (need
            //public variable with that constant there for it to refer to?)
            String[] advSettings = new String[6];

            if(getOsType().indexOf("OS/2") >= 0)
            {
                LIST_DEFAULT = "LIST";
            }

            if(LIST.equals("default"))
            {
                //just get the first item (somehow it knows first is the
                //FTP list command)
                advSettings = LoadSet.loadSet(Settings.adv_settings);

                //*** IF FILE NOT FOUND, CREATE IT AND SET IT TO LIST_DEFAULT
                if(advSettings == null)
                {
                    LIST = LIST_DEFAULT;

                    SaveSet s = new SaveSet(Settings.adv_settings, LIST);
                }
                else
                {
                    LIST = advSettings[0];

                    if(LIST == null)
                    {
                        LIST = LIST_DEFAULT;
                    }
                }
            }

            if(getOsType().indexOf("MVS") >= 0)
            {
                LIST = "LIST";
            }

            //***
            fireDirectoryUpdate(this);
            fireConnectionInitialized(this);
        }
        else
        {
            fireConnectionFailed(this, new Integer(status).toString());
        }

        return status;
    }

    /**
    * Sort the filesizes an return an array.
    *
    * The Array should be in sync with the other sort*-Methods
    *
    * @return An String-array of sizes containing the size or -1 if failed or 0 for directories
    */
00375     public String[] sortSize()
    {
        try
        {
            currentSizes.removeAllElements();
            
            for(int i=0; i<currentListing.size(); i++)
            {
                String tmp = (String) currentListing.get(i);

                // ------------- VMS override -------------------
                if(getOsType().indexOf("VMS") >= 0)
                {
                    if(tmp.indexOf(";") < 0)
                    {
                        continue;
                    }

                    currentSizes.add("-1");

                    continue;
                }

                // ------------- MVS override -------------------
                if(getOsType().indexOf("MVS") >= 0)
                {
                    if(tmp.startsWith("Volume") || (tmp.indexOf(" ") < 0))
                    {
                         continue;
                    }

                    currentSizes.add("-1");

                    continue;
                }

                // ------------------------------------------------
                else if(getOsType().indexOf("WINDOW") >= 0)
                {
                    StringTokenizer to = new StringTokenizer(tmp, " ", false);

                    if(to.countTokens() >= 4)
                    {
                        to.nextToken();
                        to.nextToken();

                        String size = to.nextToken();

                        if(size.equals("<DIR>"))
                        {
                            currentSizes.add("0");
                        }
                        else
                        {
                            try
                            {
                                Long.parseLong(size);
                                currentSizes.add(size);
                            }
                            catch(Exception ex)
                            {
                              currentSizes.add("-1");
                                Log.out("WARNING: filesize can not be determined - value is " +
                                        size);
                            }
                        }
                    }
                }

                // ------------------------------------------------
                else if(getOsType().indexOf("OS/2") >= 0)
                {
                    StringTokenizer to = new StringTokenizer(tmp, " ", false);
                    tmp = giveSize(to, 0);
                    Log.out("OS/2 parser (size): " + tmp);
                    currentSizes.add(tmp);
                }
                else
                {
                    //Log.out("\n\nparser\n\n");
                    StringTokenizer to = new StringTokenizer(tmp, " ", false);

                    if(to.countTokens() >= 8) // unix
                    {
                        tmp = giveSize(to, 4);
                        currentSizes.add(tmp);

                        //Log.out(tmp);
                    }
                    else if(to.countTokens() == 7) // unix, too
                    {
                        Log.out("WARNING: 7 token backup size parser activated!");
                        tmp = giveSize(to, 4);
                        currentSizes.add(tmp);
                    }
                    else
                    {
                        Log.debug("cannot parse line: " + tmp);
                    }
                }
            }
        }
        catch(Exception ex)
        {
            Log.debug(ex.toString() + " @FtpConnection::sortSize#1");
            return new String[0];
        }

        return toArray(currentSizes);
    }

    private String[] toArray(Vector in) {
        String ret[] = new String[in.size()];
        
        for(int i=0; i<in.size(); i++) {
            ret[i] = (String) in.get(i);
        }
        
        return ret;
    }
    
    /**
    * Sort the permissions an return an array.
    *
    * The Array should be in sync with the other sort*-Methods
    *
    * @param file The file containing the raw server listing, usually Settings.ls_out
    * @return An int-array of sizes containing W, R or DENIED for each file
    */
00504     public int[] getPermissions()
    {
        try
        {
            currentPerms.removeAllElements();
            
            for(int i=0; i<currentListing.size(); i++)
            {
                String tmp = (String) currentListing.get(i);

                // ------------- VMS override -------------------
                if(getOsType().indexOf("VMS") >= 0)
                {
                    if(tmp.indexOf(";") < 0)
                    {
                        continue;
                    }

                    currentPerms.add("r");

                    continue;
                }

                // ------------- MVS override -------------------
                if(getOsType().indexOf("MVS") >= 0)
                {
                    if(tmp.startsWith("Volume"))
                    {
                        continue;
                    }

                    currentPerms.add("r");

                    continue;
                }

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

                StringTokenizer to = new StringTokenizer(tmp.trim(), " ", false);

                if(!(to.countTokens() > 3) ||
                       (tmp.startsWith("/") && (tmp.indexOf("denied") > 0)))
                {
                    continue;
                }

                tmp = to.nextToken();

                if(tmp.length() != 10)
                {
                    //System.out.println(tmp + " - e");
                    return null; // exotic bug, hardlinks are not found or something ("/bin/ls: dir: no such file or directy" in ls output) - we have no permissions then
                }

                char ur = tmp.charAt(1);
                char uw = tmp.charAt(2);
                char ar = tmp.charAt(7);
                char aw = tmp.charAt(8);

                to.nextToken();

                String user = to.nextToken();

                //System.out.println(""+ur+":"+ar+":"+tmp);
                if(aw == 'w')
                {
                  currentPerms.add("w");
                }
                else if(user.equals(username) && (uw == 'w'))
                {
                  currentPerms.add("w");
                }
                else if(ar == 'r')
                {
                  currentPerms.add("r");
                }
                else if(user.equals(username) && (ur == 'r'))
                {
                  currentPerms.add("r");
                }
                else
                {
                    //System.out.println(ur+":"+ar+":"+user+":"+username+":");
                  currentPerms.add("n");
                }
            }
        }
        catch(Exception ex)
        {
            Log.debug(ex.toString() + " @FtpConnection::getPermissions#1");
        }

        int[] ret = new int[currentPerms.size()];
        
        for(int i=0; i<currentPerms.size(); i++) {
            String fx = (String) currentPerms.get(i);
            
            if(fx.equals("w"))
            {
                  ret[i] = W;
            }
            else if(fx.equals("n"))
            {
                  ret[i] = DENIED;
            }
            else
            {
                  ret[i] = R;
            }
            //System.out.println(ret[i]);
        }
    
        return ret;
    }

    /**
    * Sort the filenames of the current working dir an return an array.
    *
    * The Array should be in sync with the other sort*-Methods
    *
    * @return An String-array of sizes containing the name of the file (symlinks are resolved)
    */
00626     public String[] sortLs()
    {
        try
        {
            boolean newUnixDateStyle = false;
            dateVector = new Vector();
            currentFiles.removeAllElements();
            
            for(int i=0; i<currentListing.size(); i++)
            {
                String tmp = (String) currentListing.get(i);

                // ------------------- VMS override --------------------
                if(getOsType().indexOf("VMS") >= 0)
                {
                    int x = tmp.indexOf(";");

                    if(x < 0)
                    {
                        continue;
                    }

                    tmp = tmp.substring(0, x);

                    if(tmp.endsWith("DIR"))
                    {
                        currentFiles.add(tmp.substring(0, tmp.lastIndexOf(".")) +
                                    "/");
                    }
                    else
                    {
                        currentFiles.add(tmp);
                    }

                    //Log.out("listing - (vms parser): " + tmp);

                    continue;
                }
                else if(getOsType().indexOf("OS/2") >= 0)
                {
                    StringTokenizer to2 = new StringTokenizer(tmp, " ", true);

                    if(giveFile(to2, 2).indexOf("DIR") >= 0)
                    {
                        to2 = new StringTokenizer(tmp, " ", true);
                        tmp = giveFile(to2, 5);
                        tmp = tmp + "/";
                    }
                    else
                    {
                        to2 = new StringTokenizer(tmp, " ", true);

                        if(giveFile(to2, 1).indexOf("DIR") >= 0)
                        {
                            //Log.out("OS/2 parser (DIRFIX): " + tmp);
                            to2 = new StringTokenizer(tmp, " ", true);
                            tmp = giveFile(to2, 4);
                            tmp = tmp + "/";
                        }
                        else
                        {
                            to2 = new StringTokenizer(tmp, " ", true);
                            tmp = giveFile(to2, 4);
                        }
                    }

                    Log.out("OS/2 parser: " + tmp);
                    currentFiles.add(tmp);

                    continue;
                }

                // -------------------------------------------------------
                // ------------------- MVS override --------------------
                if(getOsType().indexOf("MVS") >= 0)
                {
                    if(tmp.startsWith("Volume") || (tmp.indexOf(" ") < 0))
                    {
                        Log.out("->" + tmp);

                        continue;
                    }

                    String f = tmp.substring(tmp.lastIndexOf(" ")).trim();
                    String isDir = tmp.substring(tmp.lastIndexOf(" ") - 5,
                                                 tmp.lastIndexOf(" "));

                    if(isDir.indexOf("PO") >= 0)
                    {
                        currentFiles.add(f + "/");
                    }
                    else
                    {
                        currentFiles.add(f);
                    }

                    Log.out("listing - (mvs parser): " + tmp + " -> " + f);
                    continue;
                }
                else if(getOsType().indexOf("OS/2") >= 0)
                {
                    StringTokenizer to2 = new StringTokenizer(tmp, " ", true);

                    if(giveFile(to2, 2).indexOf("DIR") >= 0)
                    {
                        to2 = new StringTokenizer(tmp, " ", true);
                        tmp = giveFile(to2, 5);
                        tmp = tmp + "/";
                    }
                    else
                    {
                        to2 = new StringTokenizer(tmp, " ", true);

                        if(giveFile(to2, 1).indexOf("DIR") >= 0)
                        {
                            //Log.out("OS/2 parser (DIRFIX): " + tmp);
                            to2 = new StringTokenizer(tmp, " ", true);
                            tmp = giveFile(to2, 4);
                            tmp = tmp + "/";
                        }
                        else
                        {
                            to2 = new StringTokenizer(tmp, " ", true);
                            tmp = giveFile(to2, 4);
                        }
                    }

                    Log.out("OS/2 parser: " + tmp);
                    currentFiles.add(tmp);

                    continue;
                }

                // -------------------------------------------------------
                // ------------------- Unix, Windows and others -----
                boolean isDir = false;
                boolean isLink = false;

                if(tmp.startsWith("d") || (tmp.indexOf("<DIR>") >= 0))
                {
                    isDir = true;
                }

                if(tmp.startsWith("l"))
                {
                    isLink = true;
                }

                if(tmp.startsWith("/") && (tmp.indexOf("denied") > 0))
                {
                    continue;
                }

                StringTokenizer to = new StringTokenizer(tmp, " ", false);
                StringTokenizer to2 = new StringTokenizer(tmp, " ", true);

                int tokens = to.countTokens();

                //Log.out("listing: tokens: " + tokens + " - " + tmp);
                boolean set = false;

                //-----preparsing-----
                int lcount = 0;

                if(!newUnixDateStyle)
                {
                    try
                    {
                        StringTokenizer tx = new StringTokenizer(tmp, " ", false);
                        tx.nextToken();
                        tx.nextToken();
                        tx.nextToken();
                        tx.nextToken();
                        tx.nextToken();

                        String date = tx.nextToken();
                        int x = date.indexOf("-");
                        int y = date.lastIndexOf("-");

                        if(y > x)
                        {
                            Log.debug("Using new Unix date parser");
                            newUnixDateStyle = true;
                        }
                    }
                    catch(Exception ex)
                    {
                        Log.out("could not preparse line: " + tmp);
                        lcount++;

                        //ex.printStackTrace();
                    }
                }

                //--------------------
                //Log.out("tmp: " + tmp);
                if(newUnixDateStyle) // unix, too
                {
                    set = true;

                    //Log.out("listing - (unix parser #2, token 7): " + tmp);
                    //------- date parser testing ---------
                    try
                    {
                        //Log.out(">>> date parser: " + tmp);
                        StringTokenizer to3 = new StringTokenizer(tmp, " ", true);
                        String date = giveFile(to3, 5);
                        String date2 = date.substring(date.indexOf("-") + 1);
                        date2 = date2.substring(date.indexOf("-") + 2);

                        //Log.out("date1: "+date+"/"+"date2: "+date2);  
                        String t = date;
                        String y = t.substring(0, t.indexOf("-"));
                        String m = t.substring(t.indexOf("-") + 1,
                                               t.indexOf("-") + 3);
                        String m1 = t.substring(t.indexOf("-") + 1);
                        String day = m1.substring(m1.indexOf("-") + 1,
                                                  m1.indexOf("-") + 3);
                        String h = date2.substring(0, date2.indexOf(":"));
                        String min = date2.substring(date2.indexOf(":") + 1,
                                                     date2.indexOf(":") + 3);

                        //Log.out("day:"+day+"year:"+y+"mon:"+m+"hour:"+h+"m:"+min);
                        Date d = new Date();
                        d.setDate(Integer.parseInt(day));
                        d.setYear(Integer.parseInt(y));
                        d.setMonth(Integer.parseInt(m) - 1);
                        d.setHours(Integer.parseInt(h));
                        d.setMinutes(Integer.parseInt(min));

                        dateVector.add(d);

                        //Log.out("+++ date: \"" + d.toString() + "\"");
                    }
                    catch(Exception ex)
                    {
                        ex.printStackTrace();

                        //set = false;
                    }

                    // -----------------------------
                    tmp = giveFile(to2, 7);

                    if(lcount > 1)
                    {
                        dateVector = new Vector();
                    }
                }
                else if(tokens > 8) // unix
                {
                    set = true;
                    tmp = giveFile(to2, 8);

                    //Log.out("listing - (unix parser, token 8): " + tmp);
                }
                else if(tokens > 3) // windows
                {
                    set = true;
                    tmp = giveFile(to2, 3);

                    if(tmp.startsWith("<error>"))
                    {
                        tmp = giveFile(to2, 4);

                        //Log.out("listing - (windows parser, token 4): " + tmp);
                    }
                    else
                    {
                        //Log.out("listing - (windows parser, token 3): " + tmp);
                    }
                }

                if(tmp.startsWith("<error>") || !set)
                {
                    if(tokens < 3)
                    {
                        continue;
                    }

                    tmp = giveFile(to2, tokens);
                    Log.out("listing - WARNING: listing style unknown, using last token as filename!");
                    Log.out("listing - this may work but may also fail, filesizes and permissions will probably fail, too...");
                    Log.out("listing - please send us a feature request with the serveraddress to let us support this listing style :)");
                    Log.out("listing - (backup parser, token " + tokens +
                            " ): " + tmp);
                }

                if(isDir)
                {
                    tmp = tmp + "/";
                }
                else if(isLink)
                {
                    tmp = tmp + "###";
                }

                currentFiles.add(tmp);
                // -------------------------------------------------------------
            }

            String[] files = toArray(currentFiles);

            for(int i = 0; i < files.length; i++)
            {
                files[i] = parseSymlink(files[i]);

                //System.out.println("> " + files[i]+":"+dateVector.elementAt(i));
                //System.out.println("> " + files.length+":"+dateVector.size());
            }

            return files;
        }
        catch(Exception ex)
        {
            Log.debug(ex.toString() + " @FtpConnection::sortLs");
            ex.printStackTrace();
        }

        //------- date parser test ------

        /*
        String[] x = (String[]) dateVector.toArray();
        for(int i=0; i<x.length; i++)
        {
                Log.out(":"+x[i]);
        }
        */

        //-------------------------------
        return new String[0];
    }

    /** get a filename */
00960     private String giveFile(StringTokenizer to, int lev)
    {
        String t2 = null;

        for(int i = 0; i < lev; i++)
        {
            if(to.hasMoreTokens())
            {
                String t = to.nextToken();

                //Log.out("> "+t);
                if(t.equals(" ") || t.equals("\t"))
                {
                    // -

                    /*
                       while(to.hasMoreTokens())
                       {
                               t2 = to.nextToken();
                           if(!t.equals(" ") && !t.equals("\t")) break;
                       }
                    */
                    i--;
                }
            }
        }

        String tmp = "<error>";

        /*
        if(t2 != null)
        {
                tmp = t2;
        }
        */
        while(tmp.equals(" ") || tmp.equals("\t") || tmp.equals("<error>"))
        {
            if(to.hasMoreTokens())
            {
                tmp = to.nextToken();
            }
            else
            {
                break;
            }
        }

        while(to.hasMoreTokens())
        {
            tmp = tmp + to.nextToken();
        }

        //Log.out(">>> "+tmp);
        return tmp;
    }

    /** get a filesize */
01017     private String giveSize(StringTokenizer to, int lev)
    {
        for(int i = 0; i < lev; i++)
        {
            if(to.hasMoreTokens())
            {
                if(to.nextToken().equals(" "))
                {
                    i--;
                }
            }
        }

        while(to.hasMoreTokens())
        {
            String tmp = to.nextToken();

            if(!tmp.equals(" "))
            {
                try
                {
                    Integer.parseInt(tmp);
                }
                catch(NumberFormatException ex)
                {
                    return "-1";
                }

                return tmp;
            }
        }

        return "-2";
    }

    /**
    * Try to determine the os-type.
    * (Used internally to check how to parse the ls-output)
    *
    * @return A Part of the SYST comman server response
    */
01058     public String getOsType()
    {
        if(TESTMODE)
        {
            return "MVS";
        }
        else
        {
            return osType;
        }
    }

    private void setOsType(String os)
    {
        osType = os.toUpperCase();
    }

    private int getPasvPort(String str)
    {
        int start = str.lastIndexOf(",");
        String lo = "";
        start++;

        while(start < str.length())
        {
            char c = str.charAt(start);

            if(!Character.isDigit(c))
            {
                break;
            }

            lo = lo + c;
            start++;
        }

        System.out.println("\n\n\n"+str);
        
        String hi = "";
        start = str.lastIndexOf(",");
        start--;

        while(true)
        {
            char c = str.charAt(start);

            if(!Character.isDigit(c))
            {
                break;
            }

            hi = c + hi;
            start--;
        }

        //System.out.print(hi+":"+lo+" - ");
        return ((Integer.parseInt(hi) * 256) + Integer.parseInt(lo));
    }

    private int getActivePort()
    {
        return (getPortA() * 256) + getPortB();
    }

    /** parse a symlink */
01123     private String parseSymlink(String file)
    {
        if(file.indexOf("->") >= 0)
        {
            //System.out.print(file+" :-> symlink converted to:");
            file = file.substring(0, file.indexOf("->")).trim();
            file = file + "###";

            //System.out.println(file);
        }

        return file;
    }

    /** parse a symlink and cut the back */
01138     private String parseSymlinkBack(String file)
    {
        if(file.indexOf("->") >= 0)
        {
            //System.out.print(file+" :-> symlink converted to:");
            file = file.substring(file.indexOf("->") + 2).trim();

            //System.out.println(file);
        }

        return file;
    }

    /** test for symlink */
01152     private boolean isSymlink(String file)
    {
        if(file.indexOf("->") >= 0)
        {
            return true;
        }

        return false;
    }

    /** Download a file or directory.
    * Uses multithreading if enabled and does not block.
    *
    * @param file The file to download
    * @return An int-statuscode, see constants defined in this class for details
    */
01168     public int handleDownload(String file)
    {
        if(Settings.getEnableMultiThreading())
        {
            Log.out("spawning new thread for this download.");

            FtpTransfer t = new FtpTransfer(host, port, getLocalPath(),
                                            getCachedPWD(), file, username,
                                            password, Transfer.DOWNLOAD,
                                            handler, listeners, crlf);
            lastTransfer = t;

            return NEW_TRANSFER_SPAWNED;
        }
        else
        {
            Log.out("multithreading is completely disabled.");

            return download(file);
        }
    }

    /**
    * Download a file or directory, block until finished.
    *
    * @param file The file to download
    * @return An int returncode
    */
01196     public int download(String file)
    {
        //Log.out("ftp download started:" + this);

        int stat;

        if(file.endsWith("/"))
        {
            shortProgress = true;
            fileCount = 0;
            baseFile = file;
            dataType = DataConnection.GETDIR;

            stat = downloadDir(file);

            //pause(100);
            fireActionFinished(this);
            fireProgressUpdate(baseFile,
                               DataConnection.DFINISHED + ":" + fileCount, -1);
            shortProgress = false;
        }
        else
        {
            dataType = DataConnection.GET;

            stat = rawDownload(file);

          if(Settings.enableFtpDelays) {
             try
             {
                Thread.sleep(100);
             } catch(Exception ex) {}
          }

            fireActionFinished(this);
        }

      if(Settings.enableFtpDelays) {
         try
         {
            Thread.sleep(400);
         }
         catch(Exception ex)
         {
         }
      }

        return stat;
    }

    /**
    * Get download InputStream.
    *
    * @param file The file to download
    * @return An InputStream
    */
01252     public InputStream getDownloadInputStream(String file)
    {
        Log.out("ftp stream download started:" + this);
        file = parse(file);

        try
        {
            int p = 0;
            dataType = DataConnection.GET;
            file = StringUtils.getFile(file);

            String path = getLocalPath() + file;

            BufferedReader in = jcon.getReader();

            modeStream();
            p = negotiatePort();

            dcon = new DataConnection(this, p, host, path, dataType, false, true);

            while(!dcon.isThere())
            {
                pause(10);
            }

            jcon.send(RETR + " " + file);

            return dcon.getInputStream();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            Log.debug(ex.toString() +
                      " @FtpConnection::getDownloadInputStream");

            return null;
        }
    }

    private int rawDownload(String file)
    {
        file = parse(file);

        //String path = file;
        try
        {
            int p = 0;

            //if(isRelative(file)) path = getLocalPath() + file;
            file = StringUtils.getFile(file);

            String path = getLocalPath() + file;

            //System.out.println(file + " : " + path);
            //BufferedReader in = jcon.getReader();
            modeStream();

            //binary();
            p = negotiatePort();

            File f = new File(path);

            //System.out.println("path: "+path);
            boolean resume = false;

            if(f.exists() && Settings.enableResuming)
            {
                jcon.send(REST + " " + f.length());

                if(getLine(PROCEED) != null)
                {
                    resume = true;
                }
            }

            dcon = new DataConnection(this, p, host, path, dataType, resume); //, new Updater());

            while(!dcon.isThere())
            {
                pause(10);
            }

            jcon.send(RETR + " " + file);

            String line = getLine(POSITIVE);
            Log.debug(line);

            // file has been created even if not downloaded, delete it
            if(line.startsWith(NEGATIVE))
            {
                File f2 = new File(path);

                if(f2.exists() && (f2.length() == 0))
                {
                    f2.delete();
                }

                return PERMISSION_DENIED;
            }
            else if(!line.startsWith(POSITIVE) && !line.startsWith(PROCEED))
            {
                return TRANSFER_FAILED;
            }

            // we need to block since some ftp-servers do not want the
            // refresh command that dirpanel sends otherwise
            while(!dcon.finished)
            {
                pause(10);
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            Log.debug(ex.toString() + " @FtpConnection::download");

            return TRANSFER_FAILED;
        }

        return TRANSFER_SUCCESSFUL;
    }

    private int downloadDir(String dir)
    {
        if(!dir.endsWith("/"))
        {
            dir = dir + "/";
        }

        if(StringUtils.isRelative(dir))
        {
            dir = getCachedPWD() + dir;
        }

        String path = getLocalPath() + StringUtils.getDir(dir);

        //System.out.println("path: "+path);
        String pwd = getCachedPWD() + StringUtils.getDir(dir);

        String oldDir = getLocalPath();
        String oldPwd = getCachedPWD();

        File f = new File(path);

        if(!f.exists())
        {
            if(!f.mkdir())
            {
                Log.debug("Can't create directory: " + dir);
            }
            else
            {
                Log.debug("Created directory...");
            }
        }

        setLocalPath(path);

        if(!chdirNoRefresh(pwd))
        {
            return CHDIR_FAILED;
        }

        try
        {
            list();
        }
        catch(IOException ex)
        {
            return PERMISSION_DENIED;
        }

        String[] tmp = sortLs();

        setLocalPath(path);

        for(int i = 0; i < tmp.length; i++)
        {
            if(tmp[i].endsWith("/"))
            {
                if(tmp[i].trim().equals("../") || tmp[i].trim().equals("./"))
                {
                    Log.debug("Skipping " + tmp[i].trim());
                }
                else
                {
                    if(!work)
                    {
                        return TRANSFER_STOPPED;
                    }

                    if(downloadDir(tmp[i]) < 0)
                    {
                        ; // return TRANSFER_FAILED;
                    }
                }
            }
            else
            {
                //System.out.println( "file: " + getLocalPath() + tmp[i] + "\n\n");
                if(!work)
                {
                    return TRANSFER_STOPPED;
                }

                fileCount++;

                if(rawDownload(getLocalPath() + tmp[i]) < 0)
                {
                    ; // return TRANSFER_FAILED;
                }
            }
        }

        chdirNoRefresh(oldPwd);
        setLocalPath(oldDir);

        return TRANSFER_SUCCESSFUL;
    }

    /** Upload a file or directory.
    * Uses multithreading if enabled and does not block.
    *
    * @param file The file to upload
    * @return An int-statuscode, NEW_TRANSFER_SPAWNED,TRANSFER_FAILED or TRANSFER_SUCCESSFUL
    */
01478     public int handleUpload(String file)
    {
        return handleUpload(file, null);
    }

    /** Upload a file or directory.
    * Uses multithreading if enabled and does not block.
    *
    * @param file The file to upload
    * @param realName The file to rename the uploaded file to
    * @return An int-statuscode, NEW_TRANSFER_SPAWNED,TRANSFER_FAILED or TRANSFER_SUCCESSFUL
    */
01490     public int handleUpload(String file, String realName)
    {
        if(Settings.getEnableMultiThreading() &&
               (!Settings.getNoUploadMultiThreading()))
        {
            Log.out("spawning new thread for this upload.");

            FtpTransfer t;

            if(realName != null)
            {
                t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(),
                                    file, username, password, Transfer.UPLOAD,
                                    handler, listeners, realName, crlf);
            }
            else
            {
                t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(),
                                    file, username, password, Transfer.UPLOAD,
                                    handler, listeners, crlf);
            }

            lastTransfer = t;

            return NEW_TRANSFER_SPAWNED;
        }
        else
        {
            if(Settings.getNoUploadMultiThreading())
            {
                Log.out("upload multithreading is disabled.");
            }
            else
            {
                Log.out("multithreading is completely disabled.");
            }

            return (realName == null) ? upload(file) : upload(file, realName);
        }
    }

    /**
    * Upload a file or directory, block until finished.
    *
    * @param file The file to upload
    * @return An int returncode
    */
01537     public int upload(String file)
    {
        return upload(file, file);
    }

    /**
    * Upload and a file or directory under a given name, block until finished.
    * Note that setting realName does not affect directory transfers
    *
    * @param file The file to upload
    * @param realName The file to rename the uploaded file to
    * @return An int responsecode
    */
01550     public int upload(String file, String realName)
    {
        return upload(file, realName, null);
    }

    /**
    * Upload from an InputStream, block until finished.
    *
    * @param file The file to upload
    * @param in InputStream to read from
    * @return An int responsecode
    */
01562     public int upload(String file, InputStream in)
    {
        return upload(file, file, in);
    }

    /**
    * Upload and a file or directory under a given name, block until finished.
    * Note that setting realName does not affect directory transfers
    *
    * @param file The file to upload
    * @param realName The file to rename the uploaded file to
    * @param in InputStream to read from
    * @return An int responsecode
    */
01576     public int upload(String file, String realName, InputStream in)
    {
        hasUploaded = true;
        Log.out("ftp upload started: " + this);

        int stat;

        if((in == null) && new File(file).isDirectory())
        {
            shortProgress = true;
            fileCount = 0;
            baseFile = file;
            dataType = DataConnection.PUTDIR;
            isDirUpload = true;

            stat = uploadDir(file);

            shortProgress = false;

            //System.out.println(fileCount + ":" + baseFile);
            fireProgressUpdate(baseFile,
                               DataConnection.DFINISHED + ":" + fileCount, -1);

            fireActionFinished(this);
            fireDirectoryUpdate(this);
        }
        else
        {
            dataType = DataConnection.PUT;
            stat = rawUpload(file, realName, in);

            try
            {
                Thread.sleep(100);
            }
            catch(Exception ex)
            {
            }

            fireActionFinished(this);
            fireDirectoryUpdate(this);
        }

        try
        {
            Thread.sleep(500);
        }
        catch(Exception ex)
        {
        }

        return stat;
    }

    private int rawUpload(String file)
    {
        return rawUpload(file, file);
    }

    private int rawUpload(String file, String realName)
    {
        return rawUpload(file, realName, null);
    }

    /** uploads a file */
01641     private int rawUpload(String file, String realName, InputStream in)
    {
        if(!file.equals(realName))
        {
            Log.debug("File: " + file + ", stored as " + realName);
        }
        else
        {
            Log.debug("File: " + file);
            realName = null;
        }

        file = parse(file);

        String path = file;

        try
        {
            int p = 0;

            if(StringUtils.isRelative(file))
            {
                path = getLocalPath() + file;
            }

            file = StringUtils.getFile(file);

            //BufferedReader in = jcon.getReader();
            boolean resume = false;
            String size = "0";

            if(Settings.enableUploadResuming && (in == null))
            {
                list();

                String[] ls = sortLs();
                String[] sizes = sortSize();

                if((ls == null) || (sizes == null))
                {
                    Log.out(">>> ls out of sync (skipping resume check)");
                }
                else
                {
                    for(int i = 0; i < ls.length; i++)
                    {
                        //Log.out(ls[i] + ":" + sizes[i]);
                        if(realName != null)
                        {
                            if(ls[i].equals(realName))
                            {
                                resume = true;
                                size = sizes[i];

                                break;
                            }
                        }
                        else
                        {
                            if(ls[i].equals(file))
                            {
                                resume = true;
                                size = sizes[i];
                            }
                        }
                    }

                    File f = new File(path);

                    if(f.exists() && (f.length() <= Integer.parseInt(size)))
                    {
                        Log.out("skipping resuming, file is <= filelength");
                        resume = false;
                    }
                    else if(f.exists() && Integer.parseInt(size) > 0)
                    {
                        if(!Settings.noUploadResumingQuestion) {
                              if(JOptionPane.showConfirmDialog(new JLabel(), "A file smaller than the one to be uploaded already exists on the server,\n do you want to resume the upload?", "Resume upload?", JOptionPane.YES_NO_OPTION)
                                          != JOptionPane.OK_OPTION) {
                                    resume = false;
                              }
                        }
                        Log.out("resume: " + resume + ", size: " + size);
                    }
                    else {
                        Log.out("resume: " + resume + ", size: " + size);
                    }
                }
            }

            modeStream();

            //binary();
            p = negotiatePort();

            if(resume && Settings.enableUploadResuming)
            {
                jcon.send(REST + " " + size);

                if(getLine(PROCEED) == null)
                {
                    resume = false;
                }
            }

            dcon = new DataConnection(this, p, host, path, dataType, resume,
                                      Integer.parseInt(size), in); //, new Updater());

            while(!dcon.isThere())
            {
                pause(10);
            }

            //System.out.println(path + " : " + file);
            if(realName != null)
            {
                jcon.send(STOR + " " + realName);
            }
            else
            {
                jcon.send(STOR + " " + file);
            }

            String tmp = getLine(POSITIVE);

            if(!tmp.startsWith(POSITIVE) && !tmp.startsWith(PROCEED))
            {
                return TRANSFER_FAILED;
            }

            Log.debug(tmp);

            // we need to block since some ftp-servers do not want the
            // refresh command that dirpanel sends otherwise
            while(!dcon.finished)
            {
                pause(10);
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            Log.debug(ex.toString() + " @FtpConnection::upload");

            return TRANSFER_FAILED;
        }

        return TRANSFER_SUCCESSFUL;
    }

    private int uploadDir(String dir)
    {
        //System.out.println("up");
        if(dir.endsWith("\\"))
        {
            System.out.println("Something's wrong with the selected directory - please report this bug!");
        }

        if(!dir.endsWith("/"))
        {
            dir = dir + "/";
        }

        if(StringUtils.isRelative(dir))
        {
            dir = getLocalPath() + dir;
        }

        String single = StringUtils.getDir(dir);
        String path = dir.substring(0, dir.indexOf(single));

        String remoteDir = getCachedPWD() + single; //StringUtils.removeStart(dir,path);
        String oldDir = getCachedPWD();

        if(Settings.safeMode)
        {
            noop();
        }

        boolean successful = mkdir(remoteDir);

        if(!successful)
        {
            return MKDIR_FAILED;
        }

        if(Settings.safeMode)
        {
            noop();
        }

        chdirNoRefresh(remoteDir);

        File f2 = new File(dir);
        String[] tmp = f2.list();

        for(int i = 0; i < tmp.length; i++)
        {
            String res = dir + tmp[i];
            File f3 = new File(res);

            if(f3.isDirectory())
            {
                if(!work)
                {
                    return TRANSFER_STOPPED;
                }

                uploadDir(res);
            }
            else
            {
                //System.out.println(">>>\nlp: "+path+single + "\nrp: ");
                //System.out.println(remoteDir +"\nres: "+res);
                if(!work)
                {
                    return TRANSFER_STOPPED;
                }

                fileCount++;

                if(rawUpload(res) < 0)
                {
                    ; //return TRANSFER_STOPPED;
                }
            }
        }

        chdirNoRefresh(oldDir);

        return TRANSFER_SUCCESSFUL;
    }

    private String parse(String file)
    {
        file = parseSymlink(file);

        return file;
    }

    /** recursive delete remote directory */
01882     private int cleanDir(String dir, String path)
    {
        if(dir.equals(""))
        {
            return 0;
        }

        if(!dir.endsWith("/"))
        {
            dir = dir + "/";
        }

        String remoteDir = StringUtils.removeStart(dir, path);

        String oldDir = pwd;
        chdirNoRefresh(pwd + remoteDir);

        try
        {
            list();
        }
        catch(IOException ex)
        {
            // probably we don't have permission to ls here
            return PERMISSION_DENIED;
        }

        String[] tmp = sortLs();
        chdirNoRefresh(oldDir);

        if(tmp == null)
        {
            return GENERIC_FAILED;
        }

        for(int i = 0; i < tmp.length; i++)
        {
            Log.out("cleanDir: " + tmp);

            if(isSymlink(tmp[i]))
            {
                Log.debug("WARNING: Skipping symlink, remove failed.");
                Log.debug("This is necessary to prevent possible data loss when removing those symlinks.");

                tmp[i] = null;
            }

            if(tmp[i] != null)
            {
                tmp[i] = parseSymlink(tmp[i]);
            }

            if((tmp[i] == null) || tmp[i].equals("./") || tmp[i].equals("../"))
            {
                //System.out.println(tmp[i]+"\n\n\n");
                continue;
            }

            //System.out.println(tmp[i]);
            //pause(500);
            if(tmp[i].endsWith("/"))
            {
                //   System.out.println(dir);
                cleanDir(dir + tmp[i], path);

                int x = removeFileOrDir(dir + tmp[i]);

                if(x < 0)
                {
                    return x;
                }
            }
            else
            {
                //   System.out.println(dir+tmp[i]);
                int x = removeFileOrDir(dir + tmp[i]);

                if(x < 0)
                {
                    return x;
                }
            }
        }

        return REMOVE_SUCCESSFUL;
    }

    /**
    * Remove a remote file or directory.
    *
    * @param file The file to remove
    * @return REMOVE_SUCCESSFUL, REMOVE_FAILED, PERMISSION_DENIED or  GENERIC_FAILED
    */
01975     public int removeFileOrDir(String file)
    {
        if(file == null)
        {
            return 0;
        }

        if(file.trim().equals(".") || file.trim().equals(".."))
        {
            Log.debug("ERROR: Catching attempt to delete . or .. directories");

            return GENERIC_FAILED;
        }

        if(isSymlink(file))
        {
            Log.debug("WARNING: Skipping symlink, remove failed.");
            Log.debug("This is necessary to prevent possible data loss when removing those symlinks.");

            return REMOVE_FAILED;
        }

        file = parseSymlink(file);

        if(file.endsWith("/"))
        {
            int ret = cleanDir(file, getCachedPWD());

            if(ret < 0)
            {
                return ret;
            }

            jcon.send(RMD + " " + file);
        }
        else
        {
            jcon.send(DELE + " " + file);
        }

        if(success(POSITIVE))//FTP250_COMPLETED))
        {
            return REMOVE_SUCCESSFUL;
        }
        else
        {
            return REMOVE_FAILED;
        }
    }

    /**
    * Disconnect from the server.
    * The connection is marked ad closed even if the server does not respond correctly -
    * if it fails for any reason the connection should just time out
    *
    */
02031     public void disconnect()
    {
        jcon.send(QUIT);
        getLine(POSITIVE);//FTP221_SERVICE_CLOSING);
        connected = false;
    }

    /**
    * Execute a remote command.
    * Sends noops before and after the command
    *
    *  @param cmd The raw command that is to be sent to the server
    */
02044     public void sendRawCommand(String cmd)
    {
        noop();
        Log.clearCache();
        jcon.send(cmd);
        noop();
    }

    /**
    * Reads the response until line found or error.
    * (Used internally)
    *
    * @param until The String the response line hast to start with, 2 for succesful return codes for example
    */
02058     public String getLine(String until)
    {
        return getLine(new String[] { until });
    }

    /**
    * Reads the response until line found or error.
    * (Used internally)
    */
02067     public String getLine(String[] until)
    {
        BufferedReader in = null;

        try
        {
            in = jcon.getReader();
        }
        catch(Exception ex)
        {
            Log.debug(ex.toString() + " @FtpConnection::getLine");
        }

        //String resultString = null;
        String tmp;

        while(true)
        {
            try
            {
                tmp = in.readLine();

                if(tmp == null)
                {
                    break;
                }

                Log.debug(tmp);

                if(((tmp.startsWith(NEGATIVE) || (tmp.startsWith(NEGATIVE2))) &&
                       (tmp.charAt(3) != MORE_LINES_APPENDED)) ||
                       tmp.startsWith(PROCEED))
                {
                    return tmp;
                }
                else
                {
                    for(int i = 0; i < until.length; i++)
                    {
                        if(tmp.startsWith(until[i]))
                        {
                            //if(resultString == null) resultString = tmp;
                            //else resultString = resultString + "\n" + tmp;
                            if(tmp.charAt(3) != MORE_LINES_APPENDED)
                            {
                                return tmp;
                            }
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                Log.debug(ex.toString() + " @FtpConnection::getLine");

                break;
            }
        }

        return null;
    }

    /**
    * Check if login() was successful.
    *
    * @return True if connected, false otherwise
    */
02134     public boolean isConnected()
    {
        return connected;
    }

    /**
    * Get the current remote directory.
    *
    * @return The server's CWD
    */
02144     public String getPWD()
    {
        if(connected)
        {
            updatePWD();
        }

        if(TESTMODE)
        {
            return "HUGO.user.";
        }

        return pwd;
    }

    /**
    * Returns current remote directory (cached)
    *
    * @return The cached CWD
    */
02164     public String getCachedPWD()
    {
        return pwd;
    }

    /**
    * updates PWD
    */
02172     private void updatePWD()
    {
        jcon.send(PWD);

        String tmp = getLine(POSITIVE);//FTP257_PATH_CREATED);

        if(tmp == null)
        {
            return;
        }

        if(TESTMODE)
        {
            tmp = "\"'T759F.'\"";
        }
              
        String x1 = tmp;
        
        if(x1.indexOf("\"") >= 0) {
            x1 = tmp.substring(tmp.indexOf("\"") + 1);
            x1 = x1.substring(0, x1.indexOf("\""));
        }

        if(getOsType().indexOf("MVS") >= 0)
        {
            //if(x1.indexOf("'") == 0) {
            //        String x2 = x1.substring(1, x1.lastIndexOf("'"));
            //        x1 = x2;
            //}
            Log.out("pwd: " + x1);
        }
        else if(!x1.endsWith("/"))
        {
            x1 = x1 + "/";
        }

        pwd = x1;
    }

    /**
    * Change directory unparsed.
    * Use chdir instead if you do not parse the dir correctly...
    *
    * @param dirName The raw directory name
    * @return True if successful, false otherwise
    */
02218     public boolean chdirRaw(String dirName)
    {
        jcon.send(CWD + " " + dirName);

        return success(POSITIVE);//FTP250_COMPLETED);
    }

    private boolean success(String op)
    {
        String tmp = getLine(op);

        if((tmp != null) && tmp.startsWith(op))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Change to the parent of the current working directory.
     * The CDUP command is a special case of CWD, and is included
     * to simplify the implementation of programs for transferring
     * directory trees between operating systems having different
     * syntaxes for naming the parent directory.
     *
     * @return True if successful, false otherwise
     */
02248     public boolean cdup()
    {
        jcon.send(CDUP);

        return success(POSITIVE);//FTP200_OK);
    }

    /**
    * Create a directory with the given name.
    *
    * @param dirName The name of the directory to create
    * @return True if successful, false otherwise
    */
02261     public boolean mkdir(String dirName)
    {
        jcon.send(MKD + " " + dirName);

        boolean ret = success(POSITIVE); // Filezille server bugfix, was: FTP257_PATH_CREATED);
        Log.out("mkdir(" + dirName + ")  returned: " + ret);

        //*** Added 03/20/2005
        fireDirectoryUpdate(this);

        //***     
        return ret;
    }

    private int negotiatePort() throws IOException
    {
        String tmp = "";

        if(Settings.getFtpPasvMode())
        {
            jcon.send(PASV);

            tmp = getLine(FTP227_ENTERING_PASSIVE_MODE);

            if((tmp != null) && !tmp.startsWith(NEGATIVE))
            {
                return getPasvPort(tmp);
            }
        }

        tmp = getActivePortCmd();
        jcon.send(tmp);
        getLine(FTP200_OK);

        return getActivePort();
    }

    /**
    * List remote directory.
    * Note that you have to get the output using the
    * sort*-methods.
    *
    * @param outfile The file to save the output to, usually Settings.ls_out
    */
02305     public void list() throws IOException
    {
        String oldType = ""; 

        try
        {
            BufferedReader in = jcon.getReader();

            int p = 0;

            modeStream();

            oldType = getTypeNow();
            ascii();

            p = negotiatePort();
            dcon = new DataConnection(this, p, host, null, DataConnection.GET, false, true); //,null);

            while(dcon.getInputStream() == null)
            {
                  //System.out.print("#");
                pause(10);
            }
            DataInputStream input = new DataInputStream(dcon.getInputStream());
            
            jcon.send(LIST);

            
            String line;
            currentListing.removeAllElements();
            
            while((line = input.readLine()) != null) {
                  System.out.println("-> "+line);
                  if(!line.trim().equals("")) { 
                        currentListing.add(line);
                  }
            }
            
            getLine(POSITIVE); //FTP226_CLOSING_DATA_REQUEST_SUCCESSFUL);
            input.close();

            if(!oldType.equals(ASCII))
            {
                type(oldType);
            }
        }
        catch(Exception ex)
        {
            Log.debug("Cannot list remote directory!");

            if(!oldType.equals(ASCII))
            {
                type(oldType);
            }

            ex.printStackTrace();
            throw new IOException(ex.getMessage());
        }
    }

    /**
    * Parses directory and does a chdir().
    * Does also fire a directory update event.
    *
    * @return True is successful, false otherwise
    */
02371     public boolean chdir(String p)
    {
      //Log.out("Change directory to: "+p);
      String tmpPath = p;

        boolean tmp = chdirWork(p);

        if(!tmp)
        {
            return false;
        }
        else
        {
            fireDirectoryUpdate(this);

            return true;
        }
    }

    /**
    * Parses directory and does a chdir(), but does not send an update signal
    *
    * @return True is successful, false otherwise
    */
02395     public boolean chdirNoRefresh(String p)
    {
        return chdirWork(p);
    }

    private boolean chdirWork(String p)
    {
        if(Settings.safeMode)
        {
            noop();
        }

        p = parseSymlinkBack(p);

        if(getOsType().indexOf("OS/2") >= 0)
        {
            return chdirRaw(p);
        }
        else if(getOsType().indexOf("MVS") >= 0)
        {
          boolean cdup = false;

          System.out.print("MVS parser: "+p+" -> ");  

          //TODO: handle relative unix paths
          if(!getPWD().startsWith("/")) //return chdirRaw(p);
          { 
           if(p.endsWith("..")) {
            cdup = true;
           }
           //TODO let dirlister form correct syntax

             p = p.replaceAll("/", "");
           boolean ew = p.startsWith("'");
           String tmp = null;

           if(p.startsWith("'") && !p.endsWith("'")) 
           {
            if(cdup){
                  p = p.substring(0,p.length()-4);
                  if(p.indexOf(".") > 0) {
                        p = p.substring(0,p.lastIndexOf("."));    
                  }
                  p += "'";
            }

            tmp = p.substring(0, p.lastIndexOf("'"));
            p = p.substring(p.lastIndexOf("'")+1);
            ew = false;
           }          
           else if(cdup){
            p = p.substring(0,p.length()-3);
            if(p.indexOf(".") > 0) {
                  p = p.substring(0,p.lastIndexOf("."));    
            }
           }

             if(p.indexOf(".") > 0 && !ew)
             {
                  p = p.substring(0, p.indexOf("."));
             }

           if(tmp != null) p = tmp+p+"'";

             System.out.println(p);
             Log.out("MVS parser: " + p);

             return chdirRaw(p);
            }
        }

        try
        {
            // check if we don't have a relative path
            if(!p.startsWith("/") && !p.startsWith("~"))
            {
                p = pwd + p;
            }

            // argh, we should document that!
            if(p.endsWith(".."))
            {
                boolean home = p.startsWith("~");
                StringTokenizer stok = new StringTokenizer(p, "/");

                //System.out.println("path p :"+p +" Tokens: "+stok.countTokens());
                if(stok.countTokens() > 2)
                {
                    String pold1 = "";
                    String pold2 = "";
                    String pnew = "";

                    while(stok.hasMoreTokens())
                    {
                        pold1 = pold2;
                        pold2 = pnew;
                        pnew = pnew + "/" + stok.nextToken();
                    }

                    p = pold1;
                }
                else
                {
                    p = "/";
                }

                if(home)
                {
                    p = p.substring(1);
                }
            }

            //System.out.println("path: "+p);
            if(!chdirRaw(p))
            {
                return false;
            }
        }
        catch(Exception ex)
        {
            Log.debug("(remote) Can not get pathname! " + pwd + ":" + p);
            ex.printStackTrace();

            return false;
        }

        //fireDirectoryUpdate(this);
        
      // --------------------------------------
      updatePWD();


        return true;
    }

    /**
    * Waits a specified amount of time
    *
    * @param time The time to sleep in milliseconds
    */
02535     public void pause(int time)
    {
        try
        {
            Thread.sleep(time);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    /**
    * Return the local working directory
    *
    * @return The local CWD
    */
02552     public String getLocalPath()
    {
        return localPath;
    }

    /**
    * Set the local working directory
    *
    * @param newPath The new local CWD
    * @return Always true in the current implementation
    */
02563     public boolean setLocalPath(String newPath)
    {
        localPath = newPath;

        if(!localPath.endsWith("/"))
        {
            localPath = localPath + "/";
        }

        return true;
    }

    /**
    * Checks wheter a file exists.
    *
    * @param file the name of the file
    * @return An int-code: CHDIR_FAILED, PERMISSION_DENIED (no listing allowed), R, W or DENIED
    */
02581     public int exists(String file)
    {
        String dir = null;
        String tmpPWD = getCachedPWD();

        if(file.indexOf("/") >= 0)
        {
            dir = file.substring(0, file.lastIndexOf("/") + 1);
            Log.out("checking dir: " + dir);

            if(!chdir(dir))
            {
                return CHDIR_FAILED;
            }
        }

        try
        {
            list();
        }
        catch(IOException ex)
        {
            ex.printStackTrace();

            return PERMISSION_DENIED;
        }

        String f = file.substring(file.lastIndexOf("/") + 1);
        Log.out("checking file: " + f);

        String[] files = sortLs();
        int[] perms = getPermissions();

        int y = -1;

        for(int x = 0; x < files.length; x++)
        {
            if(files[x].equals(f))
            {
                y = x;

                break;
            }
        }

        if(y == -1)
        {
            return FILE_NOT_FOUND;
        }

        if(dir != null)
        {
            chdir(tmpPWD);
        }

        return perms[y];
    }

    /** Moves or renames remote file.
     * @param from remote file to move or rename.
     * @param to new file name.
     * @return <code>true</code> if completed successfully; <code>false</code> otherwise.
     */
02644     public boolean rename(String from, String to)
    {
        jcon.send("RNFR " + from);

        if(success(RC350))
        {
            jcon.send("RNTO " + to);

            if(success(POSITIVE))//FTP250_COMPLETED))
            {
                return true;
            }
        }

        return false;
    }

    /**
    * Get the port.
    *
    * @return The port used by the FTP control connection
    */
02666     public int getPort()
    {
        return port;
    }

    private int getPortA()
    {
        return porta;
    }

    private int getPortB()
    {
        return portb;
    }

    private void incrementPort()
    {
        portb++;
    }

    /*
    private String getActivePortCmd() throws UnknownHostException, IOException
    {
        InetAddress ipaddr = jcon.getLocalAddress();
        String ip = ipaddr.getHostAddress().replace('.', ',');

        incrementPort();

        int a = getPortA();
        int b = getPortB();
        String ret = "PORT " + ip + "," + a + "," + b;

        //System.out.println(ret);
        return ret;
    }
*/
    
    private String getActivePortCmd() throws UnknownHostException, IOException 
    { 
    InetAddress ipaddr = jcon.getLocalAddress(); 
    String ip = ipaddr.getHostAddress().replace('.', ','); 
     
    ServerSocket aSock = new ServerSocket(0); 
    int availPort = aSock.getLocalPort(); 
    aSock.close(); 
    porta = availPort/256; 
    portb = availPort%256; 
    String ret = "PORT " + ip + "," + porta + "," + portb; 
     
//    System.out.println(ret); 
    return ret; 
    } 
    
    /**
     * Tell server we want binary data connections.
     */
02722     public void binary()
    { // possible responses 200, 500, 501, 504, 421 and 530
        type(BINARY);
    }

    /**
     * Tell server we want ascii data connections.
     */
02730     public void ascii()
    {
        type(ASCII);
    }

    /**
    * Set type (L8,I,A for example).
    *
    * @return True if the type was changed successfully, false otherwise
    */
02740     public boolean type(String code)
    {
        jcon.send(TYPE + " " + code);

        String tmp = getLine(POSITIVE);//FTP200_OK);

        if((tmp != null) && tmp.startsWith(POSITIVE))//FTP200_OK))
        {
            typeNow = code;

            return true;
        }

        return false;
    }

    /**
    * Returns the type used.
    *
    * @return The type String, I, A, L8 for example
    */
02761     public String getTypeNow()
    {
        return typeNow;
    }

    /**
     * Do nothing, but flush buffers
     */
02769     public void noop()
    {
        jcon.send(NOOP);
        getLine(POSITIVE);//FTP200_OK);
    }

    /**
     * Try to abort the transfer.
     */
02778     public void abort()
    {
        jcon.send(ABOR);
        getLine(POSITIVE); // 226
    }

    /**
     * This command is used to find out the type of operating
     * system at the server. The reply shall have as its first
     * word one of the system names listed in the current version
     * of the Assigned Numbers document (RFC 943).
     *
     * @return The server response of the SYST command
     */
02792     public String system()
    { // possible responses 215, 500, 501, 502, and 421
        jcon.send(SYST);

        String response = getLine(POSITIVE);//FTP215_SYSTEM_TYPE);

        if(response != null)
        {
            //StringTokenizer st = new StringTokenizer(response);
            //if (st.countTokens() >= 2)
            //{
            //     st.nextToken();
            //     String os = st.nextToken();
            //     setOsType(os);
            //}
            setOsType(response);
        }
        else
        {
            setOsType("UNIX");
        }

        return response;
    }

    /**
     * Set mode to Stream.
     * Used internally.
     */
02821     public void modeStream()
    {
        if(useStream && !modeStreamSet)
        {
            String ret = mode(STREAM);

            if((ret != null) && ret.startsWith(NEGATIVE))
            {
                useStream = false;
            }
            else
            {
                modeStreamSet = true;
            }
        }
    }

    /**
     * Unsupported at this time.
     */
02841     public void modeBlocked()
    {
        if(useBlocked)
        {
            if(mode(BLOCKED).startsWith(NEGATIVE))
            {
                useBlocked = false;
            }
        }
    }

    /*
     * Unsupported at this time.
     */
    public void modeCompressed()
    {
        if(useCompressed)
        {
            if(mode(COMPRESSED).startsWith(NEGATIVE))
            {
                useCompressed = false;
            }
        }
    }

    /**
    * Set mode manually.
    *
    * @param code The mode code wanted, I, A, L8 for example
    * @return The server's response to the request
    */
02872     public String mode(String code)
    {
        jcon.send(MODE + " " + code);

        String ret = "";

        try
        {
            ret = getLine(POSITIVE);//FTP200_OK);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }

        return ret;
    }

    /**
    * Return the host used.
    *
    * @return The remote host of this connection
    */
02895     public String getHost()
    {
        return host;
    }

    /**
    * Return the username.
    *
    * @return The username used by this connection
    */
02905     public String getUsername()
    {
        return username;
    }

    /**
    * Return the password.
    *
    * @return The password used by this connection
    */
02915     public String getPassword()
    {
        return password;
    }

    /**
    * Return the DataConnection.
    * Should be necessary only for complex actions.
    *
    * @return The DataConnection object currently used by this conenction
    */
02926     public DataConnection getDataConnection()
    {
        return dcon;
    }

    /**
    * Add a ConnectionListener.
    * The Listener is notified about transfers and the connection state.
    * @param l A ConnectionListener object
    */
02936     public void addConnectionListener(ConnectionListener l)
    {
        listeners.add(l);
    }

    /**
    * Add a Vector of ConnectionListeners.
    * Each Listener is notified about transfers and the connection state.
    *
    * @param l A Vector containig ConnectionListener objects
    */
02947     public void setConnectionListeners(Vector l)
    {
        listeners = l;
    }

    /**
    * Remote directory has changed.
    *
    * @param con The FtpConnection calling the event
    */
02957     public void fireDirectoryUpdate(FtpConnection con)
    {
        if(listeners == null)
        {
            return;
        }
        else
        {
            for(int i = 0; i < listeners.size(); i++)
            {
                ((ConnectionListener) listeners.elementAt(i)).updateRemoteDirectory(con);
            }
        }
    }

    /**
    * Progress update.
    * The transfer is active and makes progress, so a signal is send
    * each few kb.
    *
    * @param file The file transferred
    * @param type the type of event, DataConnection.GETDIR for example
    * @param bytes The number of bytes transferred so far
    */
02981     public void fireProgressUpdate(String file, String type, long bytes)
    {
        //System.out.println(listener);
        if(listeners == null)
        {
            return;
        }
        else
        {
            for(int i = 0; i < listeners.size(); i++)
            {
                ConnectionListener listener = (ConnectionListener) listeners.elementAt(i);

                if(shortProgress && Settings.shortProgress)
                {
                    if(type.startsWith(DataConnection.DFINISHED))
                    {
                        listener.updateProgress(baseFile,
                                                DataConnection.DFINISHED + ":" +
                                                fileCount, bytes);
                    }
                    else if(isDirUpload)
                    {
                        listener.updateProgress(baseFile,
                                                DataConnection.PUTDIR + ":" +
                                                fileCount, bytes);
                    }
                    else
                    {
                        listener.updateProgress(baseFile,
                                                DataConnection.GETDIR + ":" +
                                                fileCount, bytes);
                    }
                }
                else
                {
                    listener.updateProgress(file, type, bytes);
                }
            }
        }
    }

    /**
    * Connection is there and user logged in
    *
    * @param con The FtpConnection calling the event
    */
03028     public void fireConnectionInitialized(FtpConnection con)
    {
        if(listeners == null)
        {
            return;
        }
        else
        {
            for(int i = 0; i < listeners.size(); i++)
            {
                ((ConnectionListener) listeners.elementAt(i)).connectionInitialized(con);
            }
        }
    }

    /**
    * We are not logged in for some reason
    *
    * @param con The FtpConnection calling the event
    * @param why The login() response code
    */
03049     public void fireConnectionFailed(FtpConnection con, String why)
    {
        if(listeners == null)
        {
            return;
        }
        else
        {
            for(int i = 0; i < listeners.size(); i++)
            {
                ((ConnectionListener) listeners.elementAt(i)).connectionFailed(con,
                                                                               why);
            }
        }
    }

    /**
    * Transfer is done
    *
    * @param con The FtpConnection calling the event
    */
03070     public void fireActionFinished(FtpConnection con)
    {
        if(listeners == null)
        {
            return;
        }
        else
        {
            for(int i = 0; i < listeners.size(); i++)
            {
                ((ConnectionListener) listeners.elementAt(i)).actionFinished(con);
            }
        }
    }

    /**
    * Return the ConnectionHandler.
    *
    * @return The connection handler of this instance
    */
03090     public ConnectionHandler getConnectionHandler()
    {
        return handler;
    }

    /**
    * Set the ConnectionHandler.
    *
    * @param h The connection handler that is to be used by this connection
    */
03100     public void setConnectionHandler(ConnectionHandler h)
    {
        handler = h;
    }

    /**
    * Get the last FtpTransfer object spawned.
    *
    */
03109     public FtpTransfer getLastInitiatedTransfer()
    {
        return lastTransfer;
    }

    /**
    * Abort the last spawned transfer.
    *
    */
03118     public void abortTransfer()
    {
        try
        {
            DataConnection dcon = lastTransfer.getDataConnection();

            if(dcon == null)
            {
                Log.out("nothing to abort");

                return;
            }

            dcon.getCon().work = false;
            dcon.sock.close();
        }
        catch(Exception ex)
        {
            Log.debug("Exception during abort: " + ex.toString());
            ex.printStackTrace();
        }
    }

    public Date[] sortDates()
    {
        if(dateVector.size() > 0)
        {
            return (Date[]) dateVector.toArray();
        }
        else
        {
            return null;
        }
    }

    public String getCRLF() {
      return crlf;
    }

      public BufferedReader getIn() {
            return in;
      }

      public void setIn(BufferedReader in) {
            this.in = in;
      }

      public BufferedReader getCommandInputReader() {
            return jcon.getIn();
      }
      
      public OutputStream getCommandOutputStream() {
            return jcon.getOut();
      }
      
}

Generated by  Doxygen 1.6.0   Back to index