/**
 * Flogman (Foto bLOG, Mail is All you Need)
 * Copyright (C) 2003 Junya Ishihara (http://flogman.sourceforge.net)
 *
 * 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.sourceforge.flogman;
import java.io.*;
import java.net.*;
import java.util.*;

/** FTP Connection Module */
public class FtpCom {
  private Socket FTPServer=null;
  private PrintWriter log = new PrintWriter( System.out );

  /** Test Code */
  public static void main( String argv[] ) {
    try {
      FtpCom ftp = new FtpCom(argv[0],argv[1],argv[2]);	 
      ftp.cd(argv[3]); 
      ftp.pwd();
      ftp.binary();
      List ls1=ftp.ls();
      List ls2=ftp.getFileList();
      ftp.noop();
      ftp.logout();
      System.out.println( "List all" );
      for( int i=0 ; i<ls1.size() ; i++ ) {
        System.out.println( ls1.get(i) );
      }
      System.out.println( "List filenames only" );
      for( int i=0 ; i<ls2.size() ; i++ ) {
        System.out.println( ls2.get(i) );
      }
      System.out.println( "End" );
    } catch( Exception e ) {
      e.printStackTrace();
    }
  }

  private FtpCom() {}

  public FtpCom( String ServerName ) throws Exception {
    this.login( ServerName, 21, "anonymous", "guest@unknown.com" );
  }

  public FtpCom( String ServerName, String User, String Pass ) throws Exception {
    this.login( ServerName, 21, User, Pass );
  }

  /** Get the last status code after reading InputStream */
  private int getLastStatusCode() throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(FTPServer.getInputStream()) );
    String buffer;
    do {
      buffer=reader.readLine();
      this.log( buffer );
    } while( buffer.charAt(3)=='-' );
    return Integer.parseInt( buffer.substring(0,3) );
  }

    /** Send specified FTP command */
    private void sendCommand( String message ) throws IOException {
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( FTPServer.getOutputStream() ));
        writer.write( message );
        writer.newLine();
        writer.flush();
        this.log( ">"+message );
    }

    public void setLogStream( PrintWriter log ) {
        this.log = log;
    }

    /** Log output */
    private void log( String message ) {
        this.log.println( message );
        this.log.flush();
    }

    /** Login */
    public void login( String ServerName, int port, String User,String Pass ) throws Exception {
        FTPServer=new Socket( ServerName, port );
        /** Verification of the connection */
        if( getLastStatusCode()!=220 ) {
            throw( new Exception("Fail connection") );
        }
        sendCommand( "USER "+User );
        if( getLastStatusCode()!=230 ) {
            // Password
            sendCommand( "PASS "+Pass );
            if( getLastStatusCode()!=230 ) {
                throw( new Exception("ID or Password is not correct.") );
            }
        }
    }

    /** Disconnect session */
    public void logout() throws Exception {
        sendCommand("QUIT");
        if( getLastStatusCode()!=221 ) {
            throw( new Exception("Fail to disconnect session correctly.") );
        }
        FTPServer.close();
    }

/**
    PORT Command
*/
    private ServerSocket port() throws Exception {
        String command="PORT";
        ServerSocket myDataSocket=new ServerSocket(0,1);
        int iPort=myDataSocket.getLocalPort();
        String ip = FTPServer.getLocalAddress().getHostAddress();
        command=command+" "+ip.replace('.',',');
        command=command+","+(iPort >> 8)+","+(iPort & 0xFF);
        sendCommand( command );
        if( getLastStatusCode()!=200 ) {
            throw( new Exception(command + " failed.") );
        }
        return myDataSocket;
    }
/**
    Set File Transfer Mode to Text
    @return Success or Fail
*/
    public void ascii() throws Exception {
        String command="TYPE A N";
        sendCommand( command );
        if( getLastStatusCode()!=200 ) {
            throw( new Exception(command + " failed.") );
        }
    }

/**
      Set File Transfer Mode to Binary
*/
    public void binary() throws Exception {
        String command="TYPE I";
        sendCommand( command );
        if( getLastStatusCode()!=200 ) {
            throw( new Exception(command + " failed.") );
        }
    }
/**
    Change current directory
    @param strDirectory
*/
    public void cd(String strDirectory) throws Exception {
        String command;
        command="CWD "+strDirectory;
        sendCommand( command );
        if( getLastStatusCode()!=250 ) {
            throw( new Exception(command + " failed.") );
        }
    }
/**
    Make Directory
    @param strDirectory
*/
    public void mkd(String strDirectory) throws Exception {
        String command;
        command="MKD "+strDirectory;
        sendCommand( command );
        if( getLastStatusCode()!=257 ) {
            throw( new Exception(command + " failed.") );
        }
    }
/**
    Get File list
    @return filelist
    If failed or file does not exist return will be "".(not null)
 */
    public List getFileList() throws Exception {
        List ls = ls();
        List file=new ArrayList();

        for( int i=0 ; i<ls.size() ; i++ ) {
            String text= (String)ls.get(i);
            int len=0;
            String name=null;
            if( !text.startsWith( "d" ) && !text.startsWith( "l" ) && !text.startsWith( "total" )) {
                //Not directory
                //Not Alias
                //If text does not start with total, considered as filename
                //System.out.println( "Beta>"+text );
                len=text.lastIndexOf(':');
                if( len>=0 ) {
                    name=text.substring( len );
                    name=name.substring( name.indexOf(' ')+1 );
                } else {
                    len=text.lastIndexOf(' ');
                    if( len<0 ) {
                        len=text.lastIndexOf('\t');
                        if( len<0 ) {
                            len=-1;
                        }
                    }
                    name=text.substring( len+1 );
                }
                //System.out.println( ":HH FileName>"+name );
                name=name.substring( name.indexOf(' ')+1 );
                //System.out.println( "File Name>"+name );
                file.add(name);
                //ftpc.get( name,localDir+"/"+name );
            }
        }
        return file;
    }

/** 
    LIST Command
    @return String returned by LIST Command
*/
    public List ls() throws Exception {
        String command="LIST";
        List list = new ArrayList();
        BufferedReader readData=null;
        ServerSocket myDataSocket=port();
        sendCommand(command);    //Check if it is directory or not
        //sendCommand("NLST -alL");
        //sendCommand("NLST");
        if( getLastStatusCode()!=150 ) {
            throw( new Exception(command + " failed.") );
        }
        Socket socRead=myDataSocket.accept();
        readData=new BufferedReader(new InputStreamReader(socRead.getInputStream()));
        String buffer;
        for(;;) {
            buffer = new String();
            buffer=readData.readLine();
            if(buffer==null)
            {
                break;
            }
            list.add( buffer );
        }
        socRead.close();
        myDataSocket.close();
        if( getLastStatusCode()!=226 ) {
            throw( new Exception(command + " failed.") );
        }
        return list;
    }
/** Download a file from a server
 * Copy the file to client default path
 * @param strFilename filename
 * @return Success or Fail
 */
    public void get(String strFilename) throws Exception {
        get(strFilename,strFilename);
    }

/** Download a file from a server
 * @return Success or Fail
 * @param strFilename Filename
 * @param strLocalFileName Specify the local location
 */
    public void get(String strFilename,String strLocalFileName) throws Exception {
        String command="RETR";
        DataInputStream readData=null;
        DataOutputStream writeData=null;
        ServerSocket myDataSocket=null;
        Socket socRead=null;

        try {
            myDataSocket=port();

            sendCommand( command+" "+strFilename);
            if( getLastStatusCode()!=150 ) {
                throw( new Exception(command + " failed.") );
            }

            socRead=myDataSocket.accept();
            readData=new DataInputStream(socRead.getInputStream());
            writeData=new DataOutputStream(new FileOutputStream(strLocalFileName));
            for(;;) {
                byte[] byteRead=new byte[10240];
                int iReadLen=readData.read(byteRead);
                if(iReadLen==(-1))
                {
                    break;
                }
                writeData.write(byteRead,0,iReadLen);
            }
            writeData.close();
            readData.close();
            socRead.close();
            if( getLastStatusCode()!=226 ) {
                throw( new Exception(command + " Transfer failed.") );
            }
        } catch( Exception e ) {
            e.printStackTrace( this.log );
            throw( new Exception() );
        } finally {
            try {
                if( socRead!=null ) socRead.close();
                if( myDataSocket!=null ) myDataSocket.close();
                if( writeData!=null ) writeData.close();
            } catch( IOException e ) {}
        }
    }

/**
 * @param strFilename
 * @return
 */
    public void put(String strFilename) throws Exception {
        put(strFilename,strFilename);
    }
/**
 * @param strLocalFileName
 * @param strFilename
 * @return
 */
    public void put(String strLocalFileName,String strFilename) throws Exception {
        String command="STOR";
        DataInputStream readData=null;
        DataOutputStream writeData=null;

        ServerSocket myDataSocket=null;
        Socket socWrite=null;
        try{
            // Check whether the file exist or not
            // If it does not exist throw exception
            readData=new DataInputStream(new FileInputStream(strLocalFileName));
            myDataSocket=port();

            sendCommand(command+" "+strFilename);
            if( getLastStatusCode()!=150 ) {
                throw( new Exception(command + " failed.") );
            }

            socWrite=myDataSocket.accept();
            writeData=new DataOutputStream(socWrite.getOutputStream());
            for(;;)
            {
                byte[] byteRead=new byte[10240];
                int iReadLen=readData.read(byteRead);
                if(iReadLen==(-1))
                {
                    break;
                }
                writeData.write(byteRead,0,iReadLen);
            }
            writeData.close();
            socWrite.close();

            if( getLastStatusCode()!=226 ) {
                throw( new Exception(command + " Transfer failed.") );
            }
        }finally{
            try{
                if(readData!=null) readData.close();
                if(writeData!=null) writeData.close();
                if(socWrite!=null) socWrite.close();
                if(myDataSocket!=null) myDataSocket.close();
            }catch(IOException e){
                e.printStackTrace( this.log );
            }
         }
    }
    /** Noop Command */
    public boolean noop() throws Exception {
        String command="NOOP";
        sendCommand( command );
        if( getLastStatusCode()!=200 ) {
            return false;
        }
        return true;
    }

    /** PWD Command */
    public boolean pwd() throws Exception {
        String command="PWD";
        sendCommand( command );
        if( getLastStatusCode()!=257 ) {
            return false;
        }
        return true;
    }

}
