summaryrefslogtreecommitdiff
path: root/xC.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2024-12-18 11:45:25 +0100
committerPřemysl Eric Janouch <p@janouch.name>2024-12-18 11:48:17 +0100
commitf6483489c2a4861bc6dd6c5521fb8a153eddbcb1 (patch)
treef6284c62baf7c6e26c407d265e78721b493eafe9 /xC.c
parented5ac1815badffa2076e5747d819943433dd8eb8 (diff)
downloadxK-f6483489c2a4861bc6dd6c5521fb8a153eddbcb1.tar.gz
xK-f6483489c2a4861bc6dd6c5521fb8a153eddbcb1.tar.xz
xK-f6483489c2a4861bc6dd6c5521fb8a153eddbcb1.zip
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.
Diffstat (limited to 'xC.c')
-rw-r--r--xC.c765
1 files changed, 381 insertions, 384 deletions
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 (&params, ' ');
- str_append (&params, param);
- }
-
- str_append_str (&modes, &params);
- str_free (&params);
-
- 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 (&params, ' ');
+ str_append (&params, param);
+ }
+
+ str_append_str (&modes, &params);
+ str_free (&params);
+
+ 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