From ca72259b125b292f217c530367f18f32ea2021b3 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Mon, 4 Aug 2014 23:36:35 +0200
Subject: kike: implement the WHO command
---
src/kike.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)
(limited to 'src')
diff --git a/src/kike.c b/src/kike.c
index aea6612..08da922 100644
--- a/src/kike.c
+++ b/src/kike.c
@@ -803,6 +803,7 @@ enum
IRC_RPL_USERHOST = 302,
IRC_RPL_UNAWAY = 305,
IRC_RPL_NOWAWAY = 306,
+ IRC_RPL_ENDOFWHO = 315,
IRC_RPL_LIST = 322,
IRC_RPL_LISTEND = 323,
IRC_RPL_CHANNELMODEIS = 324,
@@ -813,6 +814,7 @@ enum
IRC_RPL_EXCEPTLIST = 348,
IRC_RPL_ENDOFEXCEPTLIST = 349,
IRC_RPL_VERSION = 351,
+ IRC_RPL_WHOREPLY = 352,
IRC_RPL_NAMREPLY = 353,
IRC_RPL_ENDOFNAMES = 366,
IRC_RPL_BANLIST = 367,
@@ -868,6 +870,7 @@ static const char *g_default_replies[] =
[IRC_RPL_USERHOST] = ":%s",
[IRC_RPL_UNAWAY] = ":You are no longer marked as being away",
[IRC_RPL_NOWAWAY] = ":You have been marked as being away",
+ [IRC_RPL_ENDOFWHO] = "%s :End of WHO list",
[IRC_RPL_LIST] = "%s %d :%s",
[IRC_RPL_LISTEND] = ":End of LIST",
[IRC_RPL_CHANNELMODEIS] = "%s +%s",
@@ -878,6 +881,7 @@ static const char *g_default_replies[] =
[IRC_RPL_EXCEPTLIST] = "%s %s",
[IRC_RPL_ENDOFEXCEPTLIST] = "%s :End of channel exception list",
[IRC_RPL_VERSION] = "%s.%d %s :%s",
+ [IRC_RPL_WHOREPLY] = "%s %s %s %s %s %s :%d %s",
[IRC_RPL_NAMREPLY] = "%c %s :%s",
[IRC_RPL_ENDOFNAMES] = "%s :End of NAMES list",
[IRC_RPL_BANLIST] = "%s %s",
@@ -1445,6 +1449,109 @@ irc_handle_names (const struct irc_message *msg, struct client *c)
}
}
+static void
+irc_send_rpl_whoreply (struct client *c, const struct channel *chan,
+ const struct client *target)
+{
+ struct str chars;
+ str_init (&chars);
+
+ str_append_c (&chars, target->away_message ? 'G' : 'H');
+ if (target->mode & IRC_USER_MODE_OPERATOR)
+ str_append_c (&chars, '*');
+
+ struct channel_user *user;
+ if (chan && (user = channel_get_user (chan, target)))
+ {
+ if (user->modes & IRC_CHAN_MODE_OPERATOR)
+ str_append_c (&chars, '@');
+ else if (user->modes & IRC_CHAN_MODE_VOICE)
+ str_append_c (&chars, '+');
+ }
+
+ irc_send_reply (c, IRC_RPL_WHOREPLY, chan ? chan->name : "*",
+ target->username, target->hostname, target->ctx->server_name,
+ target->nickname, chars.str, 0 /* hop count */, target->realname);
+ str_free (&chars);
+}
+
+static void
+irc_match_send_rpl_whoreply (struct client *c, struct client *target,
+ const char *mask)
+{
+ bool is_roommate = false;
+ struct str_map_iter iter;
+ str_map_iter_init (&iter, &c->ctx->channels);
+ struct channel *chan;
+ while ((chan = str_map_iter_next (&iter)))
+ if (channel_get_user (chan, target) && channel_get_user (chan, c))
+ {
+ is_roommate = true;
+ break;
+ }
+ if ((target->mode & IRC_USER_MODE_INVISIBLE) && !is_roommate)
+ return;
+
+ if (fnmatch (mask, target->hostname, 0)
+ && fnmatch (mask, target->nickname, 0)
+ && fnmatch (mask, target->realname, 0)
+ && fnmatch (mask, c->ctx->server_name, 0))
+ return;
+
+ // Try to find a channel they're on that's visible to us
+ struct channel *user_chan = NULL;
+ str_map_iter_init (&iter, &c->ctx->channels);
+ while ((chan = str_map_iter_next (&iter)))
+ if (channel_get_user (chan, target)
+ && (!(chan->modes & (IRC_CHAN_MODE_PRIVATE | IRC_CHAN_MODE_SECRET))
+ || channel_get_user (chan, c)))
+ {
+ user_chan = chan;
+ break;
+ }
+ irc_send_rpl_whoreply (c, user_chan, target);
+}
+
+static void
+irc_handle_who (const struct irc_message *msg, struct client *c)
+{
+ bool only_ops = msg->params.len > 1 && !strcmp (msg->params.vector[1], "o");
+
+ const char *shown_mask = msg->params.vector[0], *used_mask;
+ if (!shown_mask)
+ used_mask = shown_mask = "*";
+ else if (!strcmp (shown_mask, "0"))
+ used_mask = "*";
+ else
+ used_mask = shown_mask;
+
+ struct channel *chan;
+ if ((chan = str_map_find (&c->ctx->channels, used_mask)))
+ {
+ bool on_chan = !!channel_get_user (chan, c);
+ if (on_chan || !(chan->modes & IRC_CHAN_MODE_SECRET))
+ for (struct channel_user *iter = chan->users;
+ iter; iter = iter->next)
+ {
+ struct client *target =
+ str_map_find (&c->ctx->users, iter->nickname);
+ if ((on_chan || !(target->mode & IRC_USER_MODE_INVISIBLE))
+ && (!only_ops || (target->mode & IRC_USER_MODE_OPERATOR)))
+ irc_send_rpl_whoreply (c, chan, target);
+ }
+ }
+ else
+ {
+ struct str_map_iter iter;
+ str_map_iter_init (&iter, &c->ctx->users);
+ struct client *target;
+ while ((target = str_map_iter_next (&iter)))
+ if (!only_ops || (target->mode & IRC_USER_MODE_OPERATOR))
+ irc_match_send_rpl_whoreply (c, target, used_mask);
+ }
+ irc_send_reply (c, IRC_RPL_ENDOFWHO, shown_mask);
+}
+
static void
irc_send_rpl_topic (struct client *c, struct channel *chan)
{
@@ -1686,6 +1793,7 @@ irc_register_handlers (struct server_context *ctx)
{ "TOPIC", true, irc_handle_topic },
{ "LIST", true, irc_handle_list },
{ "NAMES", true, irc_handle_names },
+ { "WHO", true, irc_handle_who },
};
for (size_t i = 0; i < N_ELEMENTS (message_handlers); i++)
--
cgit v1.2.3-70-g09d2