xC: fix crash with too many topic formatting items
All checks were successful
Arch Linux AUR Success
OpenBSD 7.5 Success
Alpine 3.20 Success

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.
This commit is contained in:
Přemysl Eric Janouch 2024-12-18 11:45:25 +01:00
parent ed5ac1815b
commit f6483489c2
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 383 additions and 384 deletions

2
NEWS
View File

@ -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

765
xC.c
View File

@ -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