kike: implement the WHO command

This commit is contained in:
Přemysl Eric Janouch 2014-08-04 23:36:35 +02:00
parent 3b69dfb583
commit ca72259b12
1 changed files with 108 additions and 0 deletions

View File

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