degesch: IRCv3.2 capability negotiation

We can receive and display capability values now.
This commit is contained in:
Přemysl Eric Janouch 2021-05-28 01:25:10 +02:00
parent 735096d76d
commit 17f430043a
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 46 additions and 23 deletions

2
NEWS
View File

@ -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'?"

View File

@ -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);