/**  This file contains the implementation of a FTP Client used
  *  by the FTP Explorer
  *
  *  constructors:
  *           public FtpClient();
  *           public FtpClient(FTPExplorer fp);
  *
  *  The method index:
  *           makeConnection(site);
  *           public void getFile(String fileName,String directory)  
  *           public void getDirectory(String localCurrentDir,
  *                                               String targetDir)
  *
  */
import java.io.*;
import java.net.*;
import java.math.*;
import java.util.StringTokenizer;
import Gui;

public class FtpClient 
{
    Socket cSock1;  // for control Connection
    Socket cSock2;  // for Data Connection

    DataOutputStream outbound;   // control port outbound
    DataInputStream inbound;     // control port inbound    

    DataInputStream inbound1;    // data port inbound

    String site;
    public FTPExplorer fp;

    public FtpClient ()
    {
        System.out.println("Object of class FtpClient");
    }

    public FtpClient (FTPExplorer fp)
    {
        this.fp = fp;
        System.out.println("Object of class FtpClient");
    }

    public void makeConnection(String site)
    {
        System.out.println("inside the Connection");
        String responseLine;
        try 
        {
            this.site = site;
            fp.reportProgress("site : " + site);
            Socket cSock1 = new Socket(site,21);

            outbound = new DataOutputStream( cSock1.getOutputStream());
            inbound = new DataInputStream( cSock1.getInputStream() );

            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
              System.err.println("ERROR: No such host");
              IOException ie = new IOException("Quitting ftp Process");
              throw(ie);
            }
            while (responseLine.indexOf("-") == 3) responseLine = inbound.readLine();

            outbound.writeBytes("USER "+fp.login.getText()+"\r\n");
            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
                System.err.println("ERROR: No such user");
                IOException ie =  new IOException("Quitting ftp Process");
                throw(ie);                      
            }
            while (responseLine.indexOf("-") == 3)
                    responseLine  = inbound.readLine();

            outbound.writeBytes("PASS "+fp.passwd.getText()+"\r\n");
            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
                System.err.println("ERROR: Wrong PassWord");
                IOException ie = new IOException("Quitting ftp Process");
                throw(ie);
            }
            while (responseLine.indexOf("-") == 3)
                    responseLine  = inbound.readLine();
            explore(".");
        }
        catch (UnknownHostException e)  
        {
            fp.reportProgress("UnknownHostException: " + e);
        }
        catch (IOException e)  
        {
            System.err.println("IOException: " + e);
            System.err.println("Check the Address");
        }

    }

/**
 *  This Funtions down loads a file from the present working
 *  directory in the host server to the current directory of
 *  the user. The local directory should be passed as parameter
 *  to the function. Aborts if the file does not exist in the
 *  directory. Care should be taken to call the function only
 *  with files and not directories.
 *       
 *  Result: the specified file is copied to the present directory
 *        is successful
 *  Aborts if file not found after printing the error message
 */


    public void putFile(String fileName,String directory)  
    {
        byte responseLines[] = new byte[80];
        String responseLine;
        try
        {
            outbound.writeBytes("MODE B\r\n");

            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
                fp.reportProgress("Block Mode not possible."+ 
                          " Trying Stream...");
                outbound.writeBytes("MODE S\r\n");
                System.out.println(inbound.readLine());
            }
            while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();
            
            if (fp.binary.getState())
                outbound.writeBytes("TYPE I\r\n");
            else
            if (fp.ascii.getState())
                outbound.writeBytes("TYPE A\r\n");
            else
                System.out.println("This can Not happen");

            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
                System.err.println("ERROR: Wrong Type");
                IOException ie = new IOException("Quitting ftp Process");
                throw(ie);
            }
            while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();

            outbound.writeBytes("PASV\r\n");
            String passive;
            if ((passive = inbound.readLine()).startsWith("5"))
            {
                System.err.println("ERROR: aquiring a passive port");
                IOException ie = new IOException("Quitting ftp Process");
                throw(ie);
            }
            while (responseLine.indexOf("-") == 3)
                  responseLine = inbound.readLine();
	    	

            StringTokenizer token =
                  new StringTokenizer(passive,"    ,()",false);
            Integer portNum;
            int p=0;
            
            if(token.nextToken().equals("227"))
            {
                  for(int i = 0; i<7; i++)
                     token.nextToken();
                  portNum = new Integer(token.nextToken());
                  p = portNum.intValue() * 256;
                  portNum = new Integer(token.nextToken());
                  p += portNum.intValue();   
    
                  Socket cSock2 = new Socket(site,p);               
                  DataOutputStream outBound1 = new DataOutputStream( cSock2.getOutputStream() );

                  outbound.writeBytes("PWD\r\n");
                  fp.reportProgress(inbound.readLine());

                  outbound.writeBytes("STOR " + fileName + "\r\n");
                  System.out.println(fileName);

                  if((responseLine = inbound.readLine()).startsWith("5"))
                  {
                    System.err.println("can't create  such File : may lack permission");
                    IOException ie = new IOException("Quitting ftp Process");
                    throw(ie);
                  }
                  while (responseLine.indexOf("-") == 3)
                            responseLine = inbound.readLine();
		  
                  
                  int howMany;
                  File file = new File(directory);
                  file.mkdirs();

                  FileInputStream inFile =  new FileInputStream(directory + File.separator + fileName);
                  while ((howMany = inFile.read(responseLines,0,80)) != -1)
                            outBound1.write(responseLines,0,howMany);

                  inFile.close();

                  outBound1.close();

                  cSock2.close();
                  if (!(responseLine = inbound.readLine()).startsWith("226"))
                  {
                    System.err.println("ERROR: during downloading File"
                                                                + fileName);
                    System.err.println("Try again");
                  }
                  while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();


            } 
        }
        catch (IOException ioe)  
        {
            fp.reportProgress("IOException: " + ioe);
        }
    }


/**
 *  This Funtions down loads a file from the present working
 *  directory in the host server to the current directory of
 *  the user. The local directory should be passed as parameter
 *  to the function. Aborts if the file does not exist in the
 *  directory. Care should be taken to call the function only
 *  with files and not directories.
 *       
 *  Result: the specified file is copied to the present directory
 *        is successful
 *  Aborts if file not found after printing the error message
 */


    public void getFile(String fileName,String directory)  
    {
        byte responseLines[] = new byte[80];
        String responseLine;
        try
        {
            outbound.writeBytes("MODE B\r\n");

            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
                fp.reportProgress("Block Mode not possible."+ 
                          " Trying Stream...");
                outbound.writeBytes("MODE S\r\n");
                System.out.println(inbound.readLine());
            }
            while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();
            
            if (fp.binary.getState())
                outbound.writeBytes("TYPE I\r\n");
            else
            if (fp.ascii.getState())
                outbound.writeBytes("TYPE A\r\n");
            else
                System.out.println("This can Not happen");

            if ((responseLine = inbound.readLine()).startsWith("5"))
            {
                System.err.println("ERROR: Wrong Type");
                IOException ie = new IOException("Quitting ftp Process");
                throw(ie);
            }
            while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();

            outbound.writeBytes("PASV\r\n");
            String passive;
            if ((passive = inbound.readLine()).startsWith("5"))
            {
                System.err.println("ERROR: aquiring a passive port");
                IOException ie = new IOException("Quitting ftp Process");
                throw(ie);
            }
            while (responseLine.indexOf("-") == 3)
                  responseLine = inbound.readLine();
	    	

            StringTokenizer token =
                  new StringTokenizer(passive,"    ,()",false);
            Integer portNum;
            int p=0;
            
            if(token.nextToken().equals("227"))
            {
                  for(int i = 0; i<7; i++)
                     token.nextToken();
                  portNum = new Integer(token.nextToken());
                  p = portNum.intValue() * 256;
                  portNum = new Integer(token.nextToken());
                  p += portNum.intValue();   
    
                  Socket cSock2 = new Socket(site,p);               
                  DataInputStream inbound1 = new DataInputStream( cSock2.getInputStream() );

                  outbound.writeBytes("PWD\r\n");
                  fp.reportProgress(inbound.readLine());

                  outbound.writeBytes("RETR " + fileName + "\r\n");
                  System.out.println(fileName);

                  if((responseLine = inbound.readLine()).startsWith("5"))
                  {
                    System.err.println("ERROR: No such File");
                    IOException ie = new IOException("Quitting ftp Process");
                    throw(ie);
                  }
                  while (responseLine.indexOf("-") == 3)
                            responseLine = inbound.readLine();
		  
                  
                  int howMany;
                  File file = new File(directory);
                  file.mkdirs();

                  RandomAccessFile outFile =  new RandomAccessFile(directory + File.separator + fileName,"rw");
                  while ((howMany = inbound1.read(responseLines,0,80)) != -1)
                            outFile.write(responseLines,0,howMany);

                  outFile.close();

                  inbound1.close();

                  cSock2.close();
                  if (!(responseLine = inbound.readLine()).startsWith("226"))
                  {
                    System.err.println("ERROR: during downloading File"
                                                                + fileName);
                    System.err.println("Try again");
                  }
                  while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();


            } 
        }
        catch (IOException ioe)  
        {
            fp.reportProgress("IOException: " + ioe);
        }
    }


/**
 *   This Funtions down loads a directory from the present working
 *   directory in the host server to the current directory of
 *   the user. The local directory should be passed as parameter
 *   to the function along with the destination directory
 *   Aborts if the directory does not exist in the present working 
 *   directory. 
 *
 *   The function first lists all the files and subdirectories in
 *   the destination directory and then calls itself recursively
 *   for all the subdirectories with correspondingly updated local
 *   directories. The function getFile is called for the files in
 *   the directory
 *
 *   Result: the specified directory is copied to the
 *   present directoryf is successful
 *   Aborts if directory not found after printing the
 *               error message
 */

    public void getDirectory(String localCurrentDir, String targetDir)
    {
       String files[] = new String[1000];
                    // 1000 is the maximum files in a folder allowed
       String directories[] = new String[50];
                    // 1000 is the maximum files in a folder allowed
       int fileCount = 0;
       int dirCount = 0;
       boolean cdDone = false;
       String responseLine = null;
       try
       {
            if (outbound != null)
            {
                outbound.writeBytes("CWD "+targetDir+"\r\n");
    
                if ((responseLine=inbound.readLine()).startsWith("5"))
                {
                 fp.reportProgress("Such a Directory doesnt Exists");
                 fp.reportProgress("Omitting downLoading"+
                            " the Directory");
                 return;
                }
                cdDone = true;
                while (responseLine.indexOf("-") == 3)
                    responseLine = inbound.readLine();

                outbound.writeBytes("PASV\r\n");
               
                String passive;
                if ((passive = inbound.readLine()).startsWith("5"))
                {
                    System.err.println("ERROR: in aquiring a"+
                                                 " passive port");
                    IOException ie = new IOException("Quitting ftp Process");
                    throw(ie);
                }
                while (responseLine.indexOf("-") == 3)
                            responseLine = inbound.readLine();
    
    
                StringTokenizer token = new StringTokenizer(passive,"    ,()",false);
                Integer portNum;
                int p=0;
                
                if(token.nextToken().equals("227"))
                {
                  for(int i = 0; i<7; i++)
                     token.nextToken();
                  portNum = new Integer(token.nextToken());
                  p = portNum.intValue() * 256;
                  portNum = new Integer(token.nextToken());
                  p += portNum.intValue();   

                  System.out.println(p);

                  Socket cSock2 = new Socket(site,p);               
                  DataInputStream inbound1 = new DataInputStream(cSock2.getInputStream());

                  outbound.writeBytes("LIST\r\n");
    
                  if ((responseLine = inbound.readLine()).startsWith("5"))
                  {
                      System.err.println("ERROR: Cannot List the"+
                                                     " Directory");
                      IOException ie = new IOException("omitting the"+
                                                     " directory");
                      throw(ie);
                  }

                  while (responseLine.indexOf("-") == 3)
                        responseLine = inbound.readLine();

                  String inputLine = null;
                  String inputToken = null;
                  int fileOrDir;
                  System.out.println(inbound1.readLine());
                  byte ch[] = new byte[2];
                  ch[1] = 0;
                  while (true)
                  {
                    if ((ch[0] = (byte)inbound1.read()) == -1)
                        break;

                    if (inbound1 == null)
                            break;

                    if (cSock2 == null)
                        break;

                    inputLine = inbound1.readLine();
                    inputLine = (new String(ch))+inputLine;

                    if (inputLine.equals(""))
                             break;
                    fileOrDir = -1;
                    token = new StringTokenizer(inputLine," ",false);
                    inputToken = token.nextToken();
       
                    
                    if (inputToken.startsWith("d"))
                        fileOrDir = 1;
                    else if(inputToken.startsWith("-"))
                        fileOrDir = 0;

                    while(token.hasMoreTokens())
                        inputToken = token.nextToken();

                    if (inputToken.equals(".") || inputToken.equals("..") )
                            continue;

                    if (fileOrDir == 1)
                    {
                        directories[dirCount] = inputToken;
                        dirCount++;
                    }
                    else
                    if (fileOrDir == 0)
                    {
                        files[fileCount] = inputToken;
                        fileCount++;
                    }
                    System.out.println(inputToken);
                  }

                  System.out.println("Finished Listing....");
    
                  if (!(responseLine = inbound.readLine()).startsWith("226"))
                  {
                   System.err.println("ERROR: Could not list the"+
                    "           directory completely");
                   IOException ie =  new IOException("omitting the rest");
                   throw(ie);
                  }
                  while (responseLine.indexOf("-") == 3)
                            responseLine = inbound.readLine();
        
                  for (int i = 0 ; i < fileCount;i++)
                  {
                        System.out.println(i);
                        fp.reportProgress(files[i]);
                        getFile(files[i],localCurrentDir+File.separator+targetDir);
                  }
        
                  for (int i = 0 ; i < dirCount;i++)
                  {
                        fp.reportProgress(directories[i]);
                        getDirectory(localCurrentDir +File.separator + targetDir,directories[i]);
                  }
        
                }
                else
                {
                   fp.reportProgress("Error reserving a"+
                            " passive port  at the server side");
                }
                outbound.writeBytes("CWD ..\r\n");
                if((responseLine=inbound.readLine()).startsWith("5"))
                {
                    fp.reportProgress("cannot return from Directory.");
                    return;
                }
            }
            else
            {
             System.err.println("Control connection not yet open");
             System.err.println("Trying to write into it...");
            }
       }
       catch(IOException ie) 
       {
         try
         {
            if (cdDone)
            outbound.writeBytes("CWD ..\r\n");
            if((responseLine=inbound.readLine()).startsWith("5"))
            {
                    fp.reportProgress("Panic: cannot return from Directory.");
                    return;
            }
            fp.reportProgress("IOException :" + ie);
         }
         catch (IOException nie)
         {
                fp.reportProgress("Fatal: Exception inside exception"+ nie);
         }

       }
    }

/**
  *     This function descends down a specified directory in a
  *     host machine's present working directory.
  *     After descending it displays the list of files and 
  *     directories in that directory.
  *     if your permissions are not enough to descend to the
  *     directory it comes back to the previous directory
  *
  *     Result : The contents of selected directory is displayed
  *
  *              if directory not found or insufficient rights.
  *
  */

    public void explore(String directory)
    {
       System.out.println("inside the function to explore:  "
                                                    + directory);
       boolean cdDone = false;
       String responseLine = null;
       while (fp.entries.getItemCount() > 0)
            fp.entries.delItem(0);
       try
       {
            if (outbound != null)
            {
                outbound.writeBytes("CWD "+directory+"\r\n");
    
                if ((responseLine=inbound.readLine()).startsWith("5"))
                {
                 fp.reportProgress("Such a Directory doesnt Exists");
                 fp.reportProgress("Omitting downLoading Directory");
                 return;
                }
                while (responseLine.indexOf("-") == 3)
                                    responseLine = inbound.readLine();

                cdDone = true;
                outbound.writeBytes("PASV\r\n");
               
                String passive;
                if ((passive = inbound.readLine()).startsWith("5"))
                {
                 System.err.println("ERROR: in aquiring a passive port");
                 IOException ie =  new IOException("Quitting ftp Process");
                 throw(ie);
                }
                while (responseLine.indexOf("-") == 3)
                                    responseLine = inbound.readLine();
    
                /**
                System.out.println(passive);
                */
    
                StringTokenizer token =  new StringTokenizer(passive,"    ,()",false);
                Integer portNum;
                int p=0;
                
                if(token.nextToken().equals("227"))
                {
                      for(int i = 0; i<7; i++)
                         token.nextToken();
                      portNum = new Integer(token.nextToken());
                      p = portNum.intValue() * 256;
                      portNum = new Integer(token.nextToken());
                      p += portNum.intValue();   

                      System.out.println(p);

                      Socket cSock2 = new Socket(site,p);               
                      DataInputStream inbound1 = new DataInputStream( cSock2.getInputStream());

                      outbound.writeBytes("LIST\r\n");
        
                      if((responseLine = inbound.readLine()).startsWith("5"))
                      {
                        System.err.println("ERROR: Cannot List the"+
                                                           " Directory");
                        IOException ie = new IOException("omitting the directory");
                        throw(ie);
                      }
                      while (responseLine.indexOf("-") == 3)
                                     responseLine = inbound.readLine();

                      String inputLine = null;
                      String inputToken = null;
                      int fileOrDir;
                      System.out.println(inbound1.readLine());
                      byte ch[] = new byte[2];
                      ch[1] = 0;
                      while (true)
                      {
                        if ((ch[0] = (byte)inbound1.read()) == -1)
                        	break;

                        if (inbound1 == null)
                                break;

                        if (cSock2 == null)
                         	break;

                        inputLine = inbound1.readLine();
                        inputLine = (new String(ch))+inputLine;

                        if (inputLine.equals(""))
                                 break;

                        fileOrDir = -1;
                      token=new StringTokenizer(inputLine," ",false);
                        inputToken = token.nextToken();
                        //System.out.println(inputToken);
                        
                        if (inputToken.startsWith("d"))
                            fileOrDir = 1;
                        else if(inputToken.startsWith("-"))
                            fileOrDir = 0;

                        while(token.hasMoreTokens())
                            inputToken = token.nextToken();

                        if (fileOrDir == 1)
                        {
                         fp.entries.addItem(inputToken);
                         fp.isDir[fp.entries.getItemCount() - 1]=true;
                        }
                        else
                        if (fileOrDir == 0)
                        {
                         fp.entries.addItem(inputToken);
                         fp.isDir[fp.entries.getItemCount() - 1]=false;
                        }

                        System.out.println(inputToken);
                      }// while
                      System.out.println("Finished Listing....");
                      if (!(responseLine = inbound.readLine()).startsWith("226"))
                      {
                        System.err.println("ERROR: Could not list"+
                                         " the directory completely");
                        IOException ie = new IOException("omitting the rest");
                        throw(ie);
                      }
                }//if 
            }//if
       }//try
       catch(IOException ie) 
       {
         try
         {
            if (cdDone)
            outbound.writeBytes("CWD ..\r\n");
            if((responseLine=inbound.readLine()).startsWith("5"))
            {
                    fp.reportProgress("Panic: cannot return from Directory.");
                    return;
            }
            fp.reportProgress("IOException :" + ie);
         }
         catch (IOException nie)
         {
                fp.reportProgress("Fatal: Exception inside exception"+ nie);
         }
       }
       catch(ArrayIndexOutOfBoundsException ae) 
       {
        try
          {
            if (cdDone)
            outbound.writeBytes("CWD ..\r\n");
            if((responseLine=inbound.readLine()).startsWith("5"))
            {
                    fp.reportProgress("Panic: cannot return from Directory.");
                    return;
            }
            fp.reportProgress("Too many files in the directory"+
                                                " >1000. Connect again");
         }
         catch (IOException nie)
         {
                fp.reportProgress("Fatal: Exception inside exception"+ nie);
         }
       }
    }//explore

}

