From ba14a30c0a88b0e35a99cabe73525ddfcfd6ef3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Sat, 13 Jun 2015 18:37:07 +0200 Subject: [PATCH] kike: implement WHOWAS --- kike-replies | 3 ++ kike.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/kike-replies b/kike-replies index c585740..ed26232 100644 --- a/kike-replies +++ b/kike-replies @@ -17,6 +17,7 @@ 311 IRC_RPL_WHOISUSER "%s %s %s * :%s" 312 IRC_RPL_WHOISSERVER "%s %s :%s" 313 IRC_RPL_WHOISOPERATOR "%s :is an IRC operator" +314 IRC_RPL_WHOWASUSER "%s %s %s * :%s" 315 IRC_RPL_ENDOFWHO "%s :End of WHO list" 317 IRC_RPL_WHOISIDLE "%s %d :seconds idle" 318 IRC_RPL_ENDOFWHOIS "%s :End of WHOIS list" @@ -39,6 +40,7 @@ 366 IRC_RPL_ENDOFNAMES "%s :End of NAMES list" 367 IRC_RPL_BANLIST "%s %s" 368 IRC_RPL_ENDOFBANLIST "%s :End of channel ban list" +369 IRC_RPL_ENDOFWHOWAS "%s :End of WHOWAS" 372 IRC_RPL_MOTD ":- %s" 375 IRC_RPL_MOTDSTART ":- %s Message of the day - " 376 IRC_RPL_ENDOFMOTD ":End of MOTD command" @@ -47,6 +49,7 @@ 402 IRC_ERR_NOSUCHSERVER "%s :No such server" 403 IRC_ERR_NOSUCHCHANNEL "%s :No such channel" 404 IRC_ERR_CANNOTSENDTOCHAN "%s :Cannot send to channel" +406 IRC_ERR_WASNOSUCHNICK "%s :There was no such nickname" 409 IRC_ERR_NOORIGIN ":No origin specified" 410 IRC_ERR_INVALIDCAPCMD "%s :%s" 411 IRC_ERR_NORECIPIENT ":No recipient given (%s)" diff --git a/kike.c b/kike.c index d13def9..1e3fb9b 100644 --- a/kike.c +++ b/kike.c @@ -535,6 +535,37 @@ channel_user_count (const struct channel *chan) // --- IRC server context ------------------------------------------------------ +struct whowas_info +{ + char *nickname; ///< IRC nickname + char *username; ///< IRC username + char *realname; ///< IRC realname + char *hostname; ///< Hostname shown to the network +}; + +struct whowas_info * +whowas_info_new (struct client *c) +{ + struct whowas_info *self = xmalloc (sizeof *self); + self->nickname = xstrdup (c->nickname); + self->username = xstrdup (c->username); + self->realname = xstrdup (c->realname); + self->hostname = xstrdup (c->hostname); + return self; +} + +static void +whowas_info_destroy (struct whowas_info *self) +{ + free (self->nickname); + free (self->username); + free (self->realname); + free (self->hostname); + free (self); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + struct server_context { int *listen_fds; ///< Listening socket FD's @@ -550,6 +581,8 @@ struct server_context struct str_map handlers; ///< Message handlers struct str_map cap_handlers; ///< CAP message handlers + struct str_map whowas; ///< WHOWAS registry + struct poller poller; ///< Manages polled description struct poller_timer quit_timer; ///< Quit timeout timer bool quitting; ///< User requested quitting @@ -589,6 +622,10 @@ server_context_init (struct server_context *self) str_map_init (&self->cap_handlers); self->cap_handlers.key_xfrm = irc_strxfrm; + str_map_init (&self->whowas); + self->whowas.key_xfrm = irc_strxfrm; + self->whowas.free = (void (*) (void *)) whowas_info_destroy; + poller_init (&self->poller); poller_timer_init (&self->quit_timer, &self->poller); self->quit_timer.dispatcher = on_irc_quit_timeout; @@ -638,6 +675,7 @@ server_context_free (struct server_context *self) str_map_free (&self->channels); str_map_free (&self->handlers); str_map_free (&self->cap_handlers); + str_map_free (&self->whowas); poller_free (&self->poller); str_vector_free (&self->motd); @@ -784,6 +822,15 @@ client_send (struct client *c, const char *format, ...) str_free (&tmp); } +static void +client_add_to_whowas (struct client *c) +{ + // Only keeping one entry for each nickname + // TODO: make sure this list doesn't get too long, for example by + // putting them in a linked list ordered by time + str_map_set (&c->ctx->whowas, c->nickname, whowas_info_new (c)); +} + static void client_unregister (struct client *c, const char *reason) { @@ -808,6 +855,8 @@ client_unregister (struct client *c, const char *reason) irc_channel_destroy_if_empty (c->ctx, chan); } + client_add_to_whowas (c); + str_map_set (&c->ctx->users, c->nickname, NULL); free (c->nickname); c->nickname = NULL; @@ -1126,6 +1175,8 @@ irc_try_finish_registration (struct client *c) client_send (c, ":%s NOTICE %s :" "Your SSL client certificate fingerprint is %s", ctx->server_name, c->nickname, c->ssl_cert_fingerprint); + + str_map_set (&ctx->whowas, c->nickname, NULL); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1328,6 +1379,8 @@ irc_handle_nick (const struct irc_message *msg, struct client *c) if (c->registered) { + client_add_to_whowas (c); + char *message = xstrdup_printf (":%s!%s@%s NICK :%s", c->nickname, c->username, c->hostname, nickname); irc_send_to_roommates (c, message); @@ -2306,8 +2359,8 @@ 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_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) @@ -2386,6 +2439,37 @@ irc_handle_whois (const struct irc_message *msg, struct client *c) str_vector_free (&masks); } +static void +irc_handle_whowas (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 > 2 && !irc_is_this_me (c->ctx, msg->params.vector[2])) + RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHSERVER, msg->params.vector[2]); + // The "count" parameter is ignored, we only store one entry for a nick + + struct str_vector nicks; + str_vector_init (&nicks); + split_str_ignore_empty (msg->params.vector[0], ',', &nicks); + + for (size_t i = 0; i < nicks.len; i++) + { + const char *nick = nicks.vector[i]; + struct whowas_info *info = str_map_find (&c->ctx->whowas, nick); + if (!info) + irc_send_reply (c, IRC_ERR_WASNOSUCHNICK, nick); + else + { + irc_send_reply (c, IRC_RPL_WHOWASUSER, nick, + info->username, info->hostname, info->realname); + irc_send_reply (c, IRC_RPL_WHOISSERVER, nick, c->ctx->server_name, + str_map_find (&c->ctx->config, "server_info")); + } + irc_send_reply (c, IRC_RPL_ENDOFWHOWAS, nick); + } + str_vector_free (&nicks); +} + static void irc_send_rpl_topic (struct client *c, struct channel *chan) { @@ -2812,6 +2896,7 @@ irc_register_handlers (struct server_context *ctx) { "NAMES", true, irc_handle_names }, { "WHO", true, irc_handle_who }, { "WHOIS", true, irc_handle_whois }, + { "WHOWAS", true, irc_handle_whowas }, { "ISON", true, irc_handle_ison }, { "KILL", true, irc_handle_kill },