#include "tmate.h" int tmate_sx = -1; int tmate_sy = -1; struct tmate_unpacker { msgpack_object *argv; int argc; }; static void decoder_error(void) { /* TODO Don't kill the session, disconnect */ 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 tmate_notify(struct tmate_unpacker *uk) { char *msg = unpack_string(uk); tmate_status_message("%s", msg); free(msg); } static void tmate_client_pane_key(struct tmate_unpacker *uk) { struct session *s; struct window *w; struct window_pane *wp; int key = unpack_int(uk); s = RB_MIN(sessions, &sessions); if (!s) return; w = s->curw->window; if (!w) return; wp = w->active; if (!wp) return; window_pane_key(wp, s, key); } static void tmate_client_resize(struct tmate_unpacker *uk) { /* TODO This is sad, we might want our own client. */ tmate_sx = unpack_int(uk); tmate_sy = unpack_int(uk); recalculate_sizes(); /* TODO Handle reconnection cases */ } static void tmate_client_exec_cmd(struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; char *cause; int client_id = unpack_int(uk); char *cmd_str = unpack_string(uk); if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) { tmate_failed_cmd(client_id, cause); free(cause); goto out; } /* error messages land in cfg_causes */ ARRAY_FREE(&cfg_causes); cmd_q = cmdq_new(NULL); cmdq_run(cmd_q, cmdlist); cmd_list_free(cmdlist); cmdq_free(cmd_q); if (!ARRAY_EMPTY(&cfg_causes)) { cause = ARRAY_ITEM(&cfg_causes, 0); tmate_failed_cmd(client_id, cause); free(cause); ARRAY_FREE(&cfg_causes); } out: free(cmd_str); } static void handle_message(msgpack_object obj) { struct tmate_unpacker _uk; struct tmate_unpacker *uk = &_uk; init_unpacker(uk, obj); switch (unpack_int(uk)) { case TMATE_NOTIFY: tmate_notify(uk); break; case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(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"); }