aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kike.c211
1 files changed, 138 insertions, 73 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 (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))