<commit_before>/*
 * channels.c - SSH channel functions
 *
 * This file is part of the SSH Library
 *
 * Copyright (c) 2003-2008 by <NAME>
 * Copyright (c) 2009      by <NAME> <<EMAIL>>
 *
 * The SSH Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * The SSH Library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SSH Library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

#ifndef _WIN32
#include <arpa/inet.h>
#endif

#include "libssh/priv.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/socket.h"
#include "libssh/channels.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/messages.h"
#if WITH_SERVER
#include "libssh/server.h"
#endif

#define WINDOWBASE 128000
#define WINDOWLIMIT (WINDOWBASE/2)

/*
 * All implementations MUST be able to process packets with an
 * uncompressed payload length of 32768 bytes or less and a total packet
 * size of 35000 bytes or less.
 */
#define CHANNEL_MAX_PACKET 32768
#define CHANNEL_INITIAL_WINDOW 64000

/**
 * @defgroup libssh_channel The SSH channel functions
 * @ingroup libssh
 *
 * Functions that manage a SSH channel.
 *
 * @{
 */

static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet);

/**
 * @brief Allocate a new channel.
 *
 * @param[in]  session  The ssh session to use.
 *
 * @return              A pointer to a newly allocated channel, NULL on error.
 */
ssh_channel ssh_channel_new(ssh_session session) {
  ssh_channel channel = NULL;

  if(session == NULL) {
      return NULL;
  }

  channel = malloc(sizeof(struct ssh_channel_struct));
  if (channel == NULL) {
    ssh_set_error_oom(session);
    return NULL;
  }
  memset(channel,0,sizeof(struct ssh_channel_struct));

  channel->stdout_buffer = ssh_buffer_new();
  if (channel->stdout_buffer == NULL) {
    ssh_set_error_oom(session);
    SAFE_FREE(channel);
    return NULL;
  }

  channel->stderr_buffer = ssh_buffer_new();
  if (channel->stderr_buffer == NULL) {
    ssh_set_error_oom(session);
    ssh_buffer_free(channel->stdout_buffer);
    SAFE_FREE(channel);
    return NULL;
  }

  channel->session = session;
  channel->version = session->version;
  channel->exit_status = -1;

  if(session->channels == NULL) {
    session->channels = channel;
    channel->next = channel->prev = channel;
    return channel;
  }
  channel->next = session->channels;
  channel->prev = session->channels->prev;
  channel->next->prev = channel;
  channel->prev->next = channel;

  return channel;
}

/**
 * @internal
 *
 * @brief Create a new channel identifier.
 *
 * @param[in]  session  The SSH session to use.
 *
 * @return              The new channel identifier.
 */
uint32_t ssh_channel_new_id(ssh_session session) {
  return ++(session->maxchannel);
}

/**
 * @internal
 *
 * @brief Handle a SSH_PACKET_CHANNEL_OPEN_CONFIRMATION packet.
 *
 * Constructs the channel object.
 */
SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
  uint32_t channelid=0;
  uint32_t tmp;
  ssh_channel channel;
  (void)type;
  (void)user;

  ssh_log(session,SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION");

  buffer_get_u32(packet, &channelid);
  channelid=ntohl(channelid);
  channel=ssh_channel_from_local(session,channelid);
  if(channel==NULL){
    ssh_set_error(session, SSH_FATAL,
        "Unknown channel id %lu",
        (long unsigned int) channelid);
    /* TODO: Set error marking in channel object */

    return SSH_PACKET_USED;
  }

  buffer_get_u32(packet, &tmp);
  channel->remote_channel = ntohl(tmp);

  buffer_get_u32(packet, &tmp);
  channel->remote_window = ntohl(tmp);

  buffer_get_u32(packet,&tmp);
  channel->remote_maxpacket=ntohl(tmp);

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
      channel->local_channel,
      channel->remote_channel);
  ssh_log(session, SSH_LOG_PROTOCOL,
      "Remote window : %lu, maxpacket : %lu",
      (long unsigned int) channel->remote_window,
      (long unsigned int) channel->remote_maxpacket);

  channel->state = SSH_CHANNEL_STATE_OPEN;

  return SSH_PACKET_USED;
}

/**
 * @internal
 *
 * @brief Handle a SSH_CHANNEL_OPEN_FAILURE and set the state of the channel.
 */
SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){

  ssh_channel channel;
  ssh_string error_s;
  char *error = NULL;
  uint32_t code;
  (void)user;
  (void)type;
  channel=channel_from_msg(session,packet);
  if(channel==NULL){
    ssh_log(session,SSH_LOG_RARE,"Invalid channel in packet");
    return SSH_PACKET_USED;
  }
  buffer_get_u32(packet, &code);

  error_s = buffer_get_ssh_string(packet);
  if(error_s != NULL)
    error = ssh_string_to_char(error_s);
  ssh_string_free(error_s);
  if (error == NULL) {
    ssh_set_error_oom(session);
    return SSH_PACKET_USED;
  }

  ssh_set_error(session, SSH_REQUEST_DENIED,
      "Channel opening failure: channel %u error (%lu) %s",
      channel->local_channel,
      (long unsigned int) ntohl(code),
      error);
  SAFE_FREE(error);
  channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
  return SSH_PACKET_USED;
}

/**
 * @internal
 *
 * @brief Open a channel by sending a SSH_OPEN_CHANNEL message and
 *        wait for the reply.
 *
 * @param[in]  channel  The current channel.
 *
 * @param[in]  type_c   A C string describing the kind of channel (e.g. "exec").
 *
 * @param[in]  window   The receiving window of the channel. The window is the
 *                      maximum size of data that can stay in buffers and
 *                      network.
 *
 * @param[in]  maxpacket The maximum packet size allowed (like MTU).
 *
 * @param[in]  payload   The buffer containing additional payload for the query.
 */
static int channel_open(ssh_channel channel, const char *type_c, int window,
    int maxpacket, ssh_buffer payload) {
  ssh_session session = channel->session;
  ssh_string type = NULL;
  int err=SSH_ERROR;


  channel->local_channel = ssh_channel_new_id(session);
  channel->local_maxpacket = maxpacket;
  channel->local_window = window;

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Creating a channel %d with %d window and %d max packet",
      channel->local_channel, window, maxpacket);

  type = ssh_string_from_char(type_c);
  if (type == NULL) {
    ssh_set_error_oom(session);

    return err;
  }

  if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN) < 0 ||
      buffer_add_ssh_string(session->out_buffer,type) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->local_channel)) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->local_window)) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->local_maxpacket)) < 0) {
    ssh_set_error_oom(session);
    ssh_string_free(type);

    return err;
  }

  ssh_string_free(type);

  if (payload != NULL) {
    if (buffer_add_buffer(session->out_buffer, payload) < 0) {
      ssh_set_error_oom(session);

      return err;
    }
  }

  if (packet_send(session) == SSH_ERROR) {

    return err;
  }

  ssh_log(session, SSH_LOG_PACKET,
      "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d",
      type_c, channel->local_channel);

  /* Todo: fix this into a correct loop */
  /* wait until channel is opened by server */
  while(channel->state == SSH_CHANNEL_STATE_NOT_OPEN){
      err = ssh_handle_packets(session, -2);
      if (err != SSH_OK) {
          break;
      }
      if (session->session_state == SSH_SESSION_STATE_ERROR) {
          err = SSH_ERROR;
          break;
      }
  }
  if(channel->state == SSH_CHANNEL_STATE_OPEN)
    err=SSH_OK;

  return err;
}

/* return channel with corresponding local id, or NULL if not found */
ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
    ssh_channel initchan = session->channels;
    ssh_channel channel = initchan;

    for (;;) {
        if (channel == NULL) {
            return NULL;
        }
        if (channel->local_channel == id) {
            return channel;
        }
        if (channel->next == initchan) {
            return NULL;
        }
        channel = channel->next;
    }

    return NULL;
}

/**
 * @internal
 * @brief grows the local window and send a packet to the other party
 * @param session SSH session
 * @param channel SSH channel
 * @param minimumsize The minimum acceptable size for the new window.
 */
static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) {
  uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE;


  if(new_window <= channel->local_window){
    ssh_log(session,SSH_LOG_PROTOCOL,
        "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)",
        channel->local_channel, channel->remote_channel, new_window,
        channel->local_window);

    return SSH_OK;
  }
  /* WINDOW_ADJUST packet needs a relative increment rather than an absolute
   * value, so we give here the missing bytes needed to reach new_window
   */
  if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_WINDOW_ADJUST) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(new_window - channel->local_window)) < 0) {
    ssh_set_error_oom(session);
    goto error;
  }

  if (packet_send(session) == SSH_ERROR) {
    goto error;
  }

  ssh_log(session, SSH_LOG_PROTOCOL,
      "growing window (channel %d:%d) to %d bytes",
      channel->local_channel,
      channel->remote_channel,
      new_window);

  channel->local_window = new_window;


  return SSH_OK;
error:
  buffer_reinit(session->out_buffer);


  return SSH_ERROR;
}

/**
 * @internal
 *
 * @brief Parse a channel-related packet to resolve it to a ssh_channel.
 *
 * This works on SSH1 sessions too.
 *
 * @param[in]  session  The current SSH session.
 *
 * @param[in]  packet   The buffer to parse packet from. The read pointer will
 *                      be moved after the call.
 *
 * @returns             The related ssh_channel, or NULL if the channel is
 *                      unknown or the packet is invalid.
 */
static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
  ssh_channel channel;
  uint32_t chan;
#ifdef WITH_SSH1
  /* With SSH1, the channel is always the first one */
  if(session->version==1)
    return session->channels;
#endif
  if (buffer_get_u32(packet, &chan) != sizeof(uint32_t)) {
    ssh_set_error(session, SSH_FATAL,
        "Getting channel from message: short read");
    return NULL;
  }

  channel = ssh_channel_from_local(session, ntohl(chan));
  if (channel == NULL) {
    ssh_set_error(session, SSH_FATAL,
        "Server specified invalid channel %lu",
        (long unsigned int) ntohl(chan));
  }

  return channel;
}

SSH_PACKET_CALLBACK(channel_rcv_change_window) {
  ssh_channel channel;
  uint32_t bytes;
  int rc;
  (void)user;
  (void)type;


  channel = channel_from_msg(session,packet);
  if (channel == NULL) {
    ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
  }

  rc = buffer_get_u32(packet, &bytes);
  if (channel == NULL || rc != sizeof(uint32_t)) {
    ssh_log(session, SSH_LOG_PACKET,
        "Error getting a window adjust message: invalid packet");

    return SSH_PACKET_USED;
  }

  bytes = ntohl(bytes);
  ssh_log(session, SSH_LOG_PROTOCOL,
      "Adding %d bytes to channel (%d:%d) (from %d bytes)",
      bytes,
      channel->local_channel,
      channel->remote_channel,
      channel->remote_window);

  channel->remote_window += bytes;


  return SSH_PACKET_USED;
}

/* is_stderr is set to 1 if the data are extended, ie stderr */
SSH_PACKET_CALLBACK(channel_rcv_data){
  ssh_channel channel;
  ssh_string str;
  ssh_buffer buf;
  size_t len;
  int is_stderr;
  int rest;
  (void)user;

  if(type==SSH2_MSG_CHANNEL_DATA)
	  is_stderr=0;
  else
	  is_stderr=1;

  channel = channel_from_msg(session,packet);
  if (channel == NULL) {
    ssh_log(session, SSH_LOG_FUNCTIONS,
        "%s", ssh_get_error(session));

    return SSH_PACKET_USED;
  }

  if (is_stderr) {
    uint32_t ignore;
    /* uint32 data type code. we can ignore it */
    buffer_get_u32(packet, &ignore);
  }

  str = buffer_get_ssh_string(packet);
  if (str == NULL) {
    ssh_log(session, SSH_LOG_PACKET, "Invalid data packet!");

    return SSH_PACKET_USED;
  }
  len = ssh_string_len(str);

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)",
      len,
      is_stderr,
      channel->local_window,
      channel->remote_window);

  /* What shall we do in this case? Let's accept it anyway */
  if (len > channel->local_window) {
    ssh_log(session, SSH_LOG_RARE,
        "Data packet too big for our window(%" PRIdS " vs %d)",
        len,
        channel->local_window);
  }

  if (channel_default_bufferize(channel, ssh_string_data(str), len,
        is_stderr) < 0) {
    ssh_string_free(str);

    return SSH_PACKET_USED;
  }

  if (len <= channel->local_window) {
    channel->local_window -= len;
  } else {
    channel->local_window = 0; /* buggy remote */
  }

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Channel windows are now (local win=%d remote win=%d)",
      channel->local_window,
      channel->remote_window);

  ssh_string_free(str);

  if(ssh_callbacks_exists(channel->callbacks, channel_data_function)) {
      if(is_stderr) {
        buf = channel->stderr_buffer;
      } else {
        buf = channel->stdout_buffer;
      }
      rest = channel->callbacks->channel_data_function(channel->session,
                                                channel,
                                                buffer_get_rest(buf),
                                                buffer_get_rest_len(buf),
                                                is_stderr,
                                                channel->callbacks->userdata);
      if(rest > 0) {
        buffer_pass_bytes(buf, rest);
      }
      if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) {
        if (grow_window(session, channel, 0) < 0) {

          return -1;
        }
      }
  }


  return SSH_PACKET_USED;
}

SSH_PACKET_CALLBACK(channel_rcv_eof) {
  ssh_channel channel;
  (void)user;
  (void)type;


  channel = channel_from_msg(session,packet);
  if (channel == NULL) {
    ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));

    return SSH_PACKET_USED;
  }

  ssh_log(session, SSH_LOG_PACKET,
      "Received eof on channel (%d:%d)",
      channel->local_channel,
      channel->remote_channel);
  /* channel->remote_window = 0; */
  channel->remote_eof = 1;

  if(ssh_callbacks_exists(channel->callbacks, channel_eof_function)) {
      channel->callbacks->channel_eof_function(channel->session,
                                               channel,
                                               channel->callbacks->userdata);
  }


  return SSH_PACKET_USED;
}

SSH_PACKET_CALLBACK(channel_rcv_close) {
	ssh_channel channel;
	(void)user;
	(void)type;


	channel = channel_from_msg(session,packet);
	if (channel == NULL) {
		ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));

		return SSH_PACKET_USED;
	}

	ssh_log(session, SSH_LOG_PACKET,
			"Received close on channel (%d:%d)",
			channel->local_channel,
			channel->remote_channel);

	if ((channel->stdout_buffer &&
			buffer_get_rest_len(channel->stdout_buffer) > 0) ||
			(channel->stderr_buffer &&
					buffer_get_rest_len(channel->stderr_buffer) > 0)) {
		channel->delayed_close = 1;
	} else {
		channel->state = SSH_CHANNEL_STATE_CLOSED;
	}

	if (channel->remote_eof == 0) {
		ssh_log(session, SSH_LOG_PACKET,
				"Remote host not polite enough to send an eof before close");
	}
	channel->remote_eof = 1;
	/*
	 * The remote eof doesn't break things if there was still data into read
	 * buffer because the eof is ignored until the buffer is empty.
	 */

    if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) {
        channel->callbacks->channel_close_function(channel->session,
                                                 channel,
                                                 channel->callbacks->userdata);
    }


	return SSH_PACKET_USED;
}

SSH_PACKET_CALLBACK(channel_rcv_request) {
	ssh_channel channel;
	ssh_string request_s;
	char *request;
	uint32_t status;
	(void)user;
	(void)type;



	channel = channel_from_msg(session,packet);
	if (channel == NULL) {
		ssh_log(session, SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session));

		return SSH_PACKET_USED;
	}

	request_s = buffer_get_ssh_string(packet);
	if (request_s == NULL) {
		ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");

		return SSH_PACKET_USED;
	}

	request = ssh_string_to_char(request_s);
	ssh_string_free(request_s);
	if (request == NULL) {

		return SSH_PACKET_USED;
	}

	buffer_get_u8(packet, (uint8_t *) &status);

	if (strcmp(request,"exit-status") == 0) {
		SAFE_FREE(request);
		buffer_get_u32(packet, &status);
		channel->exit_status = ntohl(status);
		ssh_log(session, SSH_LOG_PACKET, "received exit-status %d", channel->exit_status);

        if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) {
            channel->callbacks->channel_exit_status_function(channel->session,
                                                     channel,
                                                     channel->exit_status,
                                                     channel->callbacks->userdata);
        }


		return SSH_PACKET_USED;
	}

	if (strcmp(request,"signal") == 0) {
		ssh_string signal_str;
        char *sig;

		SAFE_FREE(request);
		ssh_log(session, SSH_LOG_PACKET, "received signal");

		signal_str = buffer_get_ssh_string(packet);
		if (signal_str == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");

			return SSH_PACKET_USED;
		}

		sig = ssh_string_to_char(signal_str);
		ssh_string_free(signal_str);
		if (sig == NULL) {

			return SSH_PACKET_USED;
		}


		ssh_log(session, SSH_LOG_PACKET,
				"Remote connection sent a signal SIG %s", sig);
        if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) {
            channel->callbacks->channel_signal_function(channel->session,
                                                     channel,
                                                     sig,
                                                     channel->callbacks->userdata);
        }


		return SSH_PACKET_USED;
	}

	if (strcmp(request, "exit-signal") == 0) {
		const char *core = "(core dumped)";
		ssh_string tmp;
		char *sig;
		char *errmsg = NULL;
		char *lang = NULL;
		uint8_t i;

		SAFE_FREE(request);

		tmp = buffer_get_ssh_string(packet);
		if (tmp == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");

			return SSH_PACKET_USED;
		}

		sig = ssh_string_to_char(tmp);
		ssh_string_free(tmp);
		if (sig == NULL) {

			return SSH_PACKET_USED;
		}

		buffer_get_u8(packet, &i);
		if (i == 0) {
			core = "";
		}

		tmp = buffer_get_ssh_string(packet);
		if (tmp == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		errmsg = ssh_string_to_char(tmp);
		ssh_string_free(tmp);
		if (errmsg == NULL) {
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		tmp = buffer_get_ssh_string(packet);
		if (tmp == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
            SAFE_FREE(errmsg);
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		lang = ssh_string_to_char(tmp);
		ssh_string_free(tmp);
		if (lang == NULL) {
            SAFE_FREE(errmsg);
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		ssh_log(session, SSH_LOG_PACKET,
				"Remote connection closed by signal SIG %s %s", sig, core);
        if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) {
            channel->callbacks->channel_exit_signal_function(channel->session,
                                                     channel,
                                                     sig, i, errmsg, lang,
                                                     channel->callbacks->userdata);
        }

        SAFE_FREE(lang);
        SAFE_FREE(errmsg);
		SAFE_FREE(sig);


		return SSH_PACKET_USED;
	}
	if(strcmp(request,"<EMAIL>")==0){
	  SAFE_FREE(request);
	  ssh_log(session, SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive");
	  buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_FAILURE);
	  buffer_add_u32(session->out_buffer, htonl(channel->remote_channel));
	  packet_send(session);

	  return SSH_PACKET_USED;
	}

	/* If we are here, that means we have a request that is not in the understood
	 * client requests. That means we need to create a ssh message to be passed
	 * to the user code handling ssh messages
	 */
	ssh_message_handle_channel_request(session,channel,packet,request,status);

	SAFE_FREE(request);


	return SSH_PACKET_USED;
}

/*
 * When data has been received from the ssh server, it can be applied to the
 * known user function, with help of the callback, or inserted here
 *
 * FIXME is the window changed?
 */
int channel_default_bufferize(ssh_channel channel, void *data, int len,
    int is_stderr) {
  ssh_session session;

  if(channel == NULL) {
      return -1;
  }

  session = channel->session;

  if(data == NULL) {
      ssh_set_error_invalid(session, __FUNCTION__);
      return -1;
  }

  ssh_log(session, SSH_LOG_RARE,
      "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr);
  if (is_stderr == 0) {
    /* stdout */
    if (channel->stdout_buffer == NULL) {
      channel->stdout_buffer = ssh_buffer_new();
      if (channel->stdout_buffer == NULL) {
        ssh_set_error_oom(session);
        return -1;
      }
    }

    if (buffer_add_data(channel->stdout_buffer, data, len) < 0) {
      ssh_set_error_oom(session);
      ssh_buffer_free(channel->stdout_buffer);
      channel->stdout_buffer = NULL;
      return -1;
    }
  } else {
    /* stderr */
    if (channel->stderr_buffer == NULL) {
      channel->stderr_buffer = ssh_buffer_new();
      if (channel->stderr_buffer == NULL) {
        ssh_set_error_oom(session);
        return -1;
      }
    }

    if (buffer_add_data(channel->stderr_buffer, data, len) < 0) {
      ssh_set_error_oom(session);
      ssh_buffer_free(channel->stderr_buffer);
      channel->stderr_buffer = NULL;
      return -1;
    }
  }

  return 0;
}

/**
 * @brief Open a session channel (suited for a shell, not TCP forwarding).
 *
 * @param[in]  channel  An allocated channel.
 *
 * @return              SSH_OK on success, SSH_ERROR if an error occured.
 *
 * @see channel_open_forward()
 * @see channel_request_env()
 * @see channel_request_shell()
 * @see channel_request_exec()
 */
int ssh_channel_open_session(ssh_channel channel) {
  if(channel == NULL) {
      return SSH_ERROR;
  }

#ifdef WITH_SSH1
  if (channel->session->version == 1) {<commit_msg>remove code dup<commit_after>/*
 * channels.c - SSH channel functions
 *
 * This file is part of the SSH Library
 *
 * Copyright (c) 2003-2008 by <NAME>
 * Copyright (c) 2009      by <NAME> <<EMAIL>>
 *
 * The SSH Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * The SSH Library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SSH Library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

#ifndef _WIN32
#include <arpa/inet.h>
#endif

#include "libssh/priv.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/socket.h"
#include "libssh/channels.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/messages.h"
#if WITH_SERVER
#include "libssh/server.h"
#endif

#define WINDOWBASE 128000
#define WINDOWLIMIT (WINDOWBASE/2)

/*
 * All implementations MUST be able to process packets with an
 * uncompressed payload length of 32768 bytes or less and a total packet
 * size of 35000 bytes or less.
 */
#define CHANNEL_MAX_PACKET 32768
#define CHANNEL_INITIAL_WINDOW 64000

/**
 * @defgroup libssh_channel The SSH channel functions
 * @ingroup libssh
 *
 * Functions that manage a SSH channel.
 *
 * @{
 */

static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet);

/**
 * @brief Allocate a new channel.
 *
 * @param[in]  session  The ssh session to use.
 *
 * @return              A pointer to a newly allocated channel, NULL on error.
 */
ssh_channel ssh_channel_new(ssh_session session) {
  ssh_channel channel = NULL;

  if(session == NULL) {
      return NULL;
  }

  channel = malloc(sizeof(struct ssh_channel_struct));
  if (channel == NULL) {
    ssh_set_error_oom(session);
    return NULL;
  }
  memset(channel,0,sizeof(struct ssh_channel_struct));

  channel->stdout_buffer = ssh_buffer_new();
  if (channel->stdout_buffer == NULL) {
    ssh_set_error_oom(session);
    SAFE_FREE(channel);
    return NULL;
  }

  channel->stderr_buffer = ssh_buffer_new();
  if (channel->stderr_buffer == NULL) {
    ssh_set_error_oom(session);
    ssh_buffer_free(channel->stdout_buffer);
    SAFE_FREE(channel);
    return NULL;
  }

  channel->session = session;
  channel->version = session->version;
  channel->exit_status = -1;

  if(session->channels == NULL) {
    session->channels = channel;
    channel->next = channel->prev = channel;
    return channel;
  }
  channel->next = session->channels;
  channel->prev = session->channels->prev;
  channel->next->prev = channel;
  channel->prev->next = channel;

  return channel;
}

/**
 * @internal
 *
 * @brief Create a new channel identifier.
 *
 * @param[in]  session  The SSH session to use.
 *
 * @return              The new channel identifier.
 */
uint32_t ssh_channel_new_id(ssh_session session) {
  return ++(session->maxchannel);
}

/**
 * @internal
 *
 * @brief Handle a SSH_PACKET_CHANNEL_OPEN_CONFIRMATION packet.
 *
 * Constructs the channel object.
 */
SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
  uint32_t channelid=0;
  uint32_t tmp;
  ssh_channel channel;
  (void)type;
  (void)user;

  ssh_log(session,SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION");

  buffer_get_u32(packet, &channelid);
  channelid=ntohl(channelid);
  channel=ssh_channel_from_local(session,channelid);
  if(channel==NULL){
    ssh_set_error(session, SSH_FATAL,
        "Unknown channel id %lu",
        (long unsigned int) channelid);
    /* TODO: Set error marking in channel object */

    return SSH_PACKET_USED;
  }

  buffer_get_u32(packet, &tmp);
  channel->remote_channel = ntohl(tmp);

  buffer_get_u32(packet, &tmp);
  channel->remote_window = ntohl(tmp);

  buffer_get_u32(packet,&tmp);
  channel->remote_maxpacket=ntohl(tmp);

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
      channel->local_channel,
      channel->remote_channel);
  ssh_log(session, SSH_LOG_PROTOCOL,
      "Remote window : %lu, maxpacket : %lu",
      (long unsigned int) channel->remote_window,
      (long unsigned int) channel->remote_maxpacket);

  channel->state = SSH_CHANNEL_STATE_OPEN;

  return SSH_PACKET_USED;
}

/**
 * @internal
 *
 * @brief Handle a SSH_CHANNEL_OPEN_FAILURE and set the state of the channel.
 */
SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){

  ssh_channel channel;
  ssh_string error_s;
  char *error = NULL;
  uint32_t code;
  (void)user;
  (void)type;
  channel=channel_from_msg(session,packet);
  if(channel==NULL){
    ssh_log(session,SSH_LOG_RARE,"Invalid channel in packet");
    return SSH_PACKET_USED;
  }
  buffer_get_u32(packet, &code);

  error_s = buffer_get_ssh_string(packet);
  if(error_s != NULL)
    error = ssh_string_to_char(error_s);
  ssh_string_free(error_s);
  if (error == NULL) {
    ssh_set_error_oom(session);
    return SSH_PACKET_USED;
  }

  ssh_set_error(session, SSH_REQUEST_DENIED,
      "Channel opening failure: channel %u error (%lu) %s",
      channel->local_channel,
      (long unsigned int) ntohl(code),
      error);
  SAFE_FREE(error);
  channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
  return SSH_PACKET_USED;
}

/**
 * @internal
 *
 * @brief Open a channel by sending a SSH_OPEN_CHANNEL message and
 *        wait for the reply.
 *
 * @param[in]  channel  The current channel.
 *
 * @param[in]  type_c   A C string describing the kind of channel (e.g. "exec").
 *
 * @param[in]  window   The receiving window of the channel. The window is the
 *                      maximum size of data that can stay in buffers and
 *                      network.
 *
 * @param[in]  maxpacket The maximum packet size allowed (like MTU).
 *
 * @param[in]  payload   The buffer containing additional payload for the query.
 */
static int channel_open(ssh_channel channel, const char *type_c, int window,
    int maxpacket, ssh_buffer payload) {
  ssh_session session = channel->session;
  ssh_string type = NULL;
  int err=SSH_ERROR;


  channel->local_channel = ssh_channel_new_id(session);
  channel->local_maxpacket = maxpacket;
  channel->local_window = window;

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Creating a channel %d with %d window and %d max packet",
      channel->local_channel, window, maxpacket);

  type = ssh_string_from_char(type_c);
  if (type == NULL) {
    ssh_set_error_oom(session);

    return err;
  }

  if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN) < 0 ||
      buffer_add_ssh_string(session->out_buffer,type) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->local_channel)) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->local_window)) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->local_maxpacket)) < 0) {
    ssh_set_error_oom(session);
    ssh_string_free(type);

    return err;
  }

  ssh_string_free(type);

  if (payload != NULL) {
    if (buffer_add_buffer(session->out_buffer, payload) < 0) {
      ssh_set_error_oom(session);

      return err;
    }
  }

  if (packet_send(session) == SSH_ERROR) {

    return err;
  }

  ssh_log(session, SSH_LOG_PACKET,
      "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d",
      type_c, channel->local_channel);

  /* Todo: fix this into a correct loop */
  /* wait until channel is opened by server */
  while(channel->state == SSH_CHANNEL_STATE_NOT_OPEN){
      err = ssh_handle_packets(session, -2);
      if (err != SSH_OK) {
          break;
      }
      if (session->session_state == SSH_SESSION_STATE_ERROR) {
          err = SSH_ERROR;
          break;
      }
  }
  if(channel->state == SSH_CHANNEL_STATE_OPEN)
    err=SSH_OK;

  return err;
}

/* return channel with corresponding local id, or NULL if not found */
ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
    ssh_channel initchan = session->channels;
    ssh_channel channel = initchan;

    for (;;) {
        if (channel == NULL) {
            return NULL;
        }
        if (channel->local_channel == id) {
            return channel;
        }
        if (channel->next == initchan) {
            return NULL;
        }
        channel = channel->next;
    }

    return NULL;
}

/**
 * @internal
 * @brief grows the local window and send a packet to the other party
 * @param session SSH session
 * @param channel SSH channel
 * @param minimumsize The minimum acceptable size for the new window.
 */
static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) {
  uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE;


  if(new_window <= channel->local_window){
    ssh_log(session,SSH_LOG_PROTOCOL,
        "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)",
        channel->local_channel, channel->remote_channel, new_window,
        channel->local_window);

    return SSH_OK;
  }
  /* WINDOW_ADJUST packet needs a relative increment rather than an absolute
   * value, so we give here the missing bytes needed to reach new_window
   */
  if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_WINDOW_ADJUST) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 ||
      buffer_add_u32(session->out_buffer, htonl(new_window - channel->local_window)) < 0) {
    ssh_set_error_oom(session);
    goto error;
  }

  if (packet_send(session) == SSH_ERROR) {
    goto error;
  }

  ssh_log(session, SSH_LOG_PROTOCOL,
      "growing window (channel %d:%d) to %d bytes",
      channel->local_channel,
      channel->remote_channel,
      new_window);

  channel->local_window = new_window;


  return SSH_OK;
error:
  buffer_reinit(session->out_buffer);


  return SSH_ERROR;
}

/**
 * @internal
 *
 * @brief Parse a channel-related packet to resolve it to a ssh_channel.
 *
 * This works on SSH1 sessions too.
 *
 * @param[in]  session  The current SSH session.
 *
 * @param[in]  packet   The buffer to parse packet from. The read pointer will
 *                      be moved after the call.
 *
 * @returns             The related ssh_channel, or NULL if the channel is
 *                      unknown or the packet is invalid.
 */
static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
  ssh_channel channel;
  uint32_t chan;
#ifdef WITH_SSH1
  /* With SSH1, the channel is always the first one */
  if(session->version==1)
    return session->channels;
#endif
  if (buffer_get_u32(packet, &chan) != sizeof(uint32_t)) {
    ssh_set_error(session, SSH_FATAL,
        "Getting channel from message: short read");
    return NULL;
  }

  channel = ssh_channel_from_local(session, ntohl(chan));
  if (channel == NULL) {
    ssh_set_error(session, SSH_FATAL,
        "Server specified invalid channel %lu",
        (long unsigned int) ntohl(chan));
  }

  return channel;
}

SSH_PACKET_CALLBACK(channel_rcv_change_window) {
  ssh_channel channel;
  uint32_t bytes;
  int rc;
  (void)user;
  (void)type;


  channel = channel_from_msg(session,packet);
  if (channel == NULL) {
    ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
  }

  rc = buffer_get_u32(packet, &bytes);
  if (channel == NULL || rc != sizeof(uint32_t)) {
    ssh_log(session, SSH_LOG_PACKET,
        "Error getting a window adjust message: invalid packet");

    return SSH_PACKET_USED;
  }

  bytes = ntohl(bytes);
  ssh_log(session, SSH_LOG_PROTOCOL,
      "Adding %d bytes to channel (%d:%d) (from %d bytes)",
      bytes,
      channel->local_channel,
      channel->remote_channel,
      channel->remote_window);

  channel->remote_window += bytes;


  return SSH_PACKET_USED;
}

/* is_stderr is set to 1 if the data are extended, ie stderr */
SSH_PACKET_CALLBACK(channel_rcv_data){
  ssh_channel channel;
  ssh_string str;
  ssh_buffer buf;
  size_t len;
  int is_stderr;
  int rest;
  (void)user;

  if(type==SSH2_MSG_CHANNEL_DATA)
	  is_stderr=0;
  else
	  is_stderr=1;

  channel = channel_from_msg(session,packet);
  if (channel == NULL) {
    ssh_log(session, SSH_LOG_FUNCTIONS,
        "%s", ssh_get_error(session));

    return SSH_PACKET_USED;
  }

  if (is_stderr) {
    uint32_t ignore;
    /* uint32 data type code. we can ignore it */
    buffer_get_u32(packet, &ignore);
  }

  str = buffer_get_ssh_string(packet);
  if (str == NULL) {
    ssh_log(session, SSH_LOG_PACKET, "Invalid data packet!");

    return SSH_PACKET_USED;
  }
  len = ssh_string_len(str);

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)",
      len,
      is_stderr,
      channel->local_window,
      channel->remote_window);

  /* What shall we do in this case? Let's accept it anyway */
  if (len > channel->local_window) {
    ssh_log(session, SSH_LOG_RARE,
        "Data packet too big for our window(%" PRIdS " vs %d)",
        len,
        channel->local_window);
  }

  if (channel_default_bufferize(channel, ssh_string_data(str), len,
        is_stderr) < 0) {
    ssh_string_free(str);

    return SSH_PACKET_USED;
  }

  if (len <= channel->local_window) {
    channel->local_window -= len;
  } else {
    channel->local_window = 0; /* buggy remote */
  }

  ssh_log(session, SSH_LOG_PROTOCOL,
      "Channel windows are now (local win=%d remote win=%d)",
      channel->local_window,
      channel->remote_window);

  ssh_string_free(str);

  if(ssh_callbacks_exists(channel->callbacks, channel_data_function)) {
      if(is_stderr) {
        buf = channel->stderr_buffer;
      } else {
        buf = channel->stdout_buffer;
      }
      rest = channel->callbacks->channel_data_function(channel->session,
                                                channel,
                                                buffer_get_rest(buf),
                                                buffer_get_rest_len(buf),
                                                is_stderr,
                                                channel->callbacks->userdata);
      if(rest > 0) {
        buffer_pass_bytes(buf, rest);
      }
      if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) {
        if (grow_window(session, channel, 0) < 0) {

          return -1;
        }
      }
  }


  return SSH_PACKET_USED;
}

SSH_PACKET_CALLBACK(channel_rcv_eof) {
  ssh_channel channel;
  (void)user;
  (void)type;


  channel = channel_from_msg(session,packet);
  if (channel == NULL) {
    ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));

    return SSH_PACKET_USED;
  }

  ssh_log(session, SSH_LOG_PACKET,
      "Received eof on channel (%d:%d)",
      channel->local_channel,
      channel->remote_channel);
  /* channel->remote_window = 0; */
  channel->remote_eof = 1;

  if(ssh_callbacks_exists(channel->callbacks, channel_eof_function)) {
      channel->callbacks->channel_eof_function(channel->session,
                                               channel,
                                               channel->callbacks->userdata);
  }


  return SSH_PACKET_USED;
}

SSH_PACKET_CALLBACK(channel_rcv_close) {
	ssh_channel channel;
	(void)user;
	(void)type;


	channel = channel_from_msg(session,packet);
	if (channel == NULL) {
		ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));

		return SSH_PACKET_USED;
	}

	ssh_log(session, SSH_LOG_PACKET,
			"Received close on channel (%d:%d)",
			channel->local_channel,
			channel->remote_channel);

	if ((channel->stdout_buffer &&
			buffer_get_rest_len(channel->stdout_buffer) > 0) ||
			(channel->stderr_buffer &&
					buffer_get_rest_len(channel->stderr_buffer) > 0)) {
		channel->delayed_close = 1;
	} else {
		channel->state = SSH_CHANNEL_STATE_CLOSED;
	}

	if (channel->remote_eof == 0) {
		ssh_log(session, SSH_LOG_PACKET,
				"Remote host not polite enough to send an eof before close");
	}
	channel->remote_eof = 1;
	/*
	 * The remote eof doesn't break things if there was still data into read
	 * buffer because the eof is ignored until the buffer is empty.
	 */

    if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) {
        channel->callbacks->channel_close_function(channel->session,
                                                 channel,
                                                 channel->callbacks->userdata);
    }


	return SSH_PACKET_USED;
}

SSH_PACKET_CALLBACK(channel_rcv_request) {
	ssh_channel channel;
	ssh_string request_s;
	char *request;
	uint32_t status;
	(void)user;
	(void)type;



	channel = channel_from_msg(session,packet);
	if (channel == NULL) {
		ssh_log(session, SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session));

		return SSH_PACKET_USED;
	}

	request_s = buffer_get_ssh_string(packet);
	if (request_s == NULL) {
		ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");

		return SSH_PACKET_USED;
	}

	request = ssh_string_to_char(request_s);
	ssh_string_free(request_s);
	if (request == NULL) {

		return SSH_PACKET_USED;
	}

	buffer_get_u8(packet, (uint8_t *) &status);

	if (strcmp(request,"exit-status") == 0) {
		SAFE_FREE(request);
		buffer_get_u32(packet, &status);
		channel->exit_status = ntohl(status);
		ssh_log(session, SSH_LOG_PACKET, "received exit-status %d", channel->exit_status);

        if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) {
            channel->callbacks->channel_exit_status_function(channel->session,
                                                     channel,
                                                     channel->exit_status,
                                                     channel->callbacks->userdata);
        }


		return SSH_PACKET_USED;
	}

	if (strcmp(request,"signal") == 0) {
		ssh_string signal_str;
        char *sig;

		SAFE_FREE(request);
		ssh_log(session, SSH_LOG_PACKET, "received signal");

		signal_str = buffer_get_ssh_string(packet);
		if (signal_str == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");

			return SSH_PACKET_USED;
		}

		sig = ssh_string_to_char(signal_str);
		ssh_string_free(signal_str);
		if (sig == NULL) {

			return SSH_PACKET_USED;
		}


		ssh_log(session, SSH_LOG_PACKET,
				"Remote connection sent a signal SIG %s", sig);
        if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) {
            channel->callbacks->channel_signal_function(channel->session,
                                                     channel,
                                                     sig,
                                                     channel->callbacks->userdata);
        }


		return SSH_PACKET_USED;
	}

	if (strcmp(request, "exit-signal") == 0) {
		const char *core = "(core dumped)";
		ssh_string tmp;
		char *sig;
		char *errmsg = NULL;
		char *lang = NULL;
		uint8_t i;

		SAFE_FREE(request);

		tmp = buffer_get_ssh_string(packet);
		if (tmp == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");

			return SSH_PACKET_USED;
		}

		sig = ssh_string_to_char(tmp);
		ssh_string_free(tmp);
		if (sig == NULL) {

			return SSH_PACKET_USED;
		}

		buffer_get_u8(packet, &i);
		if (i == 0) {
			core = "";
		}

		tmp = buffer_get_ssh_string(packet);
		if (tmp == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		errmsg = ssh_string_to_char(tmp);
		ssh_string_free(tmp);
		if (errmsg == NULL) {
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		tmp = buffer_get_ssh_string(packet);
		if (tmp == NULL) {
			ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
            SAFE_FREE(errmsg);
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		lang = ssh_string_to_char(tmp);
		ssh_string_free(tmp);
		if (lang == NULL) {
            SAFE_FREE(errmsg);
            SAFE_FREE(sig);

			return SSH_PACKET_USED;
		}

		ssh_log(session, SSH_LOG_PACKET,
				"Remote connection closed by signal SIG %s %s", sig, core);
        if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) {
            channel->callbacks->channel_exit_signal_function(channel->session,
                                                     channel,
                                                     sig, i, errmsg, lang,
                                                     channel->callbacks->userdata);
        }

        SAFE_FREE(lang);
        SAFE_FREE(errmsg);
		SAFE_FREE(sig);


		return SSH_PACKET_USED;
	}
	if(strcmp(request,"<EMAIL>")==0){
	  SAFE_FREE(request);
	  ssh_log(session, SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive");
	  buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_FAILURE);
	  buffer_add_u32(session->out_buffer, htonl(channel->remote_channel));
	  packet_send(session);

	  return SSH_PACKET_USED;
	}

	/* If we are here, that means we have a request that is not in the understood
	 * client requests. That means we need to create a ssh message to be passed
	 * to the user code handling ssh messages
	 */
	ssh_message_handle_channel_request(session,channel,packet,request,status);

	SAFE_FREE(request);


	return SSH_PACKET_USED;
}

/*
 * When data has been received from the ssh server, it can be applied to the
 * known user function, with help of the callback, or inserted here
 *
 * FIXME is the window changed?
 */
int channel_default_bufferize(ssh_channel channel, void *data, int len,
    int is_stderr) {
	ssh_session session;
	ssh_buffer* dest;

	if(channel == NULL) {
		return -1;
	}

	session = channel->session;

	if(data == NULL) {
		ssh_set_error_invalid(session, __FUNCTION__);
		return -1;
	}

	ssh_log(session, SSH_LOG_RARE,
		"placing %d bytes into channel buffer (stderr ? %d)", len, is_stderr);
  
	dest = is_stderr ? &channel->stderr_buffer : &channel->stdout_buffer;
  
	if (*dest == NULL) {
		*dest = ssh_buffer_new();
		if (*dest == NULL) {
			ssh_set_error_oom(session);
			return -1;
		}
	}

	if (buffer_add_data(*dest, data, len) < 0) {
		ssh_set_error_oom(session);
		ssh_buffer_free(*dest);
		*dest = NULL;
		return -1;
	}

	return 0;
}

/**
 * @brief Open a session channel (suited for a shell, not TCP forwarding).
 *
 * @param[in]  channel  An allocated channel.
 *
 * @return              SSH_OK on success, SSH_ERROR if an error occured.
 *
 * @see channel_open_forward()
 * @see channel_request_env()
 * @see channel_request_shell()
 * @see channel_request_exec()
 */
int ssh_channel_open_session(ssh_channel channel) {
  if(channel == NULL) {
      return SSH_ERROR;
  }

#ifdef WITH_SSH1
  if (channel->session->version == 1) {
{'fraction_non_alphanumeric': 0.108583313677981, 'fraction_numerical': 0.007461886484979737, 'mean_word_length': 3.0431729518855657, 'pattern_counts': {'":': 0, '<': 56, '<?xml version=': 0, '>': 339, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 72, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1249966', 'n_tokens_mistral': 16305, 'n_tokens_neox': 15001, 'n_words': 3593}