/*
 * @(#)SshParameters.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.ssh.util;

import com.jscape.inet.ssh.protocol.v2.authentication.ClientAuthentication;
import com.jscape.inet.ssh.protocol.v2.messages.PublicKeyAlgorithm;
import com.jscape.inet.ssh.protocol.v2.transport.keyexchange.HostKeyVerificationService;

import java.io.File;
import java.io.Serializable;
import java.security.KeyPair;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * Contains information used in establishing a connection to and authenticating with an SSH server.<P>
 * <p/>
 * Username/Password authentication example:<P>
 * <p/>
 * <pre>
 * String hostname = "hostname.com";
 * String username = "jsmith";
 * String password = "secret";
 *
 * SshParameters sshParams = new SshParameters(hostname,username,password);
 * </pre>
 * <p/>
 * Public-key authentication using passphrase protected private key example:<P>
 * <p/>
 * <pre>
 * String hostname = "hostname.com";
 * String username = "jsmith";
 *
 * // passphrase for private key
 * String password = "secret";
 *
 * // location of private key file
 * File privateKey =  new File("c:/keys/id_rsa");
 *
 * SshParameters sshParams = new SshParameters(hostname,username,password,privateKey);
 * </pre>
 * <p/>
 * The SshParameters class supports private keys in both OpenSSH and SSH.COM private key formats.
 */
public class SshParameters
        implements Serializable {

    private static final int DEFAULT_PORT = 22;
    private static final int DEFAULT_TIMEOUT = 30000;

    private static final KeyPairAssembler KEY_ASSEMBLER = new KeyPairAssembler();

    private String proxyType;
    private String proxyHost;
    private int proxyPort;
    private String proxyUsername;
    private String proxyPassword;
    private String hostname;
    private int port;
    private long connectionTimeout;
    private long readingTimeout;
    private Integer socketTrafficClass;
    private Boolean tcpNoDelay;
    private HostKeyVerificationService hostKeyVerifier;
    private String username;
    private String password;
    private KeyPair keyPair;
    private PublicKeyAlgorithm publicKeyAlgorithm;
    private ClientAuthentication clientAuthentication;

    private static KeyPair keyPairFor(File keyFile, String filePassword) {
        try {
            return KEY_ASSEMBLER.restoreKeyPair(keyFile, filePassword);
        } catch (Exception e) {
            throw new IllegalArgumentException("Unsupported key format or password was incorrect.", e);
        }
    }

    /**
     * Creates a new parameters instance.
     * @param proxyType a proxy type
     * @param proxyHost a proxy host
     * @param proxyPort a proxy port
     * @param proxyUsername a proxy username
     * @param proxyPassword a proxy password
     * @param host a server host
     * @param port a server port
     * @param connectionTimeout a connection timeout
     * @param readingTimeout a reading timeout
     * @param socketTrafficClass a socket traffic class
     * @param tcpNoDelay a TCP no-delay flag
     * @param hostKeyVerifier a server host key verifier
     * @param username an username
     * @param password a password
     * @param keyPair user keys
     * @param clientAuthentication a client authentication operation
     */
    public SshParameters(String proxyType,
                         String proxyHost,
                         int proxyPort,
                         String proxyUsername,
                         String proxyPassword,
                         String host,
                         int port,
                         long connectionTimeout,
                         long readingTimeout,
                         Integer socketTrafficClass,
                         Boolean tcpNoDelay,
                         HostKeyVerificationService hostKeyVerifier,
                         String username,
                         String password,
                         KeyPair keyPair,
                         PublicKeyAlgorithm publicKeyAlgorithm,
                         ClientAuthentication clientAuthentication) {
        this.proxyType = proxyType;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
        this.proxyUsername = proxyUsername;
        this.proxyPassword = proxyPassword;

        assertNotNull(host);
        this.hostname = host;

        this.port = port;
        this.connectionTimeout = connectionTimeout;
        this.readingTimeout = readingTimeout;
        this.socketTrafficClass = socketTrafficClass;
        this.tcpNoDelay = tcpNoDelay;

        assertNotNull(hostKeyVerifier);
        this.hostKeyVerifier = hostKeyVerifier;

        assertNotNull(username);
        this.username = username;

        this.password = password;
        this.keyPair = keyPair;
        this.publicKeyAlgorithm = publicKeyAlgorithm;
        this.clientAuthentication = clientAuthentication;
    }

    /**
     * Creates a new parameters instance.
     * @param proxyType a proxy type
     * @param proxyHost a proxy host
     * @param proxyPort a proxy port
     * @param proxyUsername a proxy username
     * @param proxyPassword a proxy password
     * @param host a a server host
     * @param port a server port
     * @param connectionTimeout a connection timeout
     * @param readingTimeout a reading timeout
     * @param socketTrafficClass a socket traffic class
     * @param tcpNoDelay a TCP no-delay flag
     * @param hostKeyVerifier a server host key verifier
     * @param username an username
     * @param password a password
     * @param keyFile a user keys file
     * @param keyFilePassword a keys file password
     * @param clientAuthentication a client authentication operation
     */
    public SshParameters(String proxyType,
                         String proxyHost,
                         int proxyPort,
                         String proxyUsername,
                         String proxyPassword,
                         String host,
                         int port,
                         long connectionTimeout,
                         long readingTimeout,
                         Integer socketTrafficClass,
                         Boolean tcpNoDelay,
                         HostKeyVerificationService hostKeyVerifier,
                         String username,
                         String password,
                         File keyFile,
                         String keyFilePassword,
                         PublicKeyAlgorithm publicKeyAlgorithm,
                         ClientAuthentication clientAuthentication) {
        this(proxyType,
             proxyHost,
             proxyPort,
             proxyUsername,
             proxyPassword,
             host,
             port,
             connectionTimeout,
             readingTimeout,
             socketTrafficClass,
             tcpNoDelay,
             hostKeyVerifier,
             username,
             password,
             keyFile != null ? keyPairFor(keyFile, keyFilePassword) : null,
             publicKeyAlgorithm,
             clientAuthentication);
    }

    /**
     * Creates a new parameters instance.
     * @param host a server host
     * @param port a server port
     * @param username an username
     * @param password a password
     * @param keyPair user keys
     */
    public SshParameters(String host, int port, String username, String password, KeyPair keyPair) {
        this(null, null, 0, null, null, host, port, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, null, true, HostKeyVerificationService.NULL, username, password, keyPair, keyPair != null ? PublicKeyAlgorithm.algorithmFor(keyPair.getPublic()) : PublicKeyAlgorithm.RSA, null);
    }

    /**
     * Creates a new parameters instance.
     * @param host a server host
     * @param username an username
     * @param password a password
     * @param keyPair user keys
     */
    public SshParameters(String host, String username, String password, KeyPair keyPair) {
        this(host, DEFAULT_PORT, username, password, keyPair);
    }

    /**
     * Creates a new parameters instance.
     *
     * @param host          the SSH server hostname
     * @param port              the SSH server port
     * @param username          the SSH server username
     * @param password          the SSH server password
     * @param keyFile           the private key file
     * @param keyFilePassword the private key passphrase
     */
    public SshParameters(String host, int port, String username, String password, File keyFile, String keyFilePassword) {
        this(host, port, username, password, keyFile != null ? keyPairFor(keyFile, keyFilePassword) : null);
    }

    /**
     * Creates a new parameters instance.
     * @param host a server host
     * @param port a server port
     * @param username an username
     * @param password a password
     * @param keyFile a user keys file
     */
    public SshParameters(String host, int port, String username, String password, File keyFile) {
        this(host, port, username, password, keyFile, "");
    }

    /**
     * Creates a new parameters instance.
     * @param host a server host
     * @param username an username
     * @param password a password
     * @param keyFile a user keys file
     */
    public SshParameters(String host, String username, String password, File keyFile) {
        this(host, DEFAULT_PORT, username, password, keyFile);
    }

    /**
     * Creates a new parameters instance.
     * @param host a server host
     * @param port a server port
     * @param username an username
     * @param keyFile a user keys file
     * @param keyFilePassword a keys file password
     */
    public SshParameters(String host, int port, String username, File keyFile, String keyFilePassword) {
        this(host, port, username, null, keyFile, keyFilePassword);
    }

    /**
     * Creates a new SshParameters instance.
     * For use in SSH authentication when private key is protected by a passphrase.
     *
     * @param host          the SSH server hostname
     * @param username          the SSH server username
     * @param keyFilePassword the private key passphrase
     * @param keyFile           the private key file
     */
    public SshParameters(String host, String username, File keyFile, String keyFilePassword) {
        this(host, DEFAULT_PORT, username, keyFile, keyFilePassword);
    }

    /**
     * Creates a new SshParameters instance.
     * For use in SSH authentication when private key is not protected by a password.
     *
     * @param host the SSH server hostname
     * @param port     the SSH server port
     * @param username the SSH server username
     * @param keyFile  the private key file
     */
    public SshParameters(String host, int port, String username, File keyFile) {
        this(host, port, username, keyFile, "");
    }

    /**
     * Creates a new SshParameters instance.
     * For use in SSH authentication when private key is not protected by a password.
     *
     * @param host the SSH server hostname
     * @param username the SSH server username
     * @param keyFile  the private key file
     */
    public SshParameters(String host, String username, File keyFile) {
        this(host, DEFAULT_PORT, username, keyFile);
    }

    /**
     * Creates a new SshParameters instance.
     *
     * @param host the SSH server hostname
     * @param port     the SSH server port
     * @param username the SSH server username
     * @param password the SSH server password
     */
    public SshParameters(String host, int port, String username, String password) {
        this(host, port, username, password, null, "");
    }

    /**
     * Creates a new SshParameters instance.
     *
     * @param host the SSH server hostname
     * @param username the SSH server username
     * @param password the SSH server password
     */
    public SshParameters(String host, String username, String password) {
        this(host, DEFAULT_PORT, username, password);
    }

    /**
     * Copy constructor.
     *
     * @param that instance to copy
     */
    public SshParameters(SshParameters that) {
        this(that.proxyType,
             that.proxyHost,
             that.proxyPort,
             that.proxyUsername,
             that.proxyPassword,
             that.hostname,
             that.port,
             that.connectionTimeout,
             that.readingTimeout,
             that.socketTrafficClass,
             that.tcpNoDelay,
             that.hostKeyVerifier,
             that.username,
             that.password,
             that.keyPair,
             that.publicKeyAlgorithm,
             that.clientAuthentication);
    }

    /**
     * Gets the proxy type: SOCKS5, HTTP.
     *
     * @return The proxy type
     */
    public String getProxyType() {
        return this.proxyType;
    }

    /**
     * Sets the proxy type
     *
     * @param value The proxy type.
     */
    public void setProxyType(String value) {
        this.proxyType = value;
    }

    /**
     * Gets proxy hostname.
     *
     * @return The proxy hostname
     */
    public String getProxyHost() {
        return this.proxyHost;
    }

    /**
     * Sets the proxy hostname.
     *
     * @param valus The proxy hostname
     */
    public void setProxyHost(String valus) {
        this.proxyHost = valus;
    }

    /**
     * Gets the proxy port.
     *
     * @return The proxy port.
     */
    public int getProxyPort() {
        return this.proxyPort;
    }

    /**
     * Sets the proxy port.
     *
     * @param value The proxy port.
     */
    public void setProxyPort(int value) {
        this.proxyPort = value;
    }

    /**
     * Returns a proxy username.
     *
     * @return a proxy username
     */
    public String getProxyUsername() {
        return this.proxyUsername;
    }

    /**
     * Sets a proxy username.
     *
     * @param value a proxy username
     */
    public void setProxyUsername(String value) {
        this.proxyUsername = value;
    }

    /**
     * Gets the user authentication proxy.
     *
     * @return User authentication proxy.
     *
     * @deprecated use getProxyUsername()
     */
    public String getProxyUserId() {
        return this.proxyUsername;
    }

    /**
     * Sets user authentication proxy.
     *
     * @param value User authentication proxy
     *
     * @deprecated use setProxyUsername()
     */
    public void setProxyUserId(String value) {
        this.proxyUsername = value;
    }

    /**
     * Gets the proxy authentication password.
     *
     * @return Proxy authentication password.
     */
    public String getProxyPassword() {
        return this.proxyPassword;
    }

    /**
     * Sets the proxy authentication password.
     *
     * @param value Initializes the proxy authentication password.
     */
    public void setProxyPassword(String value) {
        this.proxyPassword = value;
    }

    /**
     * Returns a server host.
     *
     * @return a server host
     */
    public String getHostname() {
        return this.hostname;
    }

    /**
     * Sets a server host.
     *
     * @param value a server port
     */
    public void setHostname(String value) {
        assertNotNull(value);
        this.hostname = value;
    }

    /**
     * Gets SSH hostname.
     *
     * @return the SSH hostname
     *
     * @deprecated use getHostname()
     */
    public String getSshHostname() {
        return this.hostname;
    }

    /**
     * Sets SSH hostname.
     *
     * @param value the SSH hostname
     *
     * @deprecated use setHostname()
     */
    public void setSshHostname(String value) {
        assertNotNull(value);
        this.hostname = value;
    }

    /**
     * Returns a server port.
     *
     * @return a server port
     */
    public int getPort() {
        return this.port;
    }

    /**
     * Sets a server port.
     *
     * @param value a server port
     */
    public void setPort(int value) {
        this.port = value;
    }

    /**
     * Gets SSH port.
     * Default port is 22.
     *
     * @return the SSH port
     *
     * @deprecated use getPort()
     */
    public int getSshPort() {
        return this.port;
    }

    /**
     * Sets SSH port.
     * Default port is 22.
     *
     * @param value the SSH port
     *
     * @deprecated use setPort()
     */
    public void setSshPort(int value) {
        this.port = value;
    }

    /**
     * Returns connection timeout in milliseconds.
     *
     * @return connection timeout in milliseconds
     */
    public long getConnectionTimeout() {
        return this.connectionTimeout;
    }

    /**
     * Sets connection timeout.
     *
     * @param value connection timeout in milliseconds
     */
    public void setConnectionTimeout(long value) {
        this.connectionTimeout = value;
    }

    /**
     * Returns reading timeout in milliseconds.
     *
     * @return reading timeout in milliseconds
     */
    public long getReadingTimeout() {
        return this.readingTimeout;
    }

    /**
     * Sets reading timeout.
     *
     * @param value reading timeout in milliseconds
     */
    public void setReadingTimeout(long value) {
        this.readingTimeout = value;
    }

    /**
     * Get socket traffic class
     *
     * @return socketTrafficClass
     */
    public Integer getSocketTrafficClass() {
        return this.socketTrafficClass;
    }

    /**
     * Set socket traffic class.
     *
     * @param value
     */
    public void setSocketTrafficClass(Integer value) {
        this.socketTrafficClass = value;
    }

    /**
     * Get TCP no delay.
     *
     * @return tcpNoDelay
     */
    public Boolean getTcpNoDelay() {
        return this.tcpNoDelay;
    }

    /**
     * Set TCP no delay.
     *
     * @param value
     */
    public void setTcpNoDelay(Boolean value) {
        this.tcpNoDelay = value;
    }

    /**
     * Returns current host key verifier.
     *
     * @return current host key verifier
     */
    public HostKeyVerificationService getHostKeyVerifier() {
        return this.hostKeyVerifier;
    }

    /**
     * Setups new host key verifier.
     *
     * @param hostKeyVerifier new host key verifier
     */
    public void setHostKeyVerifier(HostKeyVerificationService hostKeyVerifier) {
        assertNotNull(hostKeyVerifier);
        this.hostKeyVerifier = hostKeyVerifier;
    }

    /**
     * Returns an username.
     *
     * @return an username
     */
    public String getUsername() {
        return this.username;
    }

    /**
     * Sets an username.
     *
     * @param value an username
     */
    public void setUsername(String value) {
        assertNotNull(value);
        this.username = value;
    }

    /**
     * Gets SSH username.
     *
     * @return the SSH username
     *
     * @deprecated use getUsername()
     */
    public String getSshUsername() {
        return this.username;
    }

    /**
     * Sets SSH username.
     *
     * @param value the SSH username
     *
     * @deprecated use setUsername()
     */
    public void setSshUsername(String value) {
        assertNotNull(value);
        this.username = value;
    }

    /**
     * Returns an user password.
     *
     * @return a password
     */
    public String getPassword() {
        return this.password;
    }

    /**
     * Sets an user password.
     *
     * @param value a password
     */
    public void setPassword(String value) {
        this.password = value;
    }

    /**
     * Gets SSH password.
     *
     * @return the SSH account password or private key passphrase is public-key authentication is used
     *
     * @deprecated use getPassword()
     */
    public String getSshPassword() {
        return this.password;
    }

    /**
     * Sets SSH password.
     *
     * @param value the SSH account password or private key passphrase if public-key authentication is used
     *
     * @deprecated use setPassword()
     */
    public void setSshPassword(String value) {
        this.password = value;
    }

    /**
     * Returns user key pair.
     *
     * @return user key pair
     */
    public KeyPair getKeyPair() {
        return this.keyPair;
    }

    /**
     * Sets user key pair.
     *
     * @param value user key pair
     */
    public void setKeyPair(KeyPair value) {
        this.keyPair = value;
    }

    /**
     * Sets key pair to be used when authenticating with SSH Server.
     *
     * @param keyFile   private key file
     * @param filePassword file password
     */
    public void setPrivateKey(File keyFile, String filePassword) {
        this.keyPair = keyPairFor(keyFile, filePassword);
    }

    /**
     * Sets key pair to be used when authenticating with SSH Server.
     *
     * @param keyFile private key file
     */
    public void setPrivateKey(File keyFile) {
        setPrivateKey(keyFile, "");
    }

    /**
     * Sets key pair to be used when authenticating with SSH Server.
     *
     * @param serializedKeyData the serialized key data
     * @param password          the private key password
     */
    public void setPrivateKey(String serializedKeyData, String password) {
        try {
            this.keyPair = KEY_ASSEMBLER.restoreKeyPair(serializedKeyData, password);
        } catch (Exception e) {
            throw new IllegalArgumentException("Unsupported key format.", e);
        }
    }

    /**
     * Sets key pair to be used when authenticating with SSH Server.
     *
     * @param serializedKeyData the serialized key data
     */
    public void setPrivateKey(String serializedKeyData) {
        setPrivateKey(serializedKeyData, "");
    }

    public PublicKeyAlgorithm getPublicKeyAlgorithm() {
        return this.publicKeyAlgorithm;
    }

    public void setPublicKeyAlgorithm(PublicKeyAlgorithm value) {
        this.publicKeyAlgorithm = value;
    }

    /**
     * Returns client authentication operation.
     *
     * @return client authentication operation
     */
    public ClientAuthentication getClientAuthentication() {
        return this.clientAuthentication;
    }

    /**
     * Sets client authentication operation.
     *
     * @param value client authentication operation
     */
    public void setClientAuthentication(ClientAuthentication value) {
        this.clientAuthentication = value;
    }

    @Override
    public String toString() {
        return "SshParameters {"
               + "proxyType='" + this.proxyType + '\''
               + ", proxyHost='" + this.proxyHost + '\''
               + ", proxyPort=" + this.proxyPort
               + ", proxyUsername='" + this.proxyUsername + '\''
               + ", proxyPassword='" + (this.proxyPassword != null ? "*****" : "null") + '\''
               + ", hostname='" + this.hostname + '\''
               + ", port=" + this.port
               + ", connectionTimeout=" + this.connectionTimeout
               + ", readingTimeout=" + this.readingTimeout
               + ", socketTrafficClass=" + this.socketTrafficClass
               + ", tcpNoDelay=" + this.tcpNoDelay
               + ", hostKeyVerifier=" + this.hostKeyVerifier
               + ", username='" + this.username + '\''
               + ", password='" + (this.password != null ? "*****" : "null") + '\''
               + ", keyPair=" + this.keyPair
               + ", clientAuthentication=" + this.clientAuthentication
               + '}';
    }
}
