diff options
author | Přemysl Janouch <p.janouch@gmail.com> | 2015-06-12 21:27:17 +0200 |
---|---|---|
committer | Přemysl Janouch <p.janouch@gmail.com> | 2015-06-12 21:27:17 +0200 |
commit | 1d53b87016965bb9d4fbb895713c6ba25fc4241e (patch) | |
tree | b667edc39d0a02e3188d7c812e2dcf0252b3ffc2 | |
parent | bf01fb7aa3101d061dc9271e6f863f5b634ca408 (diff) | |
download | xK-1d53b87016965bb9d4fbb895713c6ba25fc4241e.tar.gz xK-1d53b87016965bb9d4fbb895713c6ba25fc4241e.tar.xz xK-1d53b87016965bb9d4fbb895713c6ba25fc4241e.zip |
kike: refactor CAP processing
-rw-r--r-- | kike.c | 211 |
1 files changed, 138 insertions, 73 deletions
@@ -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 (const struct irc_message *msg, struct client *c) +irc_handle_cap_ls (struct client *c, struct irc_cap_args *a) { - if (msg->params.len < 1) - RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command); + 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"); - struct str_vector v; - str_vector_init (&v); + c->cap_negotiating = true; + client_send (c, "CAP %s LS :multi-prefix", a->target); +} - 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); - } +static void +irc_handle_cap_list (struct client *c, struct irc_cap_args *a) +{ + struct str_vector caps; + str_vector_init (&caps); - 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"); + if (c->caps_enabled & IRC_CAP_MULTI_PREFIX) + str_vector_add (&caps, "multi-prefix"); - 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); + 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); +} - if (c->caps & IRC_CAP_MULTI_PREFIX) - str_vector_add (&caps, "multi-prefix"); +static unsigned +irc_decode_capability (const char *name) +{ + if (!strcmp (name, "multi-prefix")) + return IRC_CAP_MULTI_PREFIX; + return 0; +} - 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; +static void +irc_handle_cap_req (struct client *c, struct irc_cap_args *a) +{ + c->cap_negotiating = true; - unsigned new_caps = c->caps; - bool success = true; - for (size_t i = 0; i < v.len; i++) + 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 == '-') { - 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; + removing = true; + name++; } - if (success) - { - c->caps = new_caps; - client_send (c, "CAP %s NAK :%s", target, params); - } + unsigned cap; + if (!(cap = irc_decode_capability (name))) + success = false; + else if (removing) + new_caps &= ~cap; else - client_send (c, "CAP %s ACK :%s", target, params); + new_caps |= cap; } - else if (!irc_strcmp (subcommand, "ACK")) + + 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[] = { - if (v.len) - irc_send_reply (c, IRC_ERR_INVALIDCAPCMD, - subcommand, "No acknowledgable capabilities supported"); + { "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); } - else if (!irc_strcmp (subcommand, "END")) +} + +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 irc_cap_args args; + args.target = c->nickname ? c->nickname : "*"; + args.subcommand = msg->params.vector[0]; + args.full_params = ""; + str_vector_init (&args.params); + + if (msg->params.len > 1) { - c->cap_negotiating = false; - irc_try_finish_registration (c); + args.full_params = msg->params.vector[1]; + split_str_ignore_empty (args.full_params, ' ', &args.params); } - 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)) |