kike: implement the WHOIS command
The code is starting to stink a bit, refactor imminent.
This commit is contained in:
parent
ad7d17d2d8
commit
eab2d1765a
99
src/kike.c
99
src/kike.c
@ -29,6 +29,7 @@
|
||||
static struct config_item g_config_table[] =
|
||||
{
|
||||
{ "server_name", NULL, "Server name" },
|
||||
{ "server_info", "My server", "Brief server description" },
|
||||
{ "motd", NULL, "MOTD filename" },
|
||||
{ "catalog", NULL, "catgets localization catalog" },
|
||||
|
||||
@ -259,6 +260,7 @@ struct client
|
||||
|
||||
unsigned mode; ///< User's mode
|
||||
char *away_message; ///< Away message
|
||||
time_t last_active; ///< Last PRIVMSG, to get idle time
|
||||
};
|
||||
|
||||
static void
|
||||
@ -811,7 +813,13 @@ enum
|
||||
IRC_RPL_ISON = 303,
|
||||
IRC_RPL_UNAWAY = 305,
|
||||
IRC_RPL_NOWAWAY = 306,
|
||||
IRC_RPL_WHOISUSER = 311,
|
||||
IRC_RPL_WHOISSERVER = 312,
|
||||
IRC_RPL_WHOISOPERATOR = 313,
|
||||
IRC_RPL_ENDOFWHO = 315,
|
||||
IRC_RPL_WHOISIDLE = 317,
|
||||
IRC_RPL_ENDOFWHOIS = 318,
|
||||
IRC_RPL_WHOISCHANNELS = 319,
|
||||
IRC_RPL_LIST = 322,
|
||||
IRC_RPL_LISTEND = 323,
|
||||
IRC_RPL_CHANNELMODEIS = 324,
|
||||
@ -880,7 +888,13 @@ static const char *g_default_replies[] =
|
||||
[IRC_RPL_ISON] = ":%s",
|
||||
[IRC_RPL_UNAWAY] = ":You are no longer marked as being away",
|
||||
[IRC_RPL_NOWAWAY] = ":You have been marked as being away",
|
||||
[IRC_RPL_WHOISUSER] = "%s %s %s * :%s",
|
||||
[IRC_RPL_WHOISSERVER] = "%s %s :%s",
|
||||
[IRC_RPL_WHOISOPERATOR] = "%s :is an IRC operator",
|
||||
[IRC_RPL_ENDOFWHO] = "%s :End of WHO list",
|
||||
[IRC_RPL_WHOISIDLE] = "%s %d :seconds idle",
|
||||
[IRC_RPL_ENDOFWHOIS] = "%s :End of WHOIS list",
|
||||
[IRC_RPL_WHOISCHANNELS] = "%s :%s",
|
||||
[IRC_RPL_LIST] = "%s %d :%s",
|
||||
[IRC_RPL_LISTEND] = ":End of LIST",
|
||||
[IRC_RPL_CHANNELMODEIS] = "%s +%s",
|
||||
@ -1347,6 +1361,8 @@ static void
|
||||
irc_handle_privmsg (const struct irc_message *msg, struct client *c)
|
||||
{
|
||||
irc_handle_user_message (msg, c, "PRIVMSG", true);
|
||||
// Let's not care too much about success or failure
|
||||
c->last_active = time (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1578,6 +1594,87 @@ irc_handle_who (const struct irc_message *msg, struct client *c)
|
||||
irc_send_reply (c, IRC_RPL_ENDOFWHO, shown_mask);
|
||||
}
|
||||
|
||||
static void
|
||||
irc_send_whois_reply (struct client *c, const struct client *target)
|
||||
{
|
||||
const char *nick = target->nickname;
|
||||
irc_send_reply (c, IRC_RPL_WHOISUSER, nick, target->username,
|
||||
target->hostname, target->realname);
|
||||
irc_send_reply (c, IRC_RPL_WHOISSERVER, nick, target->ctx->server_name,
|
||||
str_map_find (&c->ctx->config, "server_info"));
|
||||
if (target->mode & IRC_USER_MODE_OPERATOR)
|
||||
irc_send_reply (c, IRC_RPL_WHOISOPERATOR, nick);
|
||||
irc_send_reply (c, IRC_RPL_WHOISIDLE, nick,
|
||||
(int) (time (NULL) - target->last_active));
|
||||
|
||||
struct str_map_iter iter;
|
||||
str_map_iter_init (&iter, &c->ctx->channels);
|
||||
struct channel *chan;
|
||||
struct channel_user *channel_user;
|
||||
while ((chan = str_map_iter_next (&iter)))
|
||||
if ((channel_user = channel_get_user (chan, target))
|
||||
&& (!(chan->modes & (IRC_CHAN_MODE_PRIVATE | IRC_CHAN_MODE_SECRET))
|
||||
|| channel_get_user (chan, c)))
|
||||
{
|
||||
struct str item;
|
||||
str_init (&item);
|
||||
if (channel_user->modes & IRC_CHAN_MODE_OPERATOR)
|
||||
str_append_c (&item, '@');
|
||||
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);
|
||||
}
|
||||
|
||||
irc_send_reply (c, IRC_RPL_ENDOFWHOIS, nick);
|
||||
}
|
||||
|
||||
static void
|
||||
irc_handle_whois (const struct irc_message *msg, struct client *c)
|
||||
{
|
||||
if (msg->params.len < 1)
|
||||
RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command);
|
||||
if (msg->params.len > 1 && !irc_is_this_me (c->ctx, msg->params.vector[0]))
|
||||
RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHSERVER, msg->params.vector[0]);
|
||||
|
||||
struct str_vector masks;
|
||||
str_vector_init (&masks);
|
||||
const char *masks_str = msg->params.vector[msg->params.len > 1];
|
||||
split_str_ignore_empty (masks_str, ',', &masks);
|
||||
for (size_t i = 0; i < masks.len; i++)
|
||||
{
|
||||
const char *mask = masks.vector[i];
|
||||
struct client *target;
|
||||
if (!strpbrk (mask, "*?"))
|
||||
{
|
||||
if (!(target = str_map_find (&c->ctx->users, mask)))
|
||||
irc_send_reply (c, IRC_ERR_NOSUCHNICK, mask);
|
||||
else
|
||||
irc_send_whois_reply (c, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct str_map_iter iter;
|
||||
str_map_iter_init (&iter, &c->ctx->users);
|
||||
bool found = false;
|
||||
while ((target = str_map_iter_next (&iter))
|
||||
&& !irc_fnmatch (mask, target->nickname))
|
||||
{
|
||||
irc_send_whois_reply (c, target);
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
irc_send_reply (c, IRC_ERR_NOSUCHNICK, mask);
|
||||
}
|
||||
}
|
||||
str_vector_free (&masks);
|
||||
}
|
||||
|
||||
static void
|
||||
irc_send_rpl_topic (struct client *c, struct channel *chan)
|
||||
{
|
||||
@ -1901,6 +1998,7 @@ irc_register_handlers (struct server_context *ctx)
|
||||
{ "LIST", true, irc_handle_list },
|
||||
{ "NAMES", true, irc_handle_names },
|
||||
{ "WHO", true, irc_handle_who },
|
||||
{ "WHOIS", true, irc_handle_whois },
|
||||
{ "ISON", true, irc_handle_ison },
|
||||
};
|
||||
|
||||
@ -2239,6 +2337,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data)
|
||||
c->ctx = ctx;
|
||||
c->socket_fd = fd;
|
||||
c->hostname = xstrdup (host);
|
||||
c->last_active = time (NULL);
|
||||
LIST_PREPEND (ctx->clients, c);
|
||||
ctx->n_clients++;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user