From f6483489c2a4861bc6dd6c5521fb8a153eddbcb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?=
Date: Wed, 18 Dec 2024 11:45:25 +0100 Subject: [PATCH] xC: fix crash with too many topic formatting items Manually constructed formatters have no sentinel value. This is a one-line change in relay_prepare_channel_buffer_update(), however the whole block of "Relay output" code has been moved down, resolving one TODO and rendering two function prototypes unnecessary. --- NEWS | 2 + xC.c | 765 +++++++++++++++++++++++++++++------------------------------ 2 files changed, 383 insertions(+), 384 deletions(-) diff --git a/NEWS b/NEWS index b8c7a7d..55cabe0 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ Unreleased + * xC: fixed a crash when the channel topic had too many formatting items + * xC: fixed keyboard EOF behaviour with Readline >= 8.0 * xC: made it possible to stream commands into the binary diff --git a/xC.c b/xC.c index fde1874..21dd846 100644 --- a/xC.c +++ b/xC.c @@ -2895,390 +2895,6 @@ serialize_configuration (struct config_item *root, struct str *output) config_item_write (root, true, output); } -// --- Relay output ------------------------------------------------------------ - -static void -client_kill (struct client *c) -{ - struct app_context *ctx = c->ctx; - poller_fd_reset (&c->socket_event); - xclose (c->socket_fd); - c->socket_fd = -1; - - LIST_UNLINK (ctx->clients, c); - client_destroy (c); -} - -static void -client_update_poller (struct client *c, const struct pollfd *pfd) -{ - int new_events = POLLIN; - if (c->write_buffer.len) - new_events |= POLLOUT; - - hard_assert (new_events != 0); - if (!pfd || pfd->events != new_events) - poller_fd_set (&c->socket_event, new_events); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static void -relay_send (struct client *c) -{ - struct relay_event_message *m = &c->ctx->relay_message; - m->event_seq = c->event_seq++; - - // TODO: Also don't try sending anything if half-closed. - if (!c->initialized || c->socket_fd == -1) - return; - - // liberty has msg_{reader,writer} already, but they use 8-byte lengths. - size_t frame_len_pos = c->write_buffer.len, frame_len = 0; - str_pack_u32 (&c->write_buffer, 0); - if (!relay_event_message_serialize (m, &c->write_buffer) - || (frame_len = c->write_buffer.len - frame_len_pos - 4) > UINT32_MAX) - { - print_error ("serialization failed, killing client"); - client_kill (c); - return; - } - - uint32_t len = htonl (frame_len); - memcpy (c->write_buffer.str + frame_len_pos, &len, sizeof len); - client_update_poller (c, NULL); -} - -static void -relay_broadcast_except (struct app_context *ctx, struct client *exception) -{ - LIST_FOR_EACH (struct client, c, ctx->clients) - if (c != exception) - relay_send (c); -} - -#define relay_broadcast(ctx) relay_broadcast_except ((ctx), NULL) - -static struct relay_event_message * -relay_prepare (struct app_context *ctx) -{ - struct relay_event_message *m = &ctx->relay_message; - relay_event_message_free (m); - memset (m, 0, sizeof *m); - return m; -} - -static void -relay_prepare_ping (struct app_context *ctx) -{ - relay_prepare (ctx)->data.event = RELAY_EVENT_PING; -} - -static union relay_item_data * -relay_translate_formatter (struct app_context *ctx, union relay_item_data *p, - const struct formatter_item *i) -{ - // XXX: See attr_printer_decode_color(), this is a footgun. - int16_t c16 = i->color; - int16_t c256 = i->color >> 16; - - unsigned attrs = i->attribute; - switch (i->type) - { - case FORMATTER_ITEM_TEXT: - p->text.text = str_from_cstr (i->text); - (p++)->kind = RELAY_ITEM_TEXT; - break; - case FORMATTER_ITEM_FG_COLOR: - p->fg_color.color = c256 <= 0 ? c16 : c256; - (p++)->kind = RELAY_ITEM_FG_COLOR; - break; - case FORMATTER_ITEM_BG_COLOR: - p->bg_color.color = c256 <= 0 ? c16 : c256; - (p++)->kind = RELAY_ITEM_BG_COLOR; - break; - case FORMATTER_ITEM_ATTR: - (p++)->kind = RELAY_ITEM_RESET; - if ((c256 = ctx->theme[i->attribute].fg) >= 0) - { - p->fg_color.color = c256; - (p++)->kind = RELAY_ITEM_FG_COLOR; - } - if ((c256 = ctx->theme[i->attribute].bg) >= 0) - { - p->bg_color.color = c256; - (p++)->kind = RELAY_ITEM_BG_COLOR; - } - - attrs = ctx->theme[i->attribute].attrs; - // Fall-through - case FORMATTER_ITEM_SIMPLE: - if (attrs & TEXT_BOLD) - (p++)->kind = RELAY_ITEM_FLIP_BOLD; - if (attrs & TEXT_ITALIC) - (p++)->kind = RELAY_ITEM_FLIP_ITALIC; - if (attrs & TEXT_UNDERLINE) - (p++)->kind = RELAY_ITEM_FLIP_UNDERLINE; - if (attrs & TEXT_INVERSE) - (p++)->kind = RELAY_ITEM_FLIP_INVERSE; - if (attrs & TEXT_CROSSED_OUT) - (p++)->kind = RELAY_ITEM_FLIP_CROSSED_OUT; - if (attrs & TEXT_MONOSPACE) - (p++)->kind = RELAY_ITEM_FLIP_MONOSPACE; - break; - default: - break; - } - return p; -} - -static union relay_item_data * -relay_items (struct app_context *ctx, const struct formatter_item *items, - uint32_t *len) -{ - size_t items_len = 0; - for (size_t i = 0; items[i].type; i++) - items_len++; - - // Beware of the upper bound, currently dominated by FORMATTER_ITEM_ATTR. - union relay_item_data *a = xcalloc (items_len * 9, sizeof *a), *p = a; - for (const struct formatter_item *i = items; items_len--; i++) - p = relay_translate_formatter (ctx, p, i); - - *len = p - a; - return a; -} - -static void -relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer, - struct buffer_line *line, bool leak_to_active) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_line *e = &m->data.buffer_line; - e->event = RELAY_EVENT_BUFFER_LINE; - e->buffer_name = str_from_cstr (buffer->name); - e->is_unimportant = !!(line->flags & BUFFER_LINE_UNIMPORTANT); - e->is_highlight = !!(line->flags & BUFFER_LINE_HIGHLIGHT); - e->rendition = 1 + line->r; - e->when = line->when * 1000; - e->leak_to_active = leak_to_active; - e->items = relay_items (ctx, line->items, &e->items_len); -} - -// TODO: Consider pushing this whole block of code much further down. -static void formatter_add (struct formatter *self, const char *format, ...); -static char *irc_to_utf8 (const char *text); - -static void -relay_prepare_channel_buffer_update (struct app_context *ctx, - struct buffer *buffer, struct relay_buffer_context_channel *e) -{ - struct channel *channel = buffer->channel; - struct formatter f = formatter_make (ctx, buffer->server); - if (channel->topic) - formatter_add (&f, "#m", channel->topic); - e->topic = relay_items (ctx, f.items, &e->topic_len); - formatter_free (&f); - - // As in make_prompt(), conceal the last known channel modes. - // XXX: This should use irc_channel_is_joined(). - if (!channel->users_len) - return; - - struct str modes = str_make (); - str_append_str (&modes, &channel->no_param_modes); - - struct str params = str_make (); - struct str_map_iter iter = str_map_iter_make (&channel->param_modes); - const char *param; - while ((param = str_map_iter_next (&iter))) - { - str_append_c (&modes, iter.link->key[0]); - str_append_c (¶ms, ' '); - str_append (¶ms, param); - } - - str_append_str (&modes, ¶ms); - str_free (¶ms); - - char *modes_utf8 = irc_to_utf8 (modes.str); - str_free (&modes); - e->modes = str_from_cstr (modes_utf8); - free (modes_utf8); -} - -static void -relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_update *e = &m->data.buffer_update; - e->event = RELAY_EVENT_BUFFER_UPDATE; - e->buffer_name = str_from_cstr (buffer->name); - e->hide_unimportant = buffer->hide_unimportant; - - struct str *server_name = NULL; - switch (buffer->type) - { - case BUFFER_GLOBAL: - e->context.kind = RELAY_BUFFER_KIND_GLOBAL; - break; - case BUFFER_SERVER: - e->context.kind = RELAY_BUFFER_KIND_SERVER; - server_name = &e->context.server.server_name; - break; - case BUFFER_CHANNEL: - e->context.kind = RELAY_BUFFER_KIND_CHANNEL; - server_name = &e->context.channel.server_name; - relay_prepare_channel_buffer_update (ctx, buffer, &e->context.channel); - break; - case BUFFER_PM: - e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE; - server_name = &e->context.private_message.server_name; - break; - } - if (server_name) - *server_name = str_from_cstr (buffer->server->name); -} - -static void -relay_prepare_buffer_stats (struct app_context *ctx, struct buffer *buffer) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_stats *e = &m->data.buffer_stats; - e->event = RELAY_EVENT_BUFFER_STATS; - e->buffer_name = str_from_cstr (buffer->name); - e->new_messages = MIN (UINT32_MAX, - buffer->new_messages_count - buffer->new_unimportant_count); - e->new_unimportant_messages = MIN (UINT32_MAX, - buffer->new_unimportant_count); - e->highlighted = buffer->highlighted; -} - -static void -relay_prepare_buffer_rename (struct app_context *ctx, struct buffer *buffer, - const char *new_name) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_rename *e = &m->data.buffer_rename; - e->event = RELAY_EVENT_BUFFER_RENAME; - e->buffer_name = str_from_cstr (buffer->name); - e->new = str_from_cstr (new_name); -} - -static void -relay_prepare_buffer_remove (struct app_context *ctx, struct buffer *buffer) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_remove *e = &m->data.buffer_remove; - e->event = RELAY_EVENT_BUFFER_REMOVE; - e->buffer_name = str_from_cstr (buffer->name); -} - -static void -relay_prepare_buffer_activate (struct app_context *ctx, struct buffer *buffer) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_activate *e = &m->data.buffer_activate; - e->event = RELAY_EVENT_BUFFER_ACTIVATE; - e->buffer_name = str_from_cstr (buffer->name); -} - -static void -relay_prepare_buffer_input (struct app_context *ctx, struct buffer *buffer, - const char *input) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_input *e = &m->data.buffer_input; - e->event = RELAY_EVENT_BUFFER_INPUT; - e->buffer_name = str_from_cstr (buffer->name); - e->text = str_from_cstr (input); -} - -static void -relay_prepare_buffer_clear (struct app_context *ctx, - struct buffer *buffer) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_buffer_clear *e = &m->data.buffer_clear; - e->event = RELAY_EVENT_BUFFER_CLEAR; - e->buffer_name = str_from_cstr (buffer->name); -} - -enum relay_server_state -relay_server_state_for_server (struct server *s) -{ - switch (s->state) - { - case IRC_DISCONNECTED: return RELAY_SERVER_STATE_DISCONNECTED; - case IRC_CONNECTING: return RELAY_SERVER_STATE_CONNECTING; - case IRC_CONNECTED: return RELAY_SERVER_STATE_CONNECTED; - case IRC_REGISTERED: return RELAY_SERVER_STATE_REGISTERED; - case IRC_CLOSING: - case IRC_HALF_CLOSED: return RELAY_SERVER_STATE_DISCONNECTING; - } - return 0; -} - -static void -relay_prepare_server_update (struct app_context *ctx, struct server *s) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_server_update *e = &m->data.server_update; - e->event = RELAY_EVENT_SERVER_UPDATE; - e->server_name = str_from_cstr (s->name); - e->data.state = relay_server_state_for_server (s); - if (s->state == IRC_REGISTERED) - { - char *user_utf8 = irc_to_utf8 (s->irc_user->nickname); - e->data.registered.user = str_from_cstr (user_utf8); - free (user_utf8); - - char *user_modes_utf8 = irc_to_utf8 (s->irc_user_modes.str); - e->data.registered.user_modes = str_from_cstr (user_modes_utf8); - free (user_modes_utf8); - } -} - -static void -relay_prepare_server_rename (struct app_context *ctx, struct server *s, - const char *new_name) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_server_rename *e = &m->data.server_rename; - e->event = RELAY_EVENT_SERVER_RENAME; - e->server_name = str_from_cstr (s->name); - e->new = str_from_cstr (new_name); -} - -static void -relay_prepare_server_remove (struct app_context *ctx, struct server *s) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_server_remove *e = &m->data.server_remove; - e->event = RELAY_EVENT_SERVER_REMOVE; - e->server_name = str_from_cstr (s->name); -} - -static void -relay_prepare_error (struct app_context *ctx, uint32_t seq, const char *message) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_error *e = &m->data.error; - e->event = RELAY_EVENT_ERROR; - e->command_seq = seq; - e->error = str_from_cstr (message); -} - -static struct relay_event_data_response * -relay_prepare_response (struct app_context *ctx, uint32_t seq) -{ - struct relay_event_message *m = relay_prepare (ctx); - struct relay_event_data_response *e = &m->data.response; - e->event = RELAY_EVENT_RESPONSE; - e->command_seq = seq; - return e; -} - // --- Terminal output --------------------------------------------------------- /// Default colour pair @@ -4519,6 +4135,387 @@ formatter_flush (struct formatter *self, FILE *stream, int flush_opts) attr_printer_reset (&state); } +// --- Relay output ------------------------------------------------------------ + +static void +client_kill (struct client *c) +{ + struct app_context *ctx = c->ctx; + poller_fd_reset (&c->socket_event); + xclose (c->socket_fd); + c->socket_fd = -1; + + LIST_UNLINK (ctx->clients, c); + client_destroy (c); +} + +static void +client_update_poller (struct client *c, const struct pollfd *pfd) +{ + int new_events = POLLIN; + if (c->write_buffer.len) + new_events |= POLLOUT; + + hard_assert (new_events != 0); + if (!pfd || pfd->events != new_events) + poller_fd_set (&c->socket_event, new_events); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +relay_send (struct client *c) +{ + struct relay_event_message *m = &c->ctx->relay_message; + m->event_seq = c->event_seq++; + + // TODO: Also don't try sending anything if half-closed. + if (!c->initialized || c->socket_fd == -1) + return; + + // liberty has msg_{reader,writer} already, but they use 8-byte lengths. + size_t frame_len_pos = c->write_buffer.len, frame_len = 0; + str_pack_u32 (&c->write_buffer, 0); + if (!relay_event_message_serialize (m, &c->write_buffer) + || (frame_len = c->write_buffer.len - frame_len_pos - 4) > UINT32_MAX) + { + print_error ("serialization failed, killing client"); + client_kill (c); + return; + } + + uint32_t len = htonl (frame_len); + memcpy (c->write_buffer.str + frame_len_pos, &len, sizeof len); + client_update_poller (c, NULL); +} + +static void +relay_broadcast_except (struct app_context *ctx, struct client *exception) +{ + LIST_FOR_EACH (struct client, c, ctx->clients) + if (c != exception) + relay_send (c); +} + +#define relay_broadcast(ctx) relay_broadcast_except ((ctx), NULL) + +static struct relay_event_message * +relay_prepare (struct app_context *ctx) +{ + struct relay_event_message *m = &ctx->relay_message; + relay_event_message_free (m); + memset (m, 0, sizeof *m); + return m; +} + +static void +relay_prepare_ping (struct app_context *ctx) +{ + relay_prepare (ctx)->data.event = RELAY_EVENT_PING; +} + +static union relay_item_data * +relay_translate_formatter (struct app_context *ctx, union relay_item_data *p, + const struct formatter_item *i) +{ + // XXX: See attr_printer_decode_color(), this is a footgun. + int16_t c16 = i->color; + int16_t c256 = i->color >> 16; + + unsigned attrs = i->attribute; + switch (i->type) + { + case FORMATTER_ITEM_TEXT: + p->text.text = str_from_cstr (i->text); + (p++)->kind = RELAY_ITEM_TEXT; + break; + case FORMATTER_ITEM_FG_COLOR: + p->fg_color.color = c256 <= 0 ? c16 : c256; + (p++)->kind = RELAY_ITEM_FG_COLOR; + break; + case FORMATTER_ITEM_BG_COLOR: + p->bg_color.color = c256 <= 0 ? c16 : c256; + (p++)->kind = RELAY_ITEM_BG_COLOR; + break; + case FORMATTER_ITEM_ATTR: + (p++)->kind = RELAY_ITEM_RESET; + if ((c256 = ctx->theme[i->attribute].fg) >= 0) + { + p->fg_color.color = c256; + (p++)->kind = RELAY_ITEM_FG_COLOR; + } + if ((c256 = ctx->theme[i->attribute].bg) >= 0) + { + p->bg_color.color = c256; + (p++)->kind = RELAY_ITEM_BG_COLOR; + } + + attrs = ctx->theme[i->attribute].attrs; + // Fall-through + case FORMATTER_ITEM_SIMPLE: + if (attrs & TEXT_BOLD) + (p++)->kind = RELAY_ITEM_FLIP_BOLD; + if (attrs & TEXT_ITALIC) + (p++)->kind = RELAY_ITEM_FLIP_ITALIC; + if (attrs & TEXT_UNDERLINE) + (p++)->kind = RELAY_ITEM_FLIP_UNDERLINE; + if (attrs & TEXT_INVERSE) + (p++)->kind = RELAY_ITEM_FLIP_INVERSE; + if (attrs & TEXT_CROSSED_OUT) + (p++)->kind = RELAY_ITEM_FLIP_CROSSED_OUT; + if (attrs & TEXT_MONOSPACE) + (p++)->kind = RELAY_ITEM_FLIP_MONOSPACE; + break; + default: + break; + } + return p; +} + +static union relay_item_data * +relay_items (struct app_context *ctx, const struct formatter_item *items, + uint32_t *len) +{ + size_t items_len = 0; + for (size_t i = 0; items[i].type; i++) + items_len++; + + // Beware of the upper bound, currently dominated by FORMATTER_ITEM_ATTR. + union relay_item_data *a = xcalloc (items_len * 9, sizeof *a), *p = a; + for (const struct formatter_item *i = items; items_len--; i++) + p = relay_translate_formatter (ctx, p, i); + + *len = p - a; + return a; +} + +static void +relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer, + struct buffer_line *line, bool leak_to_active) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_line *e = &m->data.buffer_line; + e->event = RELAY_EVENT_BUFFER_LINE; + e->buffer_name = str_from_cstr (buffer->name); + e->is_unimportant = !!(line->flags & BUFFER_LINE_UNIMPORTANT); + e->is_highlight = !!(line->flags & BUFFER_LINE_HIGHLIGHT); + e->rendition = 1 + line->r; + e->when = line->when * 1000; + e->leak_to_active = leak_to_active; + e->items = relay_items (ctx, line->items, &e->items_len); +} + +static void +relay_prepare_channel_buffer_update (struct app_context *ctx, + struct buffer *buffer, struct relay_buffer_context_channel *e) +{ + struct channel *channel = buffer->channel; + struct formatter f = formatter_make (ctx, buffer->server); + if (channel->topic) + formatter_add (&f, "#m", channel->topic); + FORMATTER_ADD_ITEM (&f, END); + e->topic = relay_items (ctx, f.items, &e->topic_len); + formatter_free (&f); + + // As in make_prompt(), conceal the last known channel modes. + // XXX: This should use irc_channel_is_joined(). + if (!channel->users_len) + return; + + struct str modes = str_make (); + str_append_str (&modes, &channel->no_param_modes); + + struct str params = str_make (); + struct str_map_iter iter = str_map_iter_make (&channel->param_modes); + const char *param; + while ((param = str_map_iter_next (&iter))) + { + str_append_c (&modes, iter.link->key[0]); + str_append_c (¶ms, ' '); + str_append (¶ms, param); + } + + str_append_str (&modes, ¶ms); + str_free (¶ms); + + char *modes_utf8 = irc_to_utf8 (modes.str); + str_free (&modes); + e->modes = str_from_cstr (modes_utf8); + free (modes_utf8); +} + +static void +relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_update *e = &m->data.buffer_update; + e->event = RELAY_EVENT_BUFFER_UPDATE; + e->buffer_name = str_from_cstr (buffer->name); + e->hide_unimportant = buffer->hide_unimportant; + + struct str *server_name = NULL; + switch (buffer->type) + { + case BUFFER_GLOBAL: + e->context.kind = RELAY_BUFFER_KIND_GLOBAL; + break; + case BUFFER_SERVER: + e->context.kind = RELAY_BUFFER_KIND_SERVER; + server_name = &e->context.server.server_name; + break; + case BUFFER_CHANNEL: + e->context.kind = RELAY_BUFFER_KIND_CHANNEL; + server_name = &e->context.channel.server_name; + relay_prepare_channel_buffer_update (ctx, buffer, &e->context.channel); + break; + case BUFFER_PM: + e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE; + server_name = &e->context.private_message.server_name; + break; + } + if (server_name) + *server_name = str_from_cstr (buffer->server->name); +} + +static void +relay_prepare_buffer_stats (struct app_context *ctx, struct buffer *buffer) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_stats *e = &m->data.buffer_stats; + e->event = RELAY_EVENT_BUFFER_STATS; + e->buffer_name = str_from_cstr (buffer->name); + e->new_messages = MIN (UINT32_MAX, + buffer->new_messages_count - buffer->new_unimportant_count); + e->new_unimportant_messages = MIN (UINT32_MAX, + buffer->new_unimportant_count); + e->highlighted = buffer->highlighted; +} + +static void +relay_prepare_buffer_rename (struct app_context *ctx, struct buffer *buffer, + const char *new_name) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_rename *e = &m->data.buffer_rename; + e->event = RELAY_EVENT_BUFFER_RENAME; + e->buffer_name = str_from_cstr (buffer->name); + e->new = str_from_cstr (new_name); +} + +static void +relay_prepare_buffer_remove (struct app_context *ctx, struct buffer *buffer) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_remove *e = &m->data.buffer_remove; + e->event = RELAY_EVENT_BUFFER_REMOVE; + e->buffer_name = str_from_cstr (buffer->name); +} + +static void +relay_prepare_buffer_activate (struct app_context *ctx, struct buffer *buffer) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_activate *e = &m->data.buffer_activate; + e->event = RELAY_EVENT_BUFFER_ACTIVATE; + e->buffer_name = str_from_cstr (buffer->name); +} + +static void +relay_prepare_buffer_input (struct app_context *ctx, struct buffer *buffer, + const char *input) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_input *e = &m->data.buffer_input; + e->event = RELAY_EVENT_BUFFER_INPUT; + e->buffer_name = str_from_cstr (buffer->name); + e->text = str_from_cstr (input); +} + +static void +relay_prepare_buffer_clear (struct app_context *ctx, + struct buffer *buffer) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_buffer_clear *e = &m->data.buffer_clear; + e->event = RELAY_EVENT_BUFFER_CLEAR; + e->buffer_name = str_from_cstr (buffer->name); +} + +enum relay_server_state +relay_server_state_for_server (struct server *s) +{ + switch (s->state) + { + case IRC_DISCONNECTED: return RELAY_SERVER_STATE_DISCONNECTED; + case IRC_CONNECTING: return RELAY_SERVER_STATE_CONNECTING; + case IRC_CONNECTED: return RELAY_SERVER_STATE_CONNECTED; + case IRC_REGISTERED: return RELAY_SERVER_STATE_REGISTERED; + case IRC_CLOSING: + case IRC_HALF_CLOSED: return RELAY_SERVER_STATE_DISCONNECTING; + } + return 0; +} + +static void +relay_prepare_server_update (struct app_context *ctx, struct server *s) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_server_update *e = &m->data.server_update; + e->event = RELAY_EVENT_SERVER_UPDATE; + e->server_name = str_from_cstr (s->name); + e->data.state = relay_server_state_for_server (s); + if (s->state == IRC_REGISTERED) + { + char *user_utf8 = irc_to_utf8 (s->irc_user->nickname); + e->data.registered.user = str_from_cstr (user_utf8); + free (user_utf8); + + char *user_modes_utf8 = irc_to_utf8 (s->irc_user_modes.str); + e->data.registered.user_modes = str_from_cstr (user_modes_utf8); + free (user_modes_utf8); + } +} + +static void +relay_prepare_server_rename (struct app_context *ctx, struct server *s, + const char *new_name) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_server_rename *e = &m->data.server_rename; + e->event = RELAY_EVENT_SERVER_RENAME; + e->server_name = str_from_cstr (s->name); + e->new = str_from_cstr (new_name); +} + +static void +relay_prepare_server_remove (struct app_context *ctx, struct server *s) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_server_remove *e = &m->data.server_remove; + e->event = RELAY_EVENT_SERVER_REMOVE; + e->server_name = str_from_cstr (s->name); +} + +static void +relay_prepare_error (struct app_context *ctx, uint32_t seq, const char *message) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_error *e = &m->data.error; + e->event = RELAY_EVENT_ERROR; + e->command_seq = seq; + e->error = str_from_cstr (message); +} + +static struct relay_event_data_response * +relay_prepare_response (struct app_context *ctx, uint32_t seq) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_response *e = &m->data.response; + e->event = RELAY_EVENT_RESPONSE; + e->command_seq = seq; + return e; +} + // --- Buffers ----------------------------------------------------------------- static void