class SshError(SshException):
    pass class SshLoginError(SshError):
    pass class SshHostKeyException(SshException):
    pass class SshNonblockingTryAgainException(SshException):
    pass class SshTimeoutException(SshException):
    pass DEFAULT_EXECUTE_READ_BLOCK_SIZE = 8192 class _CSftpAttributesStruct(Structure):
class CTimeval(Structure):
class _CSshKeyStruct(Structure):
    def __repr__(self):
    def is_regular(self):
    def is_directory(self):
    def is_symlink(self):
    def is_special(self):
    def is_unknown_type(self):
    def modified_time(self):
    def modified_time_dt(self): SSH_OK    = 0 SSH_ERROR = -1 SSH_AGAIN = -2 SSH_EOF   = -127 SSH_AUTH_ERROR   = -1 SSH_AUTH_DENIED  = 1 SSH_AUTH_PARTIAL = 2 SSH_AUTH_AGAIN   = 4 SSH_AUTH_SUCCESS = 0 SSH_SERVER_ERROR          = -1 SSH_SERVER_NOT_KNOWN      = 0 SSH_SERVER_KNOWN_OK       = 1 SSH_SERVER_KNOWN_CHANGED  = 2 SSH_SERVER_FOUND_OTHER    = 3 SSH_OPTIONS = { 'user':           (SSH_OPTIONS_USER, _OT_STRING),
                'host':           (SSH_OPTIONS_HOST, _OT_STRING),
                'verbosity':      (SSH_OPTIONS_LOG_VERBOSITY, _OT_UINT),
                'port':           (SSH_OPTIONS_PORT, _OT_UINT),
                'fd':             (SSH_OPTIONS_FD, _OT_INT),
                'ssh_dir':        (SSH_OPTIONS_SSH_DIR, _OT_STRING),
                'identity':       (SSH_OPTIONS_IDENTITY, _OT_STRING),
                'add_identity':   (SSH_OPTIONS_ADD_IDENTITY, None),
                'knownhosts':     (SSH_OPTIONS_KNOWNHOSTS, _OT_STRING),
                'timeout':        (SSH_OPTIONS_TIMEOUT, _OT_LONG),
                'timeout_usec':   (SSH_OPTIONS_TIMEOUT_USEC, _OT_LONG),
                'ssh1':           (SSH_OPTIONS_SSH1, _OT_BOOL),
                'ssh2':           (SSH_OPTIONS_SSH2, _OT_BOOL),
                'cipherscs':      (SSH_OPTIONS_CIPHERS_C_S, _OT_STRING),
                'cipherssc':      (SSH_OPTIONS_CIPHERS_S_C, _OT_STRING),
                'compresscs':     (SSH_OPTIONS_COMPRESSION_C_S, _OT_STRING),
                'compresssc':     (SSH_OPTIONS_COMPRESSION_S_C, _OT_STRING),
                'proxycmd':       (SSH_OPTIONS_PROXYCOMMAND, _OT_STRING),
                'bindaddr':       (SSH_OPTIONS_BINDADDR, _OT_STRING),
                'stricthostkeys': (SSH_OPTIONS_STRICTHOSTKEYCHECK, _OT_BOOL),
                'compression':    (SSH_OPTIONS_COMPRESSION, _OT_STRING),
                'compression_n':  (SSH_OPTIONS_COMPRESSION_LEVEL, _OT_INT) } SSH_SERVER_FILE_NOT_FOUND = 4 SSH_CLOSED        = 0x01 SSH_READ_PENDING  = 0x02 SSH_WRITE_PENDING = 0x04 SSH_CLOSED_ERROR  = 0x08 class SshChannel(object):
    def __init__(self, ssh_session, ssh_channel=None):
        self.__ssh_session_int = getattr(ssh_session, 
                                         'session_id', 
                                         ssh_session)

        self.__ssh_channel_int = getattr(ssh_channel, 
                                         'session_id', 
                                         ssh_channel)

    def __enter__(self):
        if self.__ssh_channel_int is None:
            self.__ssh_channel_int = _ssh_channel_new(self.__ssh_session_int)

        return self

    def __exit__(self, e_type, e_value, e_tb):
        # The documentation says that a "free" implies a "close", and that a 
        # "close" implies a "send eof". From a cursory glance, this seems
        # accurate.
        _ssh_channel_free(self.__ssh_channel_int)
        self.__ssh_channel_int = None
    
    def __del__(self):
        # The documentation says that a "free" implies a "close", and that a 
        # "close" implies a "send eof". From a cursory glance, this seems
        # accurate.
        if self.__ssh_channel_int is not None:
            _ssh_channel_free(self.__ssh_channel_int)

    def open_forward(self, host_remote, port_remote, host_source, port_local):
        _ssh_channel_open_forward(self.__ssh_channel_int, 
                                  host_remote, 
                                  port_remote, 
                                  host_source, 
                                  port_local)

    def write(self, data):
        _ssh_channel_write(self.__ssh_channel_int, data)

    def read(self, count, is_stderr=False):
        return _ssh_channel_read(self.__ssh_channel_int, count, is_stderr)

    def read_nonblocking(self, count, is_stderr=False):
        return _ssh_channel_read_nonblocking(self.__ssh_channel_int, 
                                             count, 
                                             is_stderr)

    def send_eof(self):
        _ssh_channel_send_eof(self.__ssh_channel_int)

    def is_open(self):
        return _ssh_channel_is_open(self.__ssh_channel_int)

    def open_session(self):
        _ssh_channel_open_session(self.__ssh_channel_int)

    def request_exec(self, cmd):
        """Execute a command. Note that this can only be done once, and may be 
        the only operation performed with the current channel.
        """

        return _ssh_channel_request_exec(self.__ssh_channel_int, cmd)

    def request_shell(self):
        """Activate shell services on the channel (for PTY emulation)."""

        _ssh_channel_request_shell(self.__ssh_channel_int)

    def request_pty(self):
        _ssh_channel_request_pty(self.__ssh_channel_int)

    def change_pty_size(self, col, row):
        _ssh_channel_change_pty_size(self.__ssh_channel_int, col, row)

    def is_eof(self):
        return _ssh_channel_is_eof(self.__ssh_channel_int)
    
    def request_env(self, name, value):
        return _ssh_channel_request_env(self.__ssh_channel_int, name, value)

    def accept_x11(self, timeout_ms):
        ssh_x11_channel_int = _ssh_channel_accept_x11(self.__ssh_channel_int, 
                                                      timeout_ms)

        return SshChannel(self.__ssh_session_int, ssh_x11_channel_int)

    def request_x11(screen_number=0, single_connection=False, protocol=None,
                    cookie=None):
        return _ssh_channel_request_x11(self.__ssh_channel_int, screen_number, 
                                        single_connection, protocol, cookie) def ssh_get_error(ssh_session_int):
    return c_ssh_get_error(ssh_session_int) def ssh_get_error_code(ssh_session_int):
    return c_ssh_get_error_code(ssh_session_int) def bytify(s):
    if issubclass(s.__class__, str):
        return s.encode('ascii')
    else:
        return s def stringify(s):
    if issubclass(s.__class__, (bytes, bytearray)):
        return s.decode('ascii')
    else:
        return s
import logging
from ctypes import c_char_p, c_void_p, c_ubyte, byref, cast, c_uint, \
                   c_int, c_long
from pysecure.exceptions import SshError, SshLoginError, SshHostKeyException, \
                                SshNonblockingTryAgainException, \
                                SshTimeoutException
from pysecure.config import DEFAULT_EXECUTE_READ_BLOCK_SIZE
from pysecure.types import c_ssh_key
from pysecure.constants.ssh import SSH_OK, SSH_ERROR, SSH_AGAIN, SSH_EOF, \
                                   \
                                   SSH_AUTH_ERROR, SSH_AUTH_DENIED, \
                                   SSH_AUTH_PARTIAL, SSH_AUTH_AGAIN, \
                                   SSH_AUTH_SUCCESS, \
                                   \
                                   SSH_SERVER_ERROR, SSH_SERVER_NOT_KNOWN, \
                                   SSH_SERVER_KNOWN_OK, \
                                   SSH_SERVER_KNOWN_CHANGED, \
                                   SSH_SERVER_FOUND_OTHER, SSH_OPTIONS, \
                                   SSH_SERVER_FILE_NOT_FOUND, \
                                   \
                                   SSH_CLOSED, \
                                   SSH_READ_PENDING, \
                                   SSH_WRITE_PENDING, \
                                   SSH_CLOSED_ERROR
from pysecure.calls.sshi import c_free, c_ssh_pki_import_privkey_file, \
                                c_ssh_write_knownhost, c_ssh_get_pubkey_hash, \
                                c_ssh_is_server_known, c_ssh_connect, \
                                c_ssh_disconnect, c_ssh_print_hexa, \
                                c_ssh_get_hexa, c_ssh_free, c_ssh_new, \
                                c_ssh_options_set, c_ssh_init, \
                                c_ssh_finalize, c_ssh_userauth_password, \
                                c_ssh_forward_listen, c_ssh_forward_accept, \
                                c_ssh_key_new, c_ssh_userauth_publickey, \
                                c_ssh_key_free, c_ssh_get_disconnect_message, \
                                c_ssh_get_issue_banner, \
                                c_ssh_get_openssh_version, c_ssh_get_status, \
                                c_ssh_get_version, c_ssh_get_serverbanner, \
                                c_ssh_disconnect, c_ssh_is_blocking, \
                                c_ssh_threads_get_noop, \
                                c_ssh_threads_set_callbacks, \
                                c_ssh_set_blocking 
from pysecure.adapters.channela import SshChannel
from pysecure.error import ssh_get_error, ssh_get_error_code
from pysecure.utility import bytify, stringify
                                          "accepting new hosts.")

            raise SshHostKeyException("An existing host-key was not found. "
                                      "Our policy is to deny new hosts.")

        if cb is not None:
            hk = repr(PublicKeyHash(ssh_session))
            allow_auth = cb(hk, allow_new)
            
            logging.debug("Host-key callback returned [%s] when no host-key "
                          "yet available." % (allow_auth))
            
            if allow_auth is False:
                raise SshHostKeyException("New host-key was failed by "
                                          "callback.")

        logging.warn("Recording host-key for server.")
        c_ssh_write_knownhost(ssh_session)
    elif result == SSH_SERVER_ERROR:
        raise SshHostKeyException("Host key: Server error.")
    else:
        raise SshHostKeyException("Host key: Failed (unexpected error).")

def _ssh_print_hexa(title, hash_, hlen):
    assert issubclass(title.__class__, str)

    c_ssh_print_hexa(c_char_p(bytify(title)), hash_, c_int(hlen))

def _ssh_get_hexa(hash_, hlen):
    hexa = c_ssh_get_hexa(hash_, c_int(hlen))
    if hexa is None:
        raise SshError("Could not build hex-string.")

    return hexa

def _ssh_write_knownhost(ssh_session):
    logging.debug("Updating known-hosts file.")

    result = c_ssh_write_knownhost(c_void_p(ssh_session))
    if result != SSH_OK:
        error = ssh_get_error(ssh_session)
        raise SshError("Could not update known-hosts file: %s" % (error))

def _check_auth_response(result):
    if result == SSH_AUTH_ERROR:
        raise SshLoginError("Login failed: Auth error.")
    elif result == SSH_AUTH_DENIED:
        raise SshLoginError("Login failed: Auth denied.")
    elif result == SSH_AUTH_PARTIAL:
        raise SshLoginError("Login failed: Auth partial.")
    elif result == SSH_AUTH_AGAIN:
        raise SshLoginError("Login failed: Auth again.")
    elif result != SSH_AUTH_SUCCESS:
        raise SshLoginError("Login failed (unexpected error).")

def _ssh_userauth_password(ssh_session, username, password):
    if username is not None:
        assert issubclass(username.__class__, str)
    
    assert issubclass(password.__class__, str)

    logging.debug("Authenticating with a password for user [%s]." % (username))
    
    result = c_ssh_userauth_password(c_void_p(ssh_session), \
                                     c_char_p(bytify(username)), \
                                     c_char_p(bytify(password)))

    _check_auth_response(result)

def _ssh_init():
    result = c_ssh_init()
    if result < 0:
        raise SshError("Could not initialize SSH.")

def _ssh_finalize():
    result = c_ssh_finalize()
    if result < 0:
        raise SshError("Could not finalize SSH.")

def _ssh_forward_listen(ssh_session, address, port):
    if address is not None:
        assert issubclass(address.__class__, str)
        address = bytify(address)

    bound_port = c_int()
# BUG: Currently always returns SSH_AGAIN in 0.6.0 . Registered as bug #126.
    result = c_ssh_forward_listen(ssh_session, 
                                  address, 
                                  port, 
                                  byref(bound_port))

    if result == SSH_AGAIN:
        raise SshNonblockingTryAgainException()
    elif result != SSH_OK:
        error = ssh_get_error(ssh_session)
        raise SshError("Forward-listen failed: %s" % (error))

    return bound_port.value

def _ssh_forward_accept(ssh_session, timeout_ms):
    """Waiting for an incoming connection from a reverse forwarded port. Note
    that this results in a kernel block until a connection is received.
    """

    ssh_channel = c_ssh_forward_accept(c_void_p(ssh_session), 
                                       c_int(timeout_ms))

    if ssh_channel is None:
        raise SshTimeoutException()

    return ssh_channel

def _ssh_key_new():
    key = c_ssh_key_new()
    if key is None:
        raise SshError("Could not create empty key.")

    return key

def _ssh_userauth_publickey(ssh_session, priv_key):

    result = c_ssh_userauth_publickey(c_void_p(ssh_session), 