NONBLOCK_READ_TIMEOUT_MS = 1500 DEFAULT_SHELL_READ_BLOCK_SIZE = 1024 SSH_OK    = 0 SSH_ERROR = -1 SSH_AGAIN = -2 class SshError(SshException):
    pass class SshNonblockingTryAgainException(SshException):
    pass class SshNoDataReceivedException(SshException):
    pass class SshTimeoutException(SshException):
    pass def sync(cb):
    """A function that will repeatedly invoke a callback until it doesn't 
    return a try-again error.
    """

    while 1:
        try:
            cb()
        except SshNonblockingTryAgainException:
            pass
        else:
            break 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 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)
import logging
from ctypes import c_char_p, c_void_p, cast, c_uint32, c_int, \
                   create_string_buffer
from time import time
from pysecure.config import NONBLOCK_READ_TIMEOUT_MS, \
                            DEFAULT_SHELL_READ_BLOCK_SIZE
from pysecure.constants.ssh import SSH_OK, SSH_ERROR, SSH_AGAIN
from pysecure.exceptions import SshError, SshNonblockingTryAgainException, \
                                SshNoDataReceivedException, SshTimeoutException
from pysecure.utility import sync, bytify, stringify
from pysecure.calls.channeli import c_ssh_channel_new, \
                                    c_ssh_channel_open_forward, \
                                    c_ssh_channel_write, c_ssh_channel_free, \
                                    c_ssh_channel_read, \
                                    c_ssh_channel_send_eof, \
                                    c_ssh_channel_is_open, \
                                    c_ssh_channel_open_session, \
                                    c_ssh_channel_request_exec, \
                                    c_ssh_channel_request_shell, \
                                    c_ssh_channel_request_pty, \
                                    c_ssh_channel_change_pty_size, \
                                    c_ssh_channel_is_eof, \
                                    c_ssh_channel_read_nonblocking, \
                                    c_ssh_channel_request_env, \
                                    c_ssh_channel_get_session, \
                                    c_ssh_channel_accept_x11, \
                                    c_ssh_channel_request_x11
from pysecure.error import ssh_get_error, ssh_get_error_code


                                    

def _ssh_channel_new(ssh_session_int):
    logging.debug("Opening channel on session.")

    result = c_ssh_channel_new(ssh_session_int)
    if result is None:
        ssh_session_int = _ssh_channel_get_session(ssh_channel_int)
        error = ssh_get_error(ssh_session_int)

        raise SshError("Could not open channel: %s" % (error))

    return result

def _ssh_channel_open_forward(ssh_channel_int, host_remote, port_remote, 
                              host_source, port_local):

    logging.debug("Requesting forward on channel.")

    result = c_ssh_channel_open_forward(ssh_channel_int, 
                                        c_char_p(bytify(host_remote)), 
                                        c_int(port_remote), 
                                        c_char_p(bytify(host_source)), 
                                        c_int(port_local))

    if result == SSH_AGAIN:
        raise SshNonblockingTryAgainException()
    elif result != SSH_OK:
        ssh_session_int = _ssh_channel_get_session(ssh_channel_int)
        error = ssh_get_error(ssh_session_int)

        raise SshError("Forward failed: %s" % (error))

def _ssh_channel_write(ssh_channel_int, data):
    data_len = len(data)
    sent_bytes = c_ssh_channel_write(ssh_channel_int, 
                                     cast(c_char_p(data), c_void_p), 
                                     c_uint32(data_len))

    if sent_bytes == SSH_ERROR:
        ssh_session_int = _ssh_channel_get_session(ssh_channel_int)
        error = ssh_get_error(ssh_session_int)

        raise SshError("Channel write failed: %s" % (error))
    elif sent_bytes != data_len:
        raise SshError("Channel write of (%d) bytes failed for length (%d) of "
                       "written data." % (data_len, sent_bytes))

def _ssh_channel_read(ssh_channel_int, count, is_stderr):
    """Do a read on a channel."""

    buffer_ = create_string_buffer(count)
    while 1:
        received_bytes = c_ssh_channel_read(ssh_channel_int, 
                                            cast(buffer_, c_void_p), 
                                            c_uint32(count),
                                            c_int(int(is_stderr)))

        if received_bytes == SSH_ERROR:
            ssh_session_int = _ssh_channel_get_session(ssh_channel_int)
            error = ssh_get_error(ssh_session_int)

            raise SshError("Channel read failed: %s" % (error))

        # BUG: We're not using the nonblocking variant, but this can still 
        # return SSH_AGAIN due to that call's broken dependencies.
# TODO: This call might return SSH_AGAIN, even though we should always be 
#       blocking. Reported as bug #115.
        elif received_bytes == SSH_AGAIN:
            continue

        else:
            break

# TODO: Where is the timeout configured for the read?
    return buffer_.raw[0:received_bytes]

def _ssh_channel_read_nonblocking(ssh_channel_int, count, is_stderr):
    buffer_ = create_string_buffer(count)
    received_bytes = c_ssh_channel_read_nonblocking(ssh_channel_int, 
                                                    cast(buffer_, c_void_p), 
                                                    c_uint32(count),
                                                    c_int(int(is_stderr)))

    if received_bytes == SSH_ERROR:
        ssh_session_int = _ssh_channel_get_session(ssh_channel_int)
        error = ssh_get_error(ssh_session_int)

        raise SshError("Channel read (non-blocking) failed: %s" % (error))

    return buffer_.raw[0:received_bytes]

def _ssh_channel_free(ssh_channel_int):
    logging.debug("Freeing channel (%d)." % (ssh_channel_int))

    c_ssh_channel_free(ssh_channel_int)

def _ssh_channel_send_eof(ssh_channel_int):
    result = c_ssh_channel_send_eof(ssh_channel_int)
    if result != SSH_OK:
        ssh_session_int = _ssh_channel_get_session(ssh_channel_int)
        error = ssh_get_error(ssh_session_int)

        raise SshError("Could not send EOF: %s" % (error))

def _ssh_channel_is_open(ssh_channel_int):
    result = c_ssh_channel_is_open(ssh_channel_int)
    return (result != 0)

def _ssh_channel_open_session(ssh_channel_int):
    logging.debug("Request channel open-session.")


    result = c_ssh_channel_open_session(ssh_channel_int)