import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.lang.reflect.Array;

class FtpThread extends Thread {
  Socket mySocket;
  int counter;

  FtpThread(Socket i, int c) {
    mySocket=i;
    counter=c;
  }

  public void run() {
    boolean password=false;
    String user=new String();
    File thePath=new File("c:\\windows");

//    DataInputStream in=null;
    BufferedReader bIn=null;
    BufferedOutputStream bOut;
    PrintWriter pOut=null;

    Socket so=null;
    PrintWriter pSOut=null;
    DataInputStream pSIn=null;
    BufferedOutputStream bSOut=null;

    try {
      // set up socket streams
      bIn=new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
//      in=new DataInputStream(mySocket.getInputStream());
      bOut=new BufferedOutputStream(mySocket.getOutputStream());
      pOut=new PrintWriter(mySocket.getOutputStream(),true);
    }
    catch(Exception e) {
      // IOException
      System.out.println(e);
    }
      pOut.println("220 Jeff Nuttall FTP Service (Version 1.0).");

      boolean done=false;
      while(!done) {
        String str=null;
        try {
          str=bIn.readLine();
System.out.println("str:"+str);
        }
        catch(Exception e) {
          // IOException
          System.out.println(e);
        }
        // parse cmd and parameters     
        String cmd=null;
        int len=str.length();
        int space=str.indexOf(' ');
        if(len>0) {
          if(space<len && space>0) {
            cmd=str.substring(0,space);
            str=str.substring(space,len).trim();
          }
          else {
            cmd=str;
            str="";
          }
        }

        if(len<=0) {
          pOut.println("221 Bad Command.");
          done=true;
        }
        else if(cmd.equals("USER")) { 
          pOut.println("331 Password required for "+str+".");
          user=str;
        }
        else if(cmd.equals("PASS")) {
          if(user.equals("jeff") && str.equals("nutt")) {
            password=true;
            pOut.println("230 "+user+" logged in successfully.");
//            pOut.println("230 Welcome.");
//            pOut.println("230 Enjoy.");
          }
          else if(user.equals("ftp") || user.equals("anonymous")) {
            password=true;
            pOut.println("230 "+user+" logged in successfully.");
          }
          else
            pOut.println("530 Login incorrect.");
        }
        else if(!password)
          pOut.println("331 Password required");
        else if(cmd.equals("PASV")) {
          // parse parameters
          try  {
            ServerSocket ss=new ServerSocket(0);
            int lport=ss.getLocalPort();
//            pOut.println("227 Entering Passive Mode (127,0,0,1"
//              +","+Integer.toString(lport/256)
//              +","+Integer.toString(lport%256)+")");
            pOut.println("227 Entering Passive Mode ("+
              InetAddress.getLocalHost().getHostAddress().replace('.',',')
              +","+Integer.toString(lport/256)
              +","+Integer.toString(lport%256)+")");
            so=ss.accept();
            pSOut=new PrintWriter(so.getOutputStream(),true);
            bSOut=new BufferedOutputStream(so.getOutputStream());
            pSIn=new DataInputStream(so.getInputStream());
          }
          catch(Exception e) {
            System.out.println(e);
          }
//          pOut.println("200 connected on "+port);
        }
        else if(cmd.equals("PORT")) {
          // parse parameters
          int pos=str.indexOf(",");
          String address=str.substring(0,pos);
          str=str.substring(pos+1,str.length());
          for(int i=0;i<3;i++) {
            pos=str.indexOf(",");
            address=address+"."+str.substring(0,pos);
            str=str.substring(pos+1,str.length());
          }
          pos=str.indexOf(",");
          Integer I=new Integer(str.substring(0,pos));
          Integer I2=new Integer(str.substring(pos+1,str.length()));
          int port=I.intValue()*256+I2.intValue();

          // open socket
          try {
            so=new Socket(address,port);
            pSOut=new PrintWriter(so.getOutputStream(),true);
            bSOut=new BufferedOutputStream(so.getOutputStream());
            pSIn=new DataInputStream(so.getInputStream());
          }
          catch(Exception e) {
            //UnknownHostException
            //IOException
            System.out.println(e);
          }
          pOut.println("200 connected on "+port);
        }
        else if(cmd.equals("NLST")) {
          // not complete, list not in alphabetic order
          // does not filter when parameter passed

          pOut.println("150 listing "+thePath.getAbsolutePath());
          WFilter wf=new WFilter(str);
          String [] sA=thePath.list(wf);
          int length=Array.getLength(sA);

          // put in list to alphabetize
          OList list=new OList();
          for(int i=0;i<length;i++) {
            list.insert(new LString(sA[i]));          }

          OListIt it=new OListIt(list);
          LObject lo;
          it.reset();
          while((lo=it.next())!=null) {
            pSOut.println(lo);
          }
          
          pSOut.close();
          pOut.println("226 Done.");
        }
        else if(cmd.equals("LIST")) {
          // not complete, list not in alphabetic order
          // does not filter when parameter passed
          // formatting could be improved including date

          pOut.println("150 listing "+thePath.getAbsolutePath());
          WFilter wf=new WFilter(str);
          String [] sA=thePath.list(wf);
          String result,read,write;
          int length=Array.getLength(sA);

          // put in list to alphabetize
          OList list=new OList();
          for(int i=0;i<length;i++) {
            list.insert(new LString(sA[i]));
          }

          int sLen;
          SimpleDateFormat fd = new SimpleDateFormat ("MMM dd  yyyy");
                    Date date;
                    File fa;

          OListIt it=new OListIt(list);
          LObject lo;
          it.reset();
          while((lo=it.next())!=null) {
            fa=new File(thePath,lo.toString());
            date=new Date(fa.lastModified()); 
            if(fa.isDirectory())
              result="d";
            else
              result="-";

            if(fa.canRead())
              read="r";
            else
              read="-";

            if(fa.canWrite())
              write="w";
            else
              write="-";

            read=read+write+"x";
            result=result+read+read+read+"   1 win      win   ";
            read="          "+Long.toString(fa.length());
            sLen=read.length();
            result=result+read.substring(sLen-10,sLen)+" ";
            result=result+fd.format(date)+" "+lo.toString();
            pSOut.println(result);
          }
          pSOut.close();
          pOut.println("226 Done.");
        }
        else if(cmd.equals("STOR")) {
          // not complete
          pOut.println("150 listing "+thePath.getAbsolutePath());

          File f=new File(thePath,str);
          try {
            FileOutputStream fOut=new FileOutputStream(f.getPath());
            byte []buf=new byte[512];
            int len2;   
            while((len2=pSIn.read(buf))>0) {
              fOut.write(buf,0,len2);
            }
            fOut.flush();
            fOut.close();
            pSIn.close();
          }
          catch(Exception e) {
            System.out.println(e);
          }
          pOut.println("236 Done.");
        }
        else if(cmd.equals("RETR")) {
          // not complete
          pOut.println("125 Starting");

          File f=new File(thePath,str);
          if(f.exists() && f.isFile()) {
            try {
              FileInputStream fIn=new FileInputStream(f.getPath());
              byte []buf=new byte[512];
              int len2;   
              while((len2=fIn.read(buf))>0) {
                bSOut.write(buf,0,len2);
              }
              bSOut.flush();
              bSOut.close();
              fIn.close();
            }
            catch(Exception e) {
              System.out.println(e);
            }
          }
          else {
            pOut.println("250 "+f.getPath()+" not found.");
            // ???????????????
          }
          pOut.println("250 Done.");
        }
        else if(cmd.equals("CWD")) {
          str=str.replace('/','\\');
          File f=new File(str);
          File f1=null;
          if(f.isAbsolute()) 
            f1=f;
          else 
            f1=new File(thePath,str);
          
          try {
            f=new File(f1.getCanonicalPath());
          }
          catch(Exception e) {
            f=thePath;
          }
          
          if(f.isDirectory()) {
            thePath=f;
            pOut.println("250 Changed to "+thePath.getAbsolutePath()+".");
          }
          else {
            pOut.println("250 "+str+": does not exist.");
          }
        }
        else if(cmd.equals("XPWD") || cmd.equals("PWD")) {
          pOut.println("257 The current directory is "
            +thePath.getAbsolutePath());
        }
        else if(cmd.equals("QUIT")) {
          pOut.println("221 Bye!");
          done=true;
        }
        else if(cmd.equals("TYPE")) {
          // not complete, currently does nothing
          if(str.equals("I"))
            pOut.println("200 Binary Mode");
          if(str.equals("A"))
            pOut.println("200 ASCII Mode: Only Binary supported");
          else
            pOut.println("200 Mode: "+str+": Only Binary supported");
        }
        else if(cmd.equals("PASV")) {
          pOut.println("227 Entering Passive Mode (128,1,1,1,171,46)");
        }
        else {
          System.out.println("The command is "+cmd+" and the string is "+str+".");
          pOut.println("221 I don't support that command yet.");
          //done=true;
        }
      }
      try {
        mySocket.close();
        if(so!=null)
          so.close();
      }
      catch(Exception e) {
        //IOException
        System.out.println(e);
      }
System.out.println("closing:"+counter);
  }
}

class FtpServer {
  public static void main(String[] args) {
    int i=1;
    try {
      ServerSocket s=new ServerSocket(21);
      while(true) {
        Socket aSocket=s.accept();
        System.out.println("starting "+i);
        new FtpThread(aSocket,i).start();
        i++;
      }
    }
    catch (Exception e) {
      System.out.println(e);
    }
  }
}

