/*
 * @(#)TelnetSsh.java
 *
 * Copyright (c) 2003 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */
package com.jscape.inet.telnetssh;


import com.jscape.inet.ipclientssh.IpClientSsh;
import com.jscape.inet.ssh.util.SshParameters;
import com.jscape.inet.telnet.Telnet;
import com.jscape.inet.telnet.TelnetException;
import com.jscape.inet.telnet.TelnetInputStream;
import com.jscape.inet.telnet.TelnetOutputStream;

/**
 * Implements the basic functionality of a Telnet client as defined in RFC 854 tunnelling all data thru
 * a secure SSH connection.  This class requires that BOTH the telnet and SSH services be enabled on the
 * host machine and that port forwarding be enabled.  For a SSH client that does not require port forwarding
 * see the com.jscape.inet.ssh.Ssh class.
 * <p/>
 * Upon establishing a connection the Telnet server may attempt to perform option negotiation (see RFC 855).
 * Any optionnegotiation options exchanged must be handled prior to receiving a login or shell prompt.
 * <p/>
 * To handle option negotiation you must add a TelnetListener to the Telnet instance and overload the
 * doOption, dontOption, willOption and wontOption methods.
 * <p/>
 * Once option negotiation has completed sucessfully a separate thread is spawned that reads data sent by the
 * Telnet server. You can capture this data by overloading the dataReceived method in the TelnetListener
 * interface or TelnetAdapter class.
 * <p/>
 * See the {@link com.jscape.inet.ssh.Ssh} class to interact with a remote shell without port forwarding thru
 * the Telnet service.  Use of this class requires that the Telnet service be enabled on the remote host.
 * <p/>
 * Example Usage:
 * <p/>
 * <pre>
 * public class TelnetExample extends TelnetAdapter {
 *
 *      private TelnetSsh telnet = null;
 *      private OutputStream output = null;
 *      private static BufferedReader reader = null;
 *      private boolean connected = false;
 *      private String hostname = "10.0.0.1";
 *      private String sshHostname = "10.0.0.1";
 *      private String sshUsername = "jsmith";
 *      private String sshPassword = "secret";
 *      private SshParameters sshParams = null;
 *
 *      public TelnetExample() throws IOException, TelnetException {
 *
 *              // create SSH connection parameters
 *              sshParams = new SshParameters(sshHostname,sshUsername,sshPassword);
 *
 *              // create new TelnetSsh instance
 *              telnet = new TelnetSsh(sshParams,hostname);
 *
 *              // register this class as TelnetListener
 *              telnet.addTelnetListener(this);
 *
 *              // establish Telnet connection
 *              telnet.connect();
 *              connected = true;
 *
 *              // get output stream
 *              output = telnet.getOutputStream();
 *
 *              // sends all data entered at console to Telnet server
 *              String input = null;
 *              while ((input = reader.readLine()) != null) {
 *                      if (connected) {
 *                              ((TelnetOutputStream) output).println(input);
 *             } else {
 *                              break;
 *             }
 *         }
 *     }
 *
 *      // Invoked when Telnet socked is connected.
 *      public void connected(TelnetConnectedEvent event) {
 *              System.out.println("Connected");
 *     }
 *
 *      // Invoked when Telnet socket is disconnected. Disconnect can
 *      public void disconnected(TelnetDisconnectedEvent event) {
 *              connected = false;
 *              System.out.print("Disconnected.  Press enter key to quit.");
 *     }
 *
 *      // Invoked when Telnet server requests that the Telnet client begin performing specified <code>TelnetOption</code>.
 *      public void doOption(DoOptionEvent event) {
 *              // refuse any options requested by Telnet server
 *              telnet.sendWontOption(event.getOption());
 *     }
 *
 *      // Invoked when Telnet server offers to begin performing specified <code>TelnetOption</code>.
 *      public void willOption(WillOptionEvent event) {
 *              // refuse any options offered by Telnet server
 *              telnet.sendDontOption(event.getOption());
 *     }
 *
 *      // Invoked when data is received from Telnet server.
 *      public void dataReceived(TelnetDataReceivedEvent event) {
 *              // print data recevied from Telnet server to console
 *              System.out.print(event.getData());
 *     }
 *
 *      // starts console program
 *      public static void main(String[] args) {
 *              try {
 *                      // create BufferedReader to read data from console
 *                      reader = new BufferedReader(new InputStreamReader(System.in));
 *
 *                      // create new TelnetExample instance
 *                      TelnetExample example = new TelnetExample();
 *
 *         } catch (Exception e) {
 *                      e.printStackTrace(System.out);
 *         }
 *     }
 *
 * }
 *
 * </pre>
 *
 * @see com.jscape.inet.ssh.Ssh
 */
public class TelnetSsh
        extends Telnet {

    private SshParameters sshParameters;
    private IpClientSsh sshClient;

    /**
     * Creates a new TelnetSsh instance.
     *
     * @param hostname
     * @param sshParameters
     */
    public TelnetSsh(SshParameters sshParameters, String hostname) {
        super(hostname);
        this.sshParameters = sshParameters;
    }

    /**
     * Establishes connection with SSH server and creates secure tunnel to Telnet server.
     *
     * @throws TelnetException if an I/O or Telnet related error occurs
     */
    public void connect()
            throws TelnetException {
        try {
            sshClient = new IpClientSsh(sshParameters, hostname, port, timeout);
            sshClient.setReadTimeout(this.readingTimeout);
            
            sshClient.connect();

            input = new TelnetInputStream(this, sshClient.getInputStream());
            output = new TelnetOutputStream(sshClient.getOutputStream());

            // start reader in new thread
            reader = new Thread(new TelnetReader(input));
            reader.start();

            // fire connection event and negotiate options
            fireTelnetConnected(hostname, port);

        } catch (Exception e) {
            e.printStackTrace();
            throw new TelnetException(e.getMessage(), e);
        }
    }

    /**
     * Closes tunnel to Telnet server and closes connection with SSH server.
     */
    public void disconnect() {
        try {
            sshClient.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        input = null;
        output = null;
        this.fireTelnetDisconnected(hostname, port);
    }

    /**
     * Gets SSH parameters used in establishing connection with SSH server.
     *
     * @return the SSH parameters
     *
     * @see com.jscape.inet.ssh.util.SshParameters
     */
    public SshParameters getSshParameters() {
        return sshParameters;
    }

    /**
     * Sets SSH parameters used in establishing connection with SSH server.
     *
     * @param parameters the SSH parameters
     *
     * @see com.jscape.inet.ssh.util.SshParameters
     */
    public void setSshParameters(SshParameters parameters) {
        sshParameters = parameters;
    }

}
