Handle IRCv3 servers without any capabilities
authorShane Synan <digitalcircuit36939@gmail.com>
Mon, 11 Jul 2016 10:44:16 +0000 (06:44 -0400)
committerManuel Nickschas <sputnick@quassel-irc.org>
Wed, 7 Sep 2016 19:29:32 +0000 (21:29 +0200)
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
src/core/corenetwork.h
src/core/coresessioneventprocessor.cpp

index 34bb426..e3963c4 100644 (file)
@@ -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;
     }
 }
 
index 8d5f720..2bf57d6 100644 (file)
@@ -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.
      *
index ee66717..7e6a5f4 100644 (file)
@@ -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