From 1d53b87016965bb9d4fbb895713c6ba25fc4241e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Fri, 12 Jun 2015 21:27:17 +0200 Subject: [PATCH] kike: refactor CAP processing --- kike.c | 225 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 145 insertions(+), 80 deletions(-) diff --git a/kike.c b/kike.c index 948e635..83aa659 100644 --- a/kike.c +++ b/kike.c @@ -319,7 +319,7 @@ struct client bool half_closed; ///< Closing link: conn. is half-closed unsigned long cap_version; ///< CAP protocol version - unsigned caps; ///< Enabled capabilities + unsigned caps_enabled; ///< Enabled capabilities bool ssl_rx_want_tx; ///< SSL_read() wants to write bool ssl_tx_want_rx; ///< SSL_write() wants to read @@ -545,6 +545,7 @@ struct server_context struct str_map users; ///< Maps nicknames to clients struct str_map channels; ///< Maps channel names to data struct str_map handlers; ///< Message handlers + struct str_map cap_handlers; ///< CAP message handlers struct poller poller; ///< Manages polled description struct poller_timer quit_timer; ///< Quit timeout timer @@ -582,6 +583,8 @@ server_context_init (struct server_context *self) self->channels.free = (void (*) (void *)) channel_delete; str_map_init (&self->handlers); self->handlers.key_xfrm = irc_strxfrm; + str_map_init (&self->cap_handlers); + self->cap_handlers.key_xfrm = irc_strxfrm; poller_init (&self->poller); poller_timer_init (&self->quit_timer, &self->poller); @@ -631,6 +634,7 @@ server_context_free (struct server_context *self) str_map_free (&self->users); str_map_free (&self->channels); str_map_free (&self->handlers); + str_map_free (&self->cap_handlers); poller_free (&self->poller); str_vector_free (&self->motd); @@ -1121,97 +1125,157 @@ irc_try_finish_registration (struct client *c) ctx->server_name, c->nickname, c->ssl_cert_fingerprint); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct irc_cap_args +{ + const char *subcommand; ///< The subcommand being processed + const char *full_params; ///< Whole parameter string + struct str_vector params; ///< Split parameters + const char *target; ///< Target parameter for replies +}; + +static void +irc_handle_cap_ls (struct client *c, struct irc_cap_args *a) +{ + if (a->params.len == 1 + && !xstrtoul (&c->cap_version, a->params.vector[0], 10)) + irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, + a->subcommand, "Ignoring invalid protocol version number"); + + c->cap_negotiating = true; + client_send (c, "CAP %s LS :multi-prefix", a->target); +} + +static void +irc_handle_cap_list (struct client *c, struct irc_cap_args *a) +{ + struct str_vector caps; + str_vector_init (&caps); + + if (c->caps_enabled & IRC_CAP_MULTI_PREFIX) + str_vector_add (&caps, "multi-prefix"); + + char *caps_str = join_str_vector (&caps, ' '); + str_vector_free (&caps); + client_send (c, "CAP %s LIST :%s", a->target, caps_str); + free (caps_str); +} + +static unsigned +irc_decode_capability (const char *name) +{ + if (!strcmp (name, "multi-prefix")) + return IRC_CAP_MULTI_PREFIX; + return 0; +} + +static void +irc_handle_cap_req (struct client *c, struct irc_cap_args *a) +{ + c->cap_negotiating = true; + + unsigned new_caps = c->caps_enabled; + bool success = true; + for (size_t i = 0; i < a->params.len; i++) + { + bool removing = false; + const char *name = a->params.vector[i]; + if (*name == '-') + { + removing = true; + name++; + } + + unsigned cap; + if (!(cap = irc_decode_capability (name))) + success = false; + else if (removing) + new_caps &= ~cap; + else + new_caps |= cap; + } + + if (success) + { + c->caps_enabled = new_caps; + client_send (c, "CAP %s ACK :%s", a->target, a->full_params); + } + else + client_send (c, "CAP %s NAK :%s", a->target, a->full_params); +} + +static void +irc_handle_cap_ack (struct client *c, struct irc_cap_args *a) +{ + if (a->params.len) + irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, + a->subcommand, "No acknowledgable capabilities supported"); +} + +static void +irc_handle_cap_end (struct client *c, struct irc_cap_args *a) +{ + (void) a; + + c->cap_negotiating = false; + irc_try_finish_registration (c); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct irc_cap_command +{ + const char *name; + void (*handler) (struct client *, struct irc_cap_args *); +}; + +static void +irc_register_cap_handlers (struct server_context *ctx) +{ + static const struct irc_cap_command cap_handlers[] = + { + { "LS", irc_handle_cap_ls }, + { "LIST", irc_handle_cap_list }, + { "REQ", irc_handle_cap_req }, + { "ACK", irc_handle_cap_ack }, + { "END", irc_handle_cap_end }, + }; + + for (size_t i = 0; i < N_ELEMENTS (cap_handlers); i++) + { + const struct irc_cap_command *cmd = &cap_handlers[i]; + str_map_set (&ctx->cap_handlers, cmd->name, (void *) cmd); + } +} + 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); + struct irc_cap_args args; + args.target = c->nickname ? c->nickname : "*"; + args.subcommand = msg->params.vector[0]; + args.full_params = ""; + str_vector_init (&args.params); - 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); + args.full_params = msg->params.vector[1]; + split_str_ignore_empty (args.full_params, ' ', &args.params); } - 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; - client_send (c, "CAP %s LS :multi-prefix", target); - } - else if (!irc_strcmp (subcommand, "LIST")) - { - struct str_vector caps; - str_vector_init (&caps); - - if (c->caps & IRC_CAP_MULTI_PREFIX) - str_vector_add (&caps, "multi-prefix"); - - char *caps_str = join_str_vector (&caps, ' '); - str_vector_free (&caps); - client_send (c, "CAP %s LIST :%s", target, caps_str); - free (caps_str); - } - else if (!irc_strcmp (subcommand, "REQ")) - { - c->cap_negotiating = true; - - unsigned new_caps = c->caps; - bool success = true; - for (size_t i = 0; i < v.len; i++) - { - bool neg = false; - const char *name = v.vector[i]; - if (*name == '-') - { - neg = true; - name++; - } - unsigned cap = 0; - if (!strcmp (name, "multi-prefix")) - cap = IRC_CAP_MULTI_PREFIX; - else - success = false; - - if (neg) - new_caps &= ~cap; - else - new_caps |= cap; - } - - if (success) - { - c->caps = new_caps; - 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 + struct irc_cap_command *cmd = + str_map_find (&c->ctx->cap_handlers, args.subcommand); + if (!cmd) irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, - subcommand, "Invalid CAP subcommand"); + args.subcommand, "Invalid CAP subcommand"); + else + cmd->handler (c, &args); - str_vector_free (&v); + str_vector_free (&args.params); } static void @@ -1994,7 +2058,7 @@ irc_append_prefixes (struct client *c, struct channel_user *user, if (prefixes.len) { - if (c->caps & IRC_CAP_MULTI_PREFIX) + if (c->caps_enabled & IRC_CAP_MULTI_PREFIX) str_append (output, prefixes.str); else str_append_c (output, prefixes.str[0]); @@ -3508,6 +3572,7 @@ main (int argc, char *argv[]) struct server_context ctx; server_context_init (&ctx); irc_register_handlers (&ctx); + irc_register_cap_handlers (&ctx); struct error *e = NULL; if (!read_config_file (&ctx.config, &e))