/*
 * @(#)TelnetSsl.java
 *
 * Copyright (c) 2005-2006 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.telnetssl;

import com.jscape.inet.ipclient.IpClientException;
import com.jscape.inet.ipclientssl.IpClientSsl;
import com.jscape.inet.telnet.Telnet;
import com.jscape.inet.telnet.TelnetException;
import com.jscape.inet.telnet.TelnetInputStream;
import com.jscape.inet.telnet.TelnetOutputStream;

import java.io.IOException;

/**
 * Implements a secure telnet client using SSL/TLS.
 */
public class TelnetSsl
        extends Telnet {
    
    private static final int DEFAULT_PROXY_PORT = 1080;
    private static final String DEFAULT_PROXY_TYPE = "SOCKS5";

    private IpClientSsl client = null;
    private String serverFilename = null;
    private String serverPassword = null;
    private String serverStoretype = null;
    private String clientFilename = null;
    private String clientPassword = null;
    private String clientStoretype = null;
    private String proxyHostname = null;
    private int proxyPort = DEFAULT_PROXY_PORT;
    private String proxyUsername = null;
    private String proxyPassword = null;
    private String proxyType = DEFAULT_PROXY_TYPE;


    /**
     * Constructs a new TelnetSsl instance
     *
     * @param hostname the telnet server hostname
     */
    public TelnetSsl(String hostname) {
        super(hostname);
    }

    /**
     * Constructs a new TelnetSsl instance
     *
     * @param hostname the server hostname
     * @param port     the server port
     */
    public TelnetSsl(String hostname, int port) {
        super(hostname, port);
    }

    /**
     * Establishes connection to telnet server using SSL/TLS.
     *
     * @throws TelnetException if an I/O or telnet related error occurs
     */
    public void connect()
            throws TelnetException {
        client = new IpClientSsl(hostname, port, timeout);
        
        if (this.proxyHostname != null) {
            this.client.setProxyHost(this.proxyHostname, this.proxyPort);
            this.client.setProxyAuthentication(this.proxyUsername, this.proxyPassword);
            this.client.setProxyType(this.proxyType);
        }
        if (serverFilename != null && serverPassword != null) {
            client.setServerCertificates(
                    serverFilename,
                    serverPassword,
                    serverStoretype);
        }

        if (clientFilename != null && clientPassword != null) {
            client.setClientCertificates(
                    clientFilename,
                    clientPassword,
                    clientStoretype);
        }
        
        client.setReadTimeout(this.readingTimeout);

        try {
            client.connect();
            socket = client.getSocket();
            input = new TelnetInputStream(this, socket.getInputStream());
            output = new TelnetOutputStream(client.getOutputStream());

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


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

        } catch (IpClientException ip) {
            ip.printStackTrace();
            throw new TelnetException(ip.getMessage(), ip);
        } catch (IOException ioe) {
            throw new TelnetException(ioe.getMessage(), ioe);
        }
    }

    /**
     * Disconnects from telnet server.
     */
    public void disconnect() {
        // wait for any extra incoming data to be receive
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            // todo: ignore?
        }

        try {
            client.disconnect();
        } catch (Exception e) {
            // todo: ignore?
        }

        try {
            input.close();
        } catch (Exception e) {
            // todo: ignore?
        }

        try {
            output.close();
        } catch (Exception e) {
            // todo: ignore?
        }
    }

    /**
     * Sets optional server certificate to be used during authentication.
     * Default expected keystore format is JKS format. Existing certificate may be imported into keystore using
     * the <code>keytool</code> utility provided in the Sun JDK.
     * <p/>
     * Example:
     * <p/>
     * <pre>
     * keytool.exe -import -file x509.cer -keystore your_store_name
     * </pre>
     * <p/>
     * The example above imports the certificate x509.cer into a keystore named your_store_name
     * <p/>
     *
     * @param filename the keystore file containing the server certificate
     * @param password the keystore password
     */
    public void setServerCertificates(String filename, String password) {
        this.serverFilename = filename;
        this.serverPassword = password;
    }

    /**
     * Sets optional server certificate to be used during authentication.
     * Default expected keystore format is JKS format. Existing certificate may be imported into keystore using
     * the <code>keytool</code> utility provided in the Sun JDK.
     * <p/>
     * Example:
     * <p/>
     * <pre>
     * keytool.exe -import -file x509.cer -keystore your_store_name
     * </pre>
     * <p/>
     * The example above imports the certificate x509.cer into a keystore named your_store_name
     * <p/>
     *
     * @param filename  the keystore file containing the server certificate
     * @param password  the keystore password
     * @param storetype the keystore type valid values include jks and pkcs12
     */
    public void setServerCertificates(
            String filename,
            String password,
            String storetype) {
        this.serverFilename = filename;
        this.serverPassword = password;
        this.serverStoretype = storetype;
    }

    /**
     * Sets optional client certificate to be used during authentication.
     * Default expected keystore format is JKS format. Existing certificate may be imported into keystore using
     * the <code>keytool</code> utility provided in the Sun JDK.
     * <p/>
     * Example:
     * <p/>
     * <pre>
     * keytool.exe -import -file x509.cer -keystore your_store_name
     * </pre>
     * <p/>
     * The example above imports the certificate x509.cer into a keystore named your_store_name
     * <p/>
     *
     * @param filename the keystore file containing the client certificate
     * @param password the keystore password
     */
    public void setClientCertificates(String filename, String password) {
        this.clientFilename = filename;
        this.clientPassword = password;
    }

    /**
     * Sets optional client certificate to be used during authentication.
     * Default expected keystore format is JKS format. Existing certificate may be imported into keystore using
     * the <code>keytool</code> utility provided in the Sun JDK.
     * <p/>
     * Example:
     * <p/>
     * <pre>
     * keytool.exe -import -file x509.cer -keystore your_store_name
     * </pre>
     * <p/>
     * The example above imports the certificate x509.cer into a keystore named your_store_name
     * <p/>
     *
     * @param filename  the keystore file containing the client certificate
     * @param password  the keystore password
     * @param storetype the keystore type valid values include jks and pkcs12
     */
    public void setClientCertificates(
            String filename,
            String password,
            String storetype) {
        this.clientFilename = filename;
        this.clientPassword = password;
        this.clientStoretype = storetype;
    }

    /**
     * Sets the username and password to use when for authentication with
     * proxy server.
     * To clear these settings invoke the <code>#clearProxySettings</code> method.
     *
     * @param proxyUsername the proxy username
     * @param proxyPassword the proxy password
     *
     * @see #clearProxySettings
     */
    public void setProxyAuthentication(String proxyUsername,
                                       String proxyPassword) {
        this.proxyUsername = proxyUsername;
        this.proxyPassword = proxyPassword;
    }

    // PROXY CONTROL

    /**
     * Sets the proxy hostname and port for this connection.
     * To clear these settings invoke the <code>#clearProxySettings</code> method.
     *
     * @param proxyHostname the hostname or ip address of the proxy server
     * @param proxyPort     the port of the proxy server
     *
     * @see #clearProxySettings
     */
    public void setProxyHost(String proxyHostname, int proxyPort) {
        this.proxyHostname = proxyHostname;
        this.proxyPort = proxyPort;
    }

    /**
     * Sets the proxy type will be used for this connection.
     *
     * @param proxyType The proxy type. Valid values: HTTP, SOCKS5
     */
    public void setProxyType(String proxyType) {
        this.proxyType = proxyType;
    }

    /**
     * Clears proxy server values.
     */
    public void clearProxySettings() {
        setProxyAuthentication(null, null);
        setProxyHost(null, DEFAULT_PROXY_PORT);
        setProxyType(DEFAULT_PROXY_TYPE);
    }
}
