From 36d432bc4ee56dd9a89850a8dbcc58ea12d8ee33 Mon Sep 17 00:00:00 2001 From: Shane Synan Date: Mon, 11 Jul 2016 06:44:16 -0400 Subject: [PATCH] Handle IRCv3 servers without any capabilities When servers implement CAP LS but do not offer any capabilities, end negotiation and continue connecting as usual. Modify processIrcEventCap to allow for commands with two parameters. CAP LS and CAP LIST can have empty replies to signify no capabilities are available/active. Add a dedicated 'endCapNegotiation' function to avoid code duplication. Fixes repeated connection timeouts for IRCv3-compliant servers that don't have any capabilities to offer. --- src/core/corenetwork.cpp | 22 ++++++++++++++++------ src/core/corenetwork.h | 8 ++++++++ src/core/coresessioneventprocessor.cpp | 23 +++++++++++++++++++++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 34bb426f..e3963c4e 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -1006,8 +1006,13 @@ QString CoreNetwork::takeQueuedCap() void CoreNetwork::beginCapNegotiation() { // Don't begin negotiation if no capabilities are queued to request - if (!capNegotiationInProgress()) + if (!capNegotiationInProgress()) { + // If the server doesn't have any capabilities, but supports CAP LS, continue on with the + // normal connection. + displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("No capabilities available")); + endCapNegotiation(); return; + } _capNegotiationActive = true; displayMsg(Message::Server, BufferInfo::StatusBuffer, "", @@ -1036,11 +1041,16 @@ void CoreNetwork::sendNextCap() _capNegotiationActive = false; } - // If nick registration is already complete, CAP END is not required - if (!_capInitialNegotiationEnded) { - putRawLine(serverEncode(QString("CAP END"))); - _capInitialNegotiationEnded = true; - } + endCapNegotiation(); + } +} + +void CoreNetwork::endCapNegotiation() +{ + // If nick registration is already complete, CAP END is not required + if (!_capInitialNegotiationEnded) { + putRawLine(serverEncode(QString("CAP END"))); + _capInitialNegotiationEnded = true; } } diff --git a/src/core/corenetwork.h b/src/core/corenetwork.h index 8d5f7201..2bf57d6e 100644 --- a/src/core/corenetwork.h +++ b/src/core/corenetwork.h @@ -130,6 +130,14 @@ public: */ void beginCapNegotiation(); + /** + * Ends capability negotiation. + * + * This won't have effect if other CAP commands are in the command queue before calling this + * command. It should only be called when capability negotiation is complete. + */ + void endCapNegotiation(); + /** * List of capabilities requiring further core<->server messages to configure. * diff --git a/src/core/coresessioneventprocessor.cpp b/src/core/coresessioneventprocessor.cpp index ee66717e..7e6a5f4a 100644 --- a/src/core/coresessioneventprocessor.cpp +++ b/src/core/coresessioneventprocessor.cpp @@ -155,7 +155,12 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e) // Handle capability negotiation // See: http://ircv3.net/specs/core/capability-negotiation-3.2.html // And: http://ircv3.net/specs/core/capability-negotiation-3.1.html - if (e->params().count() >= 3) { + + // All commands require at least 2 parameters + if (!checkParamCount(e, 2)) + return; + + if (e->params().count() >= 2) { CoreNetwork *coreNet = coreNetwork(e); QString capCommand = e->params().at(1).trimmed().toUpper(); if (capCommand == "LS" || capCommand == "NEW") { @@ -172,7 +177,13 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e) } else { // Single line reply capListFinished = true; - availableCaps = e->params().at(2).split(' '); + if (e->params().count() >= 3) { + // Some capabilities are specified, add them + availableCaps = e->params().at(2).split(' '); + } else { + // No capabilities available, add an empty list + availableCaps = QStringList(); + } } // Sort capabilities before requesting for consistency among networks. This may avoid // unexpected cases when some networks offer capabilities in a different order than @@ -200,6 +211,10 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e) if (capListFinished) coreNet->beginCapNegotiation(); } else if (capCommand == "ACK") { + // CAP ACK requires at least 3 parameters (no empty response allowed) + if (!checkParamCount(e, 3)) + return; + // Server: CAP * ACK :multi-prefix sasl // Got the capability we want, handle as needed. // As only one capability is requested at a time, no need to split @@ -215,6 +230,10 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e) coreNet->sendNextCap(); } } else if (capCommand == "NAK" || capCommand == "DEL") { + // CAP NAK/DEL require at least 3 parameters (no empty response allowed) + if (!checkParamCount(e, 3)) + return; + // Either something went wrong with this capability, or it is no longer supported // > For CAP NAK // Server: CAP * NAK :multi-prefix sasl -- 2.20.1