/*
 * Copyright (C) 2006 Andreas Beck <becka-ftpnat@bedatec.de>
 *
 * 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.
 */

import java.applet.Applet;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedOutputStream;
import java.lang.Integer.*;
import java.net.Socket;
import java.net.UnknownHostException;

public class ftphelppentest extends Applet implements Runnable
{
  private Socket socket = null;
  private TextArea area = null;
  private Button startbutton = new Button("Start Test");
  private TextField RouterField = null;
  private TextField InternalIPField = null;
  private TextField PortlistField = null;
  private static final String CHARSET_ASCII = "US-ASCII";
  private static final byte CR = (byte) 0xd;
  private static final byte LF = (byte) 0xa;
  private String router,internalip,originalinternalip,testhost,portlist;

  /* Simple helpers to ease using the CRLF terminated control lines
   * of FTP connections.
   */ 
  private static void send (BufferedOutputStream w, String cmd) throws IOException {
    byte[] bytes = cmd.getBytes("US-ASCII");
    byte[] crnl  = { CR, LF } ;
    w.write(bytes, 0, bytes.length);
    w.write(crnl, 0, 2);
    w.flush ();
  }

  private static String readLine(InputStream r) throws IOException {
     String rc= new String("");
     byte[] bytes = {(byte) 0};
     while(-1 != r.read(bytes)) {
       if (bytes[0]==CR) /* Ignore CR */
         continue;
       if (bytes[0]==LF) /* Break at LF */
         break;
       rc=rc+(new String(bytes));
     }
     return rc.toString();
  }

  public boolean action(Event e,Object what) {
    if (e.target==startbutton) {
      internalip=InternalIPField.getText();
      router=RouterField.getText();
      portlist=PortlistField.getText();
      Thread thread = new Thread(this);
      thread.start();
    }
    return true;
  }

  private String GetLocalIP() {
    String ip;
    try {
      socket = new Socket (testhost, 21);
      ip = socket.getLocalAddress().getHostAddress();
      socket.close();
    }
    catch (IOException e) { return "Error"; }
    return ip;
  }

  public void init ()
  {
//    setLayout (new java.awt.GridLayout (1, 0));
    GridBagConstraints cons =new GridBagConstraints();
    GridBagLayout   mylayout=new GridBagLayout();

    setLayout(mylayout);
    router  =getDocumentBase().getQuery();
    RouterField=new TextField(router,20);
    testhost=getCodeBase().getHost();
    internalip=originalinternalip=GetLocalIP();
    InternalIPField=new TextField(internalip,20);
    portlist=getParameter("PORT");
    PortlistField=new TextField(portlist,30);

    area=new TextArea ("",20,40);

    cons.anchor=GridBagConstraints.SOUTH;
    cons.fill=GridBagConstraints.HORIZONTAL;
    cons.gridx=0;cons.gridy=0;
    cons.gridwidth=2;cons.gridheight=1;
    add(startbutton,cons);

    cons.fill=GridBagConstraints.NONE;
    cons.anchor=GridBagConstraints.WEST;
    cons.gridx=0;cons.gridy=1;
    cons.gridwidth=cons.gridheight=1;
    add(new Label("Routertype:"),cons);

    cons.anchor=GridBagConstraints.EAST;
    cons.gridx=1;cons.gridy=1;
    cons.gridwidth=cons.gridheight=1;
    add(RouterField,cons);

    cons.anchor=GridBagConstraints.WEST;
    cons.gridx=0;cons.gridy=2;
    cons.gridwidth=cons.gridheight=1;
    add(new Label("Internal IP:"),cons);

    cons.anchor=GridBagConstraints.EAST;
    cons.gridx=1;cons.gridy=2;
    cons.gridwidth=cons.gridheight=1;
    add(InternalIPField,cons);

    cons.anchor=GridBagConstraints.WEST;
    cons.gridx=0;cons.gridy=3;
    cons.gridwidth=cons.gridheight=1;
    add(new Label("Portlist:"),cons);

    cons.anchor=GridBagConstraints.EAST;
    cons.gridx=1;cons.gridy=3;
    cons.gridwidth=cons.gridheight=1;
    add(PortlistField,cons);


    cons.anchor=GridBagConstraints.SOUTH;
    cons.gridx=0;cons.gridy=4;
    cons.gridwidth=2;cons.gridheight=1;
    add(area,cons);

  }

  public void run() {
    String result;
    int whichport;

    
    area.append("Checking FTP-NAT for Router "+router+"\n");
    area.append("Int.IP to check is "+internalip+",mine is "+originalinternalip+"\n");
    if (!internalip.equals(originalinternalip)) {
      area.append("IPs do not match. Router very broken if that works.\n");
    }
    while(portlist!="") {
      int pos;
      if (portlist.indexOf(',')>=0) {
        whichport = Integer.parseInt (portlist.substring(0,portlist.indexOf(',')));
      } else {
        whichport = Integer.parseInt (portlist);
      }
      result="Port: "+whichport+" Result: ";
      result+=do_ftp_port(testhost, router, whichport);
      result+="\n";
      area.append(result);
      repaint();
      validate ();
      pos=portlist.indexOf(',');
      if (pos<=0) break;
      portlist=portlist.substring(pos+1);
    }
  }

  public String do_ftp_port (String host, String router, int open_port)
  {
    String rc,ip,port;
    BufferedOutputStream w;
    InputStream r;
    Socket socket;
 
    try {
      socket = new Socket (host, 21);
    }
    catch (UnknownHostException e) { return "Testhost not found"; }
    catch (IOException e) { return "IO error in Socket()"; }

    try {
      w = new BufferedOutputStream(socket.getOutputStream());
      r = socket.getInputStream ();
    }
    catch (IOException e) { return "IO error in get*Stream()"; }

    try {
      rc=readLine(r);
    }
    catch (IOException e) { return "IO error getting banner"; }

    ip = internalip.replace ('.', ',');
    port = Integer.toString ((open_port >> 8) & 0xff) + "," + Integer.toString (open_port & 0xff);

    try {
      send (w, "USER ftp");
      rc=readLine(r);
      if (0 != rc.substring(0,4).compareTo("331 ")) {
        socket.close();
        return "400 USER Error ("+rc+")";
      }
    }
    catch (IOException e) { return "IO error while processing USER request"; }

    try {
      send (w, "PASS ftpnathelpertest");
      rc=readLine(r);
      if (0 != rc.substring(0,4).compareTo("230 ")) {
        socket.close();
        return "400 PASS Error ("+rc+")";
      }
    }
    catch (IOException e) { return "IO error while processing PASS request"; }

    try {
      send (w, "CWD "+router);
      rc=readLine(r);
      if (0 != rc.substring(0,4).compareTo("250 ")) {
        socket.close();
        return "400 CWD Error ("+rc+")";
      }
    } 
    catch (IOException e) { return "IO error while processing CWD request"; }

    rc="PORT " + ip + "," + port;
    try {
      send (w, rc);
    } 
    catch (IOException e) { return "IO error while processing PORT request"; }

    try {
      rc=readLine(r);
      if (0 != rc.substring(0,4).compareTo("200 ")) {
          socket.close();
        return "400 PORT Error ("+rc+")";
      }
    }
    catch (IOException e) { return "IO error while reading PORT reply"; }
    catch (StringIndexOutOfBoundsException e) { return "400 PORT Error (short return code '"+rc+"')"; }

    try {
      send (w, "LIST " + ip + "," + port);
      rc=readLine(r);
    }
    catch (IOException e) { return "IO error while processing LIST request"; }

    try {
      socket.close();
      return rc;
    }
    catch (IOException e) { return "IO error in close();"; }
  }

  public void start() {
  }
}

