kike: implement the WHO command
This commit is contained in:
parent
3b69dfb583
commit
ca72259b12
108
src/kike.c
108
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++)
|
||||
|
Loading…
Reference in New Issue
Block a user