kike: fix RPL_WHOISCHANNELS and RPL_NAMREPLY

Now we automatically split the lists and send multiple replies,
but only if it's needed.
This commit is contained in:
Přemysl Eric Janouch 2015-06-07 03:23:15 +02:00
parent 3552b9e1fb
commit 7bcf2a066b

81
kike.c
View File

@ -944,23 +944,66 @@ client_set_ping_timer (struct client *c)
// --- IRC command handling ----------------------------------------------------
static void
irc_make_reply (struct client *c, int id, va_list ap, struct str *output)
{
str_append_printf (output, ":%s %03d %s ",
c->ctx->server_name, id, c->nickname ? c->nickname : "");
str_append_vprintf (output,
irc_get_text (c->ctx, id, g_default_replies[id]), ap);
}
// XXX: this way we cannot typecheck the arguments, so we must be careful
static void
irc_send_reply (struct client *c, int id, ...)
{
struct str tmp;
str_init (&tmp);
struct str reply;
str_init (&reply);
va_list ap;
va_start (ap, id);
str_append_printf (&tmp, ":%s %03d %s ",
c->ctx->server_name, id, c->nickname ? c->nickname : "");
str_append_vprintf (&tmp,
irc_get_text (c->ctx, id, g_default_replies[id]), ap);
irc_make_reply (c, id, ap, &reply);
va_end (ap);
client_send_str (c, &tmp);
str_free (&tmp);
client_send_str (c, &reply);
str_free (&reply);
}
/// Send a space-separated list of words across as many replies as needed
static void
irc_send_reply_vector (struct client *c, int id, char **items, ...)
{
struct str common;
str_init (&common);
va_list ap;
va_start (ap, items);
irc_make_reply (c, id, ap, &common);
va_end (ap);
// We always send at least one message (there might be a client that
// expects us to send this message at least once)
do
{
struct str reply;
str_init (&reply);
str_append_str (&reply, &common);
// If not even a single item fits in the limit (which may happen,
// in theory) it just gets cropped. We could also skip it.
if (*items)
str_append (&reply, *items++);
// Append as many items as fits in a single message
while (*items
&& reply.len + 1 + strlen (*items) <= IRC_MAX_MESSAGE_LENGTH)
str_append_printf (&reply, " %s", *items++);
client_send_str (c, &reply);
str_free (&reply);
}
while (*items);
str_free (&common);
}
#define RETURN_WITH_REPLY(c, ...) \
@ -1858,13 +1901,8 @@ irc_send_rpl_namreply (struct client *c, const struct channel *chan)
str_vector_add_owned (&nicks, str_steal (&result));
}
if (nicks.len)
{
// FIXME: split it into multiple messages if it's too long
char *reply = join_str_vector (&nicks, ' ');
irc_send_reply (c, IRC_RPL_NAMREPLY, type, chan->name, reply);
free (reply);
}
irc_send_reply_vector (c, IRC_RPL_NAMREPLY,
nicks.vector, type, chan->name, "");
str_vector_free (&nicks);
}
@ -2024,6 +2062,9 @@ irc_send_whois_reply (struct client *c, const struct client *target)
if (target->away_message)
irc_send_reply (c, IRC_RPL_AWAY, nick, target->away_message);
struct str_vector channels;
str_vector_init (&channels);
struct str_map_iter iter;
str_map_iter_init (&iter, &c->ctx->channels);
struct channel *chan;
@ -2040,14 +2081,12 @@ irc_send_whois_reply (struct client *c, const struct client *target)
else if (channel_user->modes & IRC_CHAN_MODE_VOICE)
str_append_c (&item, '+');
str_append (&item, chan->name);
str_append_c (&item, ' ');
// TODO: try to merge the results into as few messages as possible
irc_send_reply (c, IRC_RPL_WHOISCHANNELS, nick, item.str);
str_free (&item);
str_vector_add_owned (&channels, str_steal (&item));
}
irc_send_reply_vector (c, IRC_RPL_WHOISCHANNELS, channels.vector, nick, "");
str_vector_free (&channels);
irc_send_reply (c, IRC_RPL_ENDOFWHOIS, nick);
}