From 20fc6c17d1bd776087d274b319977d265a8c0656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 11 Jun 2015 22:41:01 +0200 Subject: [PATCH] kike: stubplement CAP --- kike-replies | 1 + kike.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/kike-replies b/kike-replies index c5deec5..c585740 100644 --- a/kike-replies +++ b/kike-replies @@ -48,6 +48,7 @@ 403 IRC_ERR_NOSUCHCHANNEL "%s :No such channel" 404 IRC_ERR_CANNOTSENDTOCHAN "%s :Cannot send to channel" 409 IRC_ERR_NOORIGIN ":No origin specified" +410 IRC_ERR_INVALIDCAPCMD "%s :%s" 411 IRC_ERR_NORECIPIENT ":No recipient given (%s)" 412 IRC_ERR_NOTEXTTOSEND ":No text to send" 421 IRC_ERR_UNKNOWNCOMMAND "%s: Unknown command" diff --git a/kike.c b/kike.c index df30ec0..4f9e3cd 100644 --- a/kike.c +++ b/kike.c @@ -308,10 +308,13 @@ struct client struct poller_timer kill_timer; ///< Hard kill timeout bool initialized; ///< Has any data been received yet? + bool cap_negotiating; ///< Negotiating capabilities bool registered; ///< The user has registered bool closing_link; ///< Closing link bool half_closed; ///< Closing link: conn. is half-closed + unsigned long cap_version; ///< CAP protocol version + bool ssl_rx_want_tx; ///< SSL_read() wants to write bool ssl_tx_want_rx; ///< SSL_write() wants to read SSL *ssl; ///< SSL connection @@ -339,6 +342,7 @@ client_init (struct client *self) self->socket_fd = -1; str_init (&self->read_buffer); str_init (&self->write_buffer); + self->cap_version = 301; // TODO: make this configurable and more fine-grained flood_detector_init (&self->antiflood, 10, 20); str_map_init (&self->invites); @@ -1078,6 +1082,8 @@ irc_try_finish_registration (struct client *c) struct server_context *ctx = c->ctx; if (!c->nickname || !c->username || !c->realname) return; + if (c->registered || c->cap_negotiating) + return; c->registered = true; irc_send_reply (c, IRC_RPL_WELCOME, c->nickname, c->username, c->hostname); @@ -1109,6 +1115,66 @@ irc_try_finish_registration (struct client *c) ctx->server_name, c->nickname, c->ssl_cert_fingerprint); } +static void +irc_handle_cap (const struct irc_message *msg, struct client *c) +{ + if (msg->params.len < 1) + RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command); + + struct str_vector v; + str_vector_init (&v); + + const char *subcommand = msg->params.vector[0]; + const char *params = ""; + if (msg->params.len > 1) + { + params = msg->params.vector[1]; + split_str_ignore_empty (params, ' ', &v); + } + + const char *target = c->nickname ? c->nickname : "*"; + if (!irc_strcmp (subcommand, "LS")) + { + if (v.len == 1 && !xstrtoul (&c->cap_version, v.vector[0], 10)) + irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, + subcommand, "Ignoring invalid protocol version number"); + + c->cap_negotiating = true; + // TODO: actually implement a few capabilities + client_send (c, "CAP %s LS :", target); + } + else if (!irc_strcmp (subcommand, "LIST")) + { + // TODO: list currently enabled capabilities + client_send (c, "CAP %s LIST :", target); + } + else if (!irc_strcmp (subcommand, "REQ")) + { + c->cap_negotiating = true; + // TODO: process the capability change request, "-" disables + if (v.len) + client_send (c, "CAP %s NAK :%s", target, params); + else + client_send (c, "CAP %s ACK :%s", target, params); + } + else if (!irc_strcmp (subcommand, "ACK")) + { + if (v.len) + irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, + subcommand, "No acknowledgable capabilities supported"); + } + else if (!irc_strcmp (subcommand, "END")) + { + c->cap_negotiating = false; + irc_try_finish_registration (c); + } + else + irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, + subcommand, "Invalid CAP subcommand"); + + str_vector_free (&v); +} + static void irc_handle_pass (const struct irc_message *msg, struct client *c) { @@ -2516,6 +2582,7 @@ irc_register_handlers (struct server_context *ctx) // TODO: add a field for oper-only commands? static const struct irc_command message_handlers[] = { + { "CAP", false, irc_handle_cap }, { "PASS", false, irc_handle_pass }, { "NICK", false, irc_handle_nick }, { "USER", false, irc_handle_user },