From 7d9f3acfc905ca5d0c9747699eb21d2d95fbf821 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Sun, 24 Nov 2019 13:01:48 -0800 Subject: [PATCH] Fix capability negotiation ending before sasl finishes with multi-line cap Closes #2398 --- src/common/hexchat.h | 1 + src/common/inbound.c | 21 ++++++++++++++++----- src/common/inbound.h | 2 +- src/common/proto-irc.c | 3 ++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/common/hexchat.h b/src/common/hexchat.h index 16f10ded..c64e44a0 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -578,6 +578,7 @@ typedef struct server unsigned int sasl_mech; /* mechanism for sasl auth */ unsigned int sent_capend:1; /* have sent CAP END yet */ unsigned int waiting_on_cap:1; /* waiting on another line of CAP LS */ + unsigned int waiting_on_sasl:1; /* waiting on sasl */ #ifdef USE_OPENSSL unsigned int use_ssl:1; /* is server SSL capable? */ unsigned int accept_invalid_cert:1;/* ignore result of server's cert. verify */ diff --git a/src/common/inbound.c b/src/common/inbound.c index 9c77b231..eb7054f2 100644 --- a/src/common/inbound.c +++ b/src/common/inbound.c @@ -1768,7 +1768,6 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, { char buffer[500]; /* buffer for requesting capabilities and emitting the signal */ gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */ - gboolean want_sasl = FALSE; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */ char **extensions; int i; @@ -1816,7 +1815,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, serv->sasl_mech = sasl_mech; } want_cap = TRUE; - want_sasl = TRUE; + serv->waiting_on_sasl = TRUE; g_strlcat (buffer, "sasl ", sizeof(buffer)); continue; } @@ -1842,7 +1841,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, tags_data->timestamp); tcp_sendf (serv, "%s\r\n", g_strchomp (buffer)); } - if (!want_sasl && !serv->waiting_on_cap) + if (!serv->waiting_on_sasl && !serv->waiting_on_cap) { /* if we use SASL, CAP END is dealt via raw numerics */ serv->sent_capend = TRUE; @@ -1851,13 +1850,25 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, } void -inbound_cap_nak (server *serv, const message_tags_data *tags_data) +inbound_cap_nak (server *serv, char *extensions_str, const message_tags_data *tags_data) { - if (!serv->waiting_on_cap && !serv->sent_capend) + char **extensions; + int i; + + extensions = g_strsplit (extensions_str, " ", 0); + for (i=0; extensions[i]; i++) + { + if (!g_strcmp0 (extensions[i], "sasl")) + serv->waiting_on_sasl = FALSE; + } + + if (!serv->waiting_on_cap && !serv->waiting_on_sasl && !serv->sent_capend) { serv->sent_capend = TRUE; tcp_send_len (serv, "CAP END\r\n", 9); } + + g_strfreev (extensions); } void diff --git a/src/common/inbound.h b/src/common/inbound.h index 83e78d5d..6e7c171f 100644 --- a/src/common/inbound.h +++ b/src/common/inbound.h @@ -92,7 +92,7 @@ void inbound_cap_ack (server *serv, char *nick, char *extensions, const message_tags_data *tags_data); void inbound_cap_ls (server *serv, char *nick, char *extensions, const message_tags_data *tags_data); -void inbound_cap_nak (server *serv, const message_tags_data *tags_data); +void inbound_cap_nak (server *serv, char *extensions, const message_tags_data *tags_data); void inbound_cap_list (server *serv, char *nick, char *extensions, const message_tags_data *tags_data); void inbound_cap_del (server *serv, char *nick, char *extensions, diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c index 497cb6ca..59a60a8f 100644 --- a/src/common/proto-irc.c +++ b/src/common/proto-irc.c @@ -957,6 +957,7 @@ process_numeric (session * sess, int n, EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLRESPONSE, serv->server_session, word[1], word[2], word[3], ++word_eol[4], 0, tags_data->timestamp); + serv->waiting_on_sasl = FALSE; if (!serv->sent_capend) { serv->sent_capend = TRUE; @@ -1330,7 +1331,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], } else if (strncasecmp (word[4], "NAK", 3) == 0) { - inbound_cap_nak (serv, tags_data); + inbound_cap_nak (serv, word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data); } else if (strncasecmp (word[4], "LIST", 4) == 0) {