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.
This commit is contained in:
parent
ed5ac1815b
commit
f6483489c2
2
NEWS
2
NEWS
@ -1,5 +1,7 @@
|
|||||||
Unreleased
|
Unreleased
|
||||||
|
|
||||||
|
* xC: fixed a crash when the channel topic had too many formatting items
|
||||||
|
|
||||||
* xC: fixed keyboard EOF behaviour with Readline >= 8.0
|
* xC: fixed keyboard EOF behaviour with Readline >= 8.0
|
||||||
|
|
||||||
* xC: made it possible to stream commands into the binary
|
* xC: made it possible to stream commands into the binary
|
||||||
|
765
xC.c
765
xC.c
@ -2895,390 +2895,6 @@ serialize_configuration (struct config_item *root, struct str *output)
|
|||||||
config_item_write (root, true, 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 ---------------------------------------------------------
|
// --- Terminal output ---------------------------------------------------------
|
||||||
|
|
||||||
/// Default colour pair
|
/// Default colour pair
|
||||||
@ -4519,6 +4135,387 @@ formatter_flush (struct formatter *self, FILE *stream, int flush_opts)
|
|||||||
attr_printer_reset (&state);
|
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 -----------------------------------------------------------------
|
// --- Buffers -----------------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Loading…
Reference in New Issue
Block a user