import java.io.*;
import java.net.*;

/**
 * David Gardner    (951) 801-0143    davidgardner28@gmail.com
 * <p>
 * This program opens a telnet session with minimal terminal emulation. 
 * It takes in the host and optional port number as command line arguments.
 * <p>
 * The {@link IACReader} class handles control messages sent from the server.
 * <p>
 * {@link UserReader} runs in it's own thread reading in from the System's
 * standard input, then copying it to the output stream of the telnet socket.
 * <p>
 * {@link ThreadSafeWriter} wraps synchronized declarations around the 
 * PrintWriter methods.
 * <p>
 * This is mainly demonstration of my ability to make use of  Java's Socket 
 * and Thread classes, as well as read a protocol spec and implement it.
 * <p>
 * Telnet specifications from rfc854, rfc1091 and rfc1572
 * <p>
 * Limitations: The System.in cannot be prevented from echoing to the screen, 
 * this has been documented, and the only reasonable solution seems to be to
 * create a GUI.
 *
 * @author David Gardner
 * @see <a href="http://www.rfc-editor.org/rfc/rfc854.txt">RFC 854</a>
 * @see <a href="http://www.rfc-editor.org/rfc/rfc1091.txt">RFC 1091</a>
 * @see <a href="http://www.rfc-editor.org/rfc/rfc1572.txt">RFC 1572</a>
 */
public class jTelnet {
  public static void main(String[] args) throws IOException {
    final int DEFAULT_PORT = 23;	
    final int IAC = 255;	// Telnet Constant Interpret As Command

    Socket telnetSocket = null;	
    ThreadSafeWriter out = null;		
    InputStream in = null;	
    String sHost = null;	
    UserReader userInput;		
    IACReader iacHandler;
    int portNumber = 0;
    int i;
    boolean bError = false;
		
    //check command line options 
    try {
      sHost = args [0];
    } catch (Exception error) {
      System.err.println ("Usage: java jTelnet <host name> [port number]");
      System.exit(1);
    }
    if (args.length > 1) {
      try {
        portNumber = Integer.parseInt(args[1]);
      } catch  (NumberFormatException error) {
        System.err.println ("Invalid port number.");
        System.exit(1);
      }
    } else 
      portNumber = DEFAULT_PORT;

    try {  // attempt to establish communications with server
      telnetSocket = new Socket(sHost, portNumber);
      out = new ThreadSafeWriter(telnetSocket.getOutputStream(), false);	
      in = telnetSocket.getInputStream();	
    } catch (UnknownHostException error) { 
      System.err.println ("Host: " + sHost + " unreachable.");
      System.exit(1);
    } catch (IOException error) {  
      // server exists but doesn't accept connections on specified port
      System.err.println("Couldn't establish connection to: "
                         + sHost + " at port " + portNumber);
      System.exit(1);
    }

    userInput = new UserReader (out);		
    iacHandler = new IACReader (in, out);
    userInput.start ();
    			
    /* while we still have a connection to the server print incoming data to 
       screen unless it is a command from the server, then it should be handled
       by IACReader read() returns -1 on a closed connection */
    while (!bError && userInput.isAlive() && (i = in.read ()) >= 0) { 
      if (i != IAC)
        System.out.print ((char) i);
      else 
        bError = iacHandler.exec ();
    }
    
    // cleanup
    userInput.close();
    while (userInput.isAlive()) {
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {}
    }
    out.close();
    in.close();
    telnetSocket.close();
  }//main
}
