From eab2d1765a2a88c17aed35469ff843030f02b550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Tue, 5 Aug 2014 23:11:53 +0200 Subject: [PATCH] kike: implement the WHOIS command The code is starting to stink a bit, refactor imminent. --- src/kike.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/kike.c b/src/kike.c index 82b3385..a4be70f 100644 --- a/src/kike.c +++ b/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++;