kike: implement the WHOIS command

The code is starting to stink a bit, refactor imminent.
This commit is contained in:
Přemysl Eric Janouch 2014-08-05 23:11:53 +02:00
parent ad7d17d2d8
commit eab2d1765a

View File

@ -29,6 +29,7 @@
static struct config_item g_config_table[] = static struct config_item g_config_table[] =
{ {
{ "server_name", NULL, "Server name" }, { "server_name", NULL, "Server name" },
{ "server_info", "My server", "Brief server description" },
{ "motd", NULL, "MOTD filename" }, { "motd", NULL, "MOTD filename" },
{ "catalog", NULL, "catgets localization catalog" }, { "catalog", NULL, "catgets localization catalog" },
@ -259,6 +260,7 @@ struct client
unsigned mode; ///< User's mode unsigned mode; ///< User's mode
char *away_message; ///< Away message char *away_message; ///< Away message
time_t last_active; ///< Last PRIVMSG, to get idle time
}; };
static void static void
@ -811,7 +813,13 @@ enum
IRC_RPL_ISON = 303, IRC_RPL_ISON = 303,
IRC_RPL_UNAWAY = 305, IRC_RPL_UNAWAY = 305,
IRC_RPL_NOWAWAY = 306, IRC_RPL_NOWAWAY = 306,
IRC_RPL_WHOISUSER = 311,
IRC_RPL_WHOISSERVER = 312,
IRC_RPL_WHOISOPERATOR = 313,
IRC_RPL_ENDOFWHO = 315, IRC_RPL_ENDOFWHO = 315,
IRC_RPL_WHOISIDLE = 317,
IRC_RPL_ENDOFWHOIS = 318,
IRC_RPL_WHOISCHANNELS = 319,
IRC_RPL_LIST = 322, IRC_RPL_LIST = 322,
IRC_RPL_LISTEND = 323, IRC_RPL_LISTEND = 323,
IRC_RPL_CHANNELMODEIS = 324, IRC_RPL_CHANNELMODEIS = 324,
@ -880,7 +888,13 @@ static const char *g_default_replies[] =
[IRC_RPL_ISON] = ":%s", [IRC_RPL_ISON] = ":%s",
[IRC_RPL_UNAWAY] = ":You are no longer marked as being away", [IRC_RPL_UNAWAY] = ":You are no longer marked as being away",
[IRC_RPL_NOWAWAY] = ":You have been 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_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_LIST] = "%s %d :%s",
[IRC_RPL_LISTEND] = ":End of LIST", [IRC_RPL_LISTEND] = ":End of LIST",
[IRC_RPL_CHANNELMODEIS] = "%s +%s", [IRC_RPL_CHANNELMODEIS] = "%s +%s",
@ -1347,6 +1361,8 @@ static void
irc_handle_privmsg (const struct irc_message *msg, struct client *c) irc_handle_privmsg (const struct irc_message *msg, struct client *c)
{ {
irc_handle_user_message (msg, c, "PRIVMSG", true); 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 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); 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 static void
irc_send_rpl_topic (struct client *c, struct channel *chan) 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 }, { "LIST", true, irc_handle_list },
{ "NAMES", true, irc_handle_names }, { "NAMES", true, irc_handle_names },
{ "WHO", true, irc_handle_who }, { "WHO", true, irc_handle_who },
{ "WHOIS", true, irc_handle_whois },
{ "ISON", true, irc_handle_ison }, { "ISON", true, irc_handle_ison },
}; };
@ -2239,6 +2337,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data)
c->ctx = ctx; c->ctx = ctx;
c->socket_fd = fd; c->socket_fd = fd;
c->hostname = xstrdup (host); c->hostname = xstrdup (host);
c->last_active = time (NULL);
LIST_PREPEND (ctx->clients, c); LIST_PREPEND (ctx->clients, c);
ctx->n_clients++; ctx->n_clients++;