From 17f430043ac229e091e18670be826c2d448f0408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Fri, 28 May 2021 01:25:10 +0200 Subject: [PATCH] degesch: IRCv3.2 capability negotiation We can receive and display capability values now. --- NEWS | 2 ++ degesch.c | 67 ++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/NEWS b/NEWS index 4b79444..5ccd44c 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ * degesch: added a /squery command for IRCnet + * degesch: now supporting IRCv3.2 capability negotiation + 1.1.0 (2020-10-31) "What Do You Mean By 'This Isn't Germany'?" diff --git a/degesch.c b/degesch.c index cbc13da..8b0269b 100644 --- a/degesch.c +++ b/degesch.c @@ -1718,6 +1718,7 @@ struct server char *irc_user_host; ///< Our current user@host bool autoaway_active; ///< Autoaway is currently active + struct strv cap_ls_buf; ///< Buffer for IRCv3.2 CAP LS bool cap_echo_message; ///< Whether the server echoes messages bool cap_away_notify; ///< Whether we get AWAY notifications @@ -1841,6 +1842,7 @@ server_new (struct poller *poller) self->irc_user_mode = str_make (); + self->cap_ls_buf = strv_make (); server_init_specifics (self); return self; } @@ -1887,6 +1889,7 @@ server_destroy (struct server *self) str_free (&self->irc_user_mode); free (self->irc_user_host); + strv_free (&self->cap_ls_buf); server_free_specifics (self); free (self); } @@ -4956,6 +4959,7 @@ irc_destroy_state (struct server *s) str_reset (&s->irc_user_mode); cstr_set (&s->irc_user_host, NULL); + strv_reset (&s->cap_ls_buf); s->cap_away_notify = false; s->cap_echo_message = false; @@ -5692,9 +5696,9 @@ irc_register (struct server *s) const char *realname = get_config_string (s->config, "realname"); hard_assert (username && realname); - // Start IRCv3.1 capability negotiation; + // Start IRCv3 capability negotiation, with up to 3.2 features; // at worst the server will ignore this or send a harmless error message - irc_send (s, "CAP LS"); + irc_send (s, "CAP LS 302"); const char *password = get_config_string (s->config, "password"); if (password) @@ -6537,6 +6541,39 @@ irc_handle_away (struct server *s, const struct irc_message *msg) user->away = !!msg->params.len; } +static void +irc_process_cap_ls (struct server *s) +{ + log_server_status (s, s->buffer, + "#s: #&S", "Capabilities supported", strv_join (&s->cap_ls_buf, " ")); + + struct strv chosen = strv_make (); + struct strv use = strv_make (); + + cstr_split (get_config_string (s->config, "capabilities"), ",", true, &use); + + // Filter server capabilities for ones we can make use of + for (size_t i = 0; i < s->cap_ls_buf.len; i++) + { + const char *cap = s->cap_ls_buf.vector[i]; + size_t cap_name_len = strcspn (cap, "="); + for (size_t k = 0; k < use.len; k++) + if (!strncasecmp_ascii (use.vector[k], cap, cap_name_len)) + strv_append_owned (&chosen, xstrndup (cap, cap_name_len)); + } + strv_reset (&s->cap_ls_buf); + + char *chosen_str = strv_join (&chosen, " "); + strv_free (&chosen); + strv_free (&use); + + // XXX: with IRCv3.2, this may end up being too long for one message, + // and we need to be careful with CAP END. One probably has to count + // the number of sent CAP REQ vs the number of received CAP ACK/NAK. + irc_send (s, "CAP REQ :%s", chosen_str); + free (chosen_str); +} + static void irc_handle_cap (struct server *s, const struct irc_message *msg) { @@ -6577,30 +6614,14 @@ irc_handle_cap (struct server *s, const struct irc_message *msg) } else if (!strcasecmp_ascii (subcommand, "LS")) { - log_server_status (s, s->buffer, - "#s: #S", "Capabilities supported", args); - struct strv chosen = strv_make (); - struct strv use = strv_make (); - - cstr_split (get_config_string (s->config, "capabilities"), - ",", true, &use); - - // Filter server capabilities for ones we can make use of - for (size_t i = 0; i < v.len; i++) + if (msg->params.len > 3 && !strcmp (args, "*")) + cstr_split (msg->params.vector[3], " ", true, &s->cap_ls_buf); + else { - const char *cap = v.vector[i]; - for (size_t k = 0; k < use.len; k++) - if (!strcasecmp_ascii (use.vector[k], cap)) - strv_append (&chosen, cap); + strv_append_vector (&s->cap_ls_buf, v.vector); + irc_process_cap_ls (s); } - - char *chosen_str = strv_join (&chosen, " "); - strv_free (&chosen); - strv_free (&use); - - irc_send (s, "CAP REQ :%s", chosen_str); - free (chosen_str); } strv_free (&v);