Improve performance of PostgreSQL for large databases.
[quassel.git] / src / core / coresessioneventprocessor.cpp
index cd3f808..e4621bf 100644 (file)
@@ -218,17 +218,33 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e)
         }
 
         // 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
-        QString acceptedCap = e->params().at(2).trimmed().toLower();
-
-        // Mark this cap as accepted
-        coreNet->acknowledgeCap(acceptedCap);
+        // Got the capabilities we want, handle as needed.
+        QStringList acceptedCaps;
+        acceptedCaps = e->params().at(2).split(' ');
+
+        // Store what capability was acknowledged
+        QString acceptedCap;
+
+        // Keep track of whether or not a capability requires further configuration.  Due to queuing
+        // logic in CoreNetwork::queueCap(), this shouldn't ever happen when more than one
+        // capability is requested, but it's better to handle edge cases or faulty servers.
+        bool capsRequireConfiguration = false;
+
+        for (int i = 0; i < acceptedCaps.count(); ++i) {
+            acceptedCap = acceptedCaps[i].trimmed().toLower();
+            // Mark this cap as accepted
+            coreNet->acknowledgeCap(acceptedCap);
+            if (!capsRequireConfiguration &&
+                    coreNet->capsRequiringConfiguration.contains(acceptedCap)) {
+                capsRequireConfiguration = true;
+                // Some capabilities (e.g. SASL) require further messages to finish.  If so, do NOT
+                // send the next capability; it will be handled elsewhere in CoreNetwork.
+                // Otherwise, allow moving on to the next capability.
+            }
+        }
 
-        if (!coreNet->capsRequiringConfiguration.contains(acceptedCap)) {
-            // Some capabilities (e.g. SASL) require further messages to finish.  If so, do NOT
-            // send the next capability; it will be handled elsewhere in CoreNetwork.
-            // Otherwise, move on to the next capability
+        if (!capsRequireConfiguration) {
+            // No additional configuration required, move on to the next capability
             coreNet->sendNextCap();
         }
     } else if (capCommand == "NAK" || capCommand == "DEL") {
@@ -242,7 +258,7 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e)
             return;
         }
 
-        // Either something went wrong with this capability, or it is no longer supported
+        // Either something went wrong with the capabilities, or they are no longer supported
         // > For CAP NAK
         // Server: CAP * NAK :multi-prefix sasl
         // > For CAP DEL
@@ -252,17 +268,29 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e)
         QStringList removedCaps;
         removedCaps = e->params().at(2).split(' ');
 
-        // Store what capability was denied or removed
+        // Store the capabilities that were denied or removed
         QString removedCap;
         for (int i = 0; i < removedCaps.count(); ++i) {
             removedCap = removedCaps[i].trimmed().toLower();
-            // Mark this cap as removed
+            // Mark this cap as removed.
+            // For CAP DEL, removes it from use.
+            // For CAP NAK when received before negotiation enabled these capabilities, removeCap()
+            // should do nothing.  This merely guards against non-spec servers sending an
+            // unsolicited CAP ACK then later removing that capability.
             coreNet->removeCap(removedCap);
         }
 
         if (capCommand == "NAK") {
-            // Continue negotiation only if this is the result of a denied cap, not a removed
-            // cap
+            // Continue negotiation only if this is the result of denied caps, not removed caps
+            if (removedCaps.count() > 1) {
+                // We've received a CAP NAK reply to multiple capabilities at once.  Unfortunately,
+                // we don't know which capability failed and which ones are valid to re-request, so
+                // individually retry each capability from the failed bundle.
+                // See CoreNetwork::retryCapsIndividually() for more details.
+                coreNet->retryCapsIndividually();
+                // Still need to call sendNextCap() to carry on.
+            }
+            // Carry on with negotiation
             coreNet->sendNextCap();
         }
     }
@@ -634,6 +662,21 @@ void CoreSessionEventProcessor::processIrcEventTopic(IrcEvent *e)
     }
 }
 
+/* ERROR - "ERROR :reason"
+Example:  ERROR :Closing Link: nickname[xxx.xxx.xxx.xxx] (Large base64 image paste.)
+See https://tools.ietf.org/html/rfc2812#section-3.7.4 */
+void CoreSessionEventProcessor::processIrcEventError(IrcEvent *e)
+{
+    if (!checkParamCount(e, 1))
+        return;
+
+    if (coreNetwork(e)->disconnectExpected()) {
+        // During QUIT, the server should send an error (often, but not always, "Closing Link"). As
+        // we're expecting it, don't show this to the user.
+        e->setFlag(EventManager::Silent);
+    }
+}
+
 
 #ifdef HAVE_QCA2
 void CoreSessionEventProcessor::processKeyEvent(KeyEvent *e)
@@ -965,16 +1008,24 @@ void CoreSessionEventProcessor::processIrcEvent352(IrcEvent *e)
         return;
 
     QString channel = e->params()[0];
-    IrcUser *ircuser = e->network()->ircUser(e->params()[4]);
+    // Store the nick separate from ircuser for AutoWho check below
+    QString nick = e->params()[4];
+    IrcUser *ircuser = e->network()->ircUser(nick);
     if (ircuser) {
+        // Only process the WHO information if an IRC user exists.  Don't create an IRC user here;
+        // there's no way to track when the user quits, which would leave a phantom IrcUser lying
+        // around.
+        // NOTE:  Whenever MONITOR support is introduced, the IrcUser will be created by an
+        // RPL_MONONLINE numeric before any WHO commands are run.
         processWhoInformation(e->network(), channel, ircuser, e->params()[3], e->params()[1],
                 e->params()[2], e->params()[5], e->params().last().section(" ", 1));
     }
 
     // Check if channel name has a who in progress.
-    // If not, then check if user nick exists and has a who in progress.
+    // If not, then check if user nickname has a who in progress.  Use nick directly; don't use
+    // ircuser as that may be deleted (e.g. nick joins channel, leaves before WHO reply received).
     if (coreNetwork(e)->isAutoWhoInProgress(channel) ||
-        (ircuser && coreNetwork(e)->isAutoWhoInProgress(ircuser->nick()))) {
+        (coreNetwork(e)->isAutoWhoInProgress(nick))) {
         e->setFlag(EventManager::Silent);
     }
 }
@@ -1061,8 +1112,14 @@ void CoreSessionEventProcessor::processIrcEvent354(IrcEvent *e)
         return;
 
     QString channel = e->params()[1];
-    IrcUser *ircuser = e->network()->ircUser(e->params()[5]);
+    QString nick = e->params()[5];
+    IrcUser *ircuser = e->network()->ircUser(nick);
     if (ircuser) {
+        // Only process the WHO information if an IRC user exists.  Don't create an IRC user here;
+        // there's no way to track when the user quits, which would leave a phantom IrcUser lying
+        // around.
+        // NOTE:  Whenever MONITOR support is introduced, the IrcUser will be created by an
+        // RPL_MONONLINE numeric before any WHO commands are run.
         processWhoInformation(e->network(), channel, ircuser, e->params()[4], e->params()[2],
                 e->params()[3], e->params()[6], e->params().last());
         // Don't use .section(" ", 1) with WHOX replies, for there's no hopcount to trim out
@@ -1080,9 +1137,10 @@ void CoreSessionEventProcessor::processIrcEvent354(IrcEvent *e)
     }
 
     // Check if channel name has a who in progress.
-    // If not, then check if user nick exists and has a who in progress.
+    // If not, then check if user nickname has a who in progress.  Use nick directly; don't use
+    // ircuser as that may be deleted (e.g. nick joins channel, leaves before WHO reply received).
     if (coreNetwork(e)->isAutoWhoInProgress(channel) ||
-        (ircuser && coreNetwork(e)->isAutoWhoInProgress(ircuser->nick()))) {
+        (coreNetwork(e)->isAutoWhoInProgress(nick))) {
         e->setFlag(EventManager::Silent);
     }
 }