From 7bcf2a066b2c0838dc2da4af415ec80321732118 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Sun, 7 Jun 2015 03:23:15 +0200
Subject: kike: fix RPL_WHOISCHANNELS and RPL_NAMREPLY
Now we automatically split the lists and send multiple replies,
but only if it's needed.
---
kike.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 60 insertions(+), 21 deletions(-)
diff --git a/kike.c b/kike.c
index 7a2d6e5..2ad92a0 100644
--- a/kike.c
+++ b/kike.c
@@ -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);
}
--
cgit v1.2.3-70-g09d2