1
0
mirror of https://github.com/tmate-io/tmate-ssh-server.git synced 2020-11-18 19:53:51 -08:00
tmate-ssh-server/tmate-decoder.c
2013-06-10 01:54:29 -04:00

261 lines
5.4 KiB
C

#include "tmate.h"
static struct session *main_session;
struct tmate_unpacker {
msgpack_object *argv;
int argc;
};
static void decoder_error(void)
{
#ifdef DEBUG
tmate_print_trace();
#endif
tmate_fatal("Received a bad message");
}
static void init_unpacker(struct tmate_unpacker *uk,
msgpack_object obj)
{
if (obj.type != MSGPACK_OBJECT_ARRAY)
decoder_error();
uk->argv = obj.via.array.ptr;
uk->argc = obj.via.array.size;
}
static int64_t unpack_int(struct tmate_unpacker *uk)
{
int64_t val;
if (uk->argc == 0)
decoder_error();
if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER &&
uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER)
decoder_error();
val = uk->argv[0].via.i64;
uk->argv++;
uk->argc--;
return val;
}
static void unpack_raw(struct tmate_unpacker *uk,
const char **buf, size_t *len)
{
if (uk->argc == 0)
decoder_error();
if (uk->argv[0].type != MSGPACK_OBJECT_RAW)
decoder_error();
*len = uk->argv[0].via.raw.size;
*buf = uk->argv[0].via.raw.ptr;
uk->argv++;
uk->argc--;
}
static char *unpack_string(struct tmate_unpacker *uk)
{
const char *buf;
char *alloc_buf;
size_t len;
unpack_raw(uk, &buf, &len);
alloc_buf = xmalloc(len + 1);
memcpy(alloc_buf, buf, len);
alloc_buf[len] = '\0';
return alloc_buf;
}
static void unpack_array(struct tmate_unpacker *uk,
struct tmate_unpacker *nested)
{
if (uk->argc == 0)
decoder_error();
init_unpacker(nested, uk->argv[0]);
uk->argv++;
uk->argc--;
}
#define unpack_each(nested_uk, tmp_uk, uk) \
for (unpack_array(uk, tmp_uk); \
(tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \
(tmp_uk)->argv++, (tmp_uk)->argc--)
static void tmate_header(struct tmate_unpacker *uk)
{
int protocol = unpack_int(uk);
if (protocol != 1)
decoder_error();
tmate_debug("new master, protocol: %d", protocol);
}
extern u_int next_window_pane_id;
static void tmate_sync_window_panes(struct window *w,
struct tmate_unpacker *w_uk)
{
struct tmate_unpacker uk, tmp_uk;
struct window_pane *wp, *wp_tmp;
int active_pane_id;
TAILQ_FOREACH(wp, &w->panes, entry)
wp->flags |= PANE_KILL;
unpack_each(&uk, &tmp_uk, w_uk) {
int id = unpack_int(&uk);
int sx = unpack_int(&uk);
int sy = unpack_int(&uk);
int xoff = unpack_int(&uk);
int yoff = unpack_int(&uk);
wp = window_pane_find_by_id(id);
if (!wp) {
next_window_pane_id = id;
wp = window_add_pane(w, TMATE_HLIMIT);
window_set_active_pane(w, wp);
}
wp->flags &= ~PANE_KILL;
wp->xoff = xoff;
wp->yoff = yoff;
window_pane_resize(wp, sx, sy);
wp->flags |= PANE_REDRAW;
}
TAILQ_FOREACH_SAFE(wp, &w->panes, entry, wp_tmp) {
if (wp->flags & PANE_KILL)
window_remove_pane(w, wp);
}
active_pane_id = unpack_int(w_uk);
wp = window_pane_find_by_id(active_pane_id);
if (wp && wp->window == w)
window_set_active_pane(w, wp);
}
static void tmate_sync_window(struct tmate_unpacker *uk)
{
struct session *s;
struct winlink *wl;
struct window *w;
char *cause;
int id = unpack_int(uk);
char *name = unpack_string(uk);
int sx = unpack_int(uk);
int sy = unpack_int(uk);
if (!main_session) {
main_session = session_create("default", NULL, "default", NULL,
NULL, 0, sx, sy, &cause);
if (!main_session)
tmate_fatal("can't create main session");
}
s = main_session;
wl = winlink_find_by_index(&s->windows, id);
if (!wl) {
wl = session_new(s, name, "sh", NULL, id, &cause);
if (!wl)
tmate_fatal("can't create window id=%d", id);
session_select(s, RB_ROOT(&s->windows)->idx);
}
w = wl->window;
free(w->name);
w->name = name;
s->sx = w->sx = sx;
s->sy = w->sy = sy;
tmate_sync_window_panes(w, uk);
server_redraw_window(w);
}
static void tmate_pty_data(struct tmate_unpacker *uk)
{
struct window_pane *wp;
const char *buf;
size_t len;
int id;
id = unpack_int(uk);
unpack_raw(uk, &buf, &len);
wp = window_pane_find_by_id(id);
if (!wp)
tmate_fatal("can't find pane id=%d", id);
evbuffer_add(wp->event_input, buf, len);
input_parse(wp);
wp->window->flags |= WINDOW_SILENCE;
}
static void handle_message(msgpack_object obj)
{
struct tmate_unpacker _uk;
struct tmate_unpacker *uk = &_uk;
int cmd;
init_unpacker(uk, obj);
switch (unpack_int(uk)) {
case TMATE_HEADER: tmate_header(uk); break;
case TMATE_SYNC_WINDOW: tmate_sync_window(uk); break;
case TMATE_PTY_DATA: tmate_pty_data(uk); break;
default: decoder_error();
}
}
void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len)
{
msgpack_unpacked result;
msgpack_unpacker_buffer_consumed(&decoder->unpacker, len);
msgpack_unpacked_init(&result);
while (msgpack_unpacker_next(&decoder->unpacker, &result)) {
handle_message(result.data);
}
msgpack_unpacked_destroy(&result);
if (msgpack_unpacker_message_size(&decoder->unpacker) >
TMATE_MAX_MESSAGE_SIZE) {
tmate_fatal("Message too big");
}
}
void tmate_decoder_get_buffer(struct tmate_decoder *decoder,
char **buf, size_t *len)
{
/* rewind the buffer if possible */
if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) <
TMATE_MAX_MESSAGE_SIZE) {
msgpack_unpacker_expand_buffer(&decoder->unpacker, 0);
}
*buf = msgpack_unpacker_buffer(&decoder->unpacker);
*len = msgpack_unpacker_buffer_capacity(&decoder->unpacker);
}
void tmate_decoder_init(struct tmate_decoder *decoder)
{
if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE))
tmate_fatal("cannot initialize the unpacker");
}