Handle IRCv3 servers without any capabilities
[quassel.git] / src / core / coresessioneventprocessor.cpp
index a89020c..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,17 +177,33 @@ 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
+            // others.  It also looks nicer in logs.  Not required.
+            availableCaps.sort();
             // Store what capabilities are available
-            QStringList availableCapPair;
+            QString availableCapName, availableCapValue;
             for (int i = 0; i < availableCaps.count(); ++i) {
                 // Capability may include values, e.g. CAP * LS :multi-prefix sasl=EXTERNAL
-                availableCapPair = availableCaps[i].trimmed().split('=');
-                if(availableCapPair.count() >= 2) {
-                    coreNet->addCap(availableCapPair.at(0).trimmed().toLower(), availableCapPair.at(1).trimmed());
-                } else {
-                    coreNet->addCap(availableCapPair.at(0).trimmed().toLower());
+                // Capability name comes before the first '='.  If no '=' exists, this gets the
+                // whole string instead.
+                availableCapName = availableCaps[i].section('=', 0, 0).trimmed();
+                // Some capabilities include multiple key=value pairs in the listing,
+                // e.g. "sts=duration=31536000,port=6697"
+                // Include everything after the first equal sign as part of the value.  If no '='
+                // exists, this gets an empty string.
+                availableCapValue = availableCaps[i].section('=', 1).trimmed();
+                // Only add the capability if it's non-empty
+                if (!availableCapName.isEmpty()) {
+                    coreNet->addCap(availableCapName, availableCapValue);
                 }
             }
 
@@ -190,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
@@ -205,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