Add support for multi-prefix
[quassel.git] / src / core / coresessioneventprocessor.cpp
index a47b2c0..43f44db 100644 (file)
@@ -191,6 +191,13 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e)
                     // Only request SASL if it's enabled
                     if (coreNet->networkInfo().useSasl)
                         queueCurrentCap = true;
+                } else if (availableCapPair.at(0).startsWith("away-notify") ||
+                           availableCapPair.at(0).startsWith("account-notify") ||
+                           availableCapPair.at(0).startsWith("extended-join") ||
+                           availableCapPair.at(0).startsWith("userhost-in-names") ||
+                           availableCapPair.at(0).startsWith("multi-prefix")) {
+                    // Always request these capabilities if available
+                    queueCurrentCap = true;
                 }
                 if (queueCurrentCap) {
                     if(availableCapPair.count() >= 2)
@@ -249,6 +256,50 @@ void CoreSessionEventProcessor::processIrcEventCap(IrcEvent *e)
     }
 }
 
+/* IRCv3 account-notify
+ * Log in:  ":nick!user@host ACCOUNT accountname"
+ * Log out: ":nick!user@host ACCOUNT *" */
+void CoreSessionEventProcessor::processIrcEventAccount(IrcEvent *e)
+{
+    if (!checkParamCount(e, 1))
+        return;
+
+    IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
+    if (ircuser) {
+        // FIXME Keep track of authed user account, requires adding support to ircuser.h/cpp
+        /*
+        if (e->params().at(0) != "*") {
+            // Account logged in
+            qDebug() << "account-notify:" << ircuser->nick() << "logged in to" << e->params().at(0);
+        } else {
+            // Account logged out
+            qDebug() << "account-notify:" << ircuser->nick() << "logged out";
+        }
+        */
+    } else {
+        qDebug() << "Received account-notify data for unknown user" << e->prefix();
+    }
+}
+
+/* IRCv3 away-notify - ":nick!user@host AWAY [:message]" */
+void CoreSessionEventProcessor::processIrcEventAway(IrcEvent *e)
+{
+    if (!checkParamCount(e, 2))
+        return;
+
+    // Nick is sent as part of parameters in order to split user/server decoding
+    IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
+    if (ircuser) {
+        if (!e->params().at(1).isEmpty()) {
+            ircuser->setAway(true);
+            ircuser->setAwayMessage(e->params().at(1));
+        } else {
+            ircuser->setAway(false);
+        }
+    } else {
+        qDebug() << "Received away-notify data for unknown user" << e->params().at(0);
+    }
+}
 
 void CoreSessionEventProcessor::processIrcEventInvite(IrcEvent *e)
 {
@@ -270,6 +321,17 @@ void CoreSessionEventProcessor::processIrcEventJoin(IrcEvent *e)
     QString channel = e->params()[0];
     IrcUser *ircuser = net->updateNickFromMask(e->prefix());
 
+    if (net->useCapExtendedJoin()) {
+        if (!checkParamCount(e, 3))
+            return;
+        // If logged in, :nick!user@host JOIN #channelname accountname :Real Name
+        // If logged out, :nick!user@host JOIN #channelname * :Real Name
+        // See:  http://ircv3.net/specs/extensions/extended-join-3.1.html
+        // FIXME Keep track of authed user account, requires adding support to ircuser.h/cpp
+        ircuser->setRealName(e->params()[2]);
+    }
+    // Else :nick!user@host JOIN #channelname
+
     bool handledByNetsplit = false;
     foreach(Netsplit* n, _netsplits.value(e->network())) {
         handledByNetsplit = n->userJoined(e->prefix(), channel);
@@ -277,6 +339,12 @@ void CoreSessionEventProcessor::processIrcEventJoin(IrcEvent *e)
             break;
     }
 
+    // If using away-notify, check new users.  Works around buggy IRC servers
+    // forgetting to send :away messages for users who join channels when away.
+    if (coreNetwork(e)->useCapAwayNotify()) {
+        coreNetwork(e)->queueAutoWhoOneshot(ircuser->nick());
+    }
+
     if (!handledByNetsplit)
         ircuser->joinChannel(channel);
     else
@@ -851,14 +919,40 @@ void CoreSessionEventProcessor::processIrcEvent352(IrcEvent *e)
         ircuser->setUser(e->params()[1]);
         ircuser->setHost(e->params()[2]);
 
-        bool away = e->params()[5].startsWith("G");
+        bool away = e->params()[5].contains("G", Qt::CaseInsensitive);
         ircuser->setAway(away);
         ircuser->setServer(e->params()[3]);
         ircuser->setRealName(e->params().last().section(" ", 1));
+
+        if (coreNetwork(e)->useCapMultiPrefix()) {
+            // If multi-prefix is enabled, all modes will be sent in WHO replies.
+            // :kenny.chatspike.net 352 guest #test grawity broken.symlink *.chatspike.net grawity H@%+ :0 Mantas M.
+            // See: http://ircv3.net/specs/extensions/multi-prefix-3.1.html
+            QString uncheckedModes = e->params()[5];
+            QString validModes = QString();
+            while (!uncheckedModes.isEmpty()) {
+                // Mode found in 1 left-most character, add it to the list
+                if (e->network()->prefixes().contains(uncheckedModes[0])) {
+                    validModes.append(e->network()->prefixToMode(uncheckedModes[0]));
+                }
+                // Remove this mode from the list of unchecked modes
+                uncheckedModes = uncheckedModes.remove(0, 1);
+            }
+
+            // Some IRC servers decide to not follow the spec, returning only -some- of the user
+            // modes in WHO despite listing them all in NAMES.  For now, assume it can only add
+            // and not take away.  *sigh*
+            if (!validModes.isEmpty())
+                ircuser->addUserModes(validModes);
+        }
     }
 
-    if (coreNetwork(e)->isAutoWhoInProgress(channel))
+    // Check if channel name has a who in progress.
+    // If not, then check if user nick exists and has a who in progress.
+    if (coreNetwork(e)->isAutoWhoInProgress(channel) ||
+        (ircuser && coreNetwork(e)->isAutoWhoInProgress(ircuser->nick()))) {
         e->setFlag(EventManager::Silent);
+    }
 }
 
 
@@ -881,14 +975,36 @@ void CoreSessionEventProcessor::processIrcEvent353(IrcEvent *e)
     QStringList nicks;
     QStringList modes;
 
+    // Cache result of multi-prefix to avoid unneeded casts and lookups with each iteration.
+    bool _useCapMultiPrefix = coreNetwork(e)->useCapMultiPrefix();
+
     foreach(QString nick, e->params()[2].split(' ', QString::SkipEmptyParts)) {
         QString mode;
 
-        if (e->network()->prefixes().contains(nick[0])) {
+        if (_useCapMultiPrefix) {
+            // If multi-prefix is enabled, all modes will be sent in NAMES replies.
+            // :hades.arpa 353 guest = #tethys :~&@%+aji &@Attila @+alyx +KindOne Argure
+            // See: http://ircv3.net/specs/extensions/multi-prefix-3.1.html
+            while (e->network()->prefixes().contains(nick[0])) {
+                // Mode found in 1 left-most character, add it to the list.
+                // FIXME Only allow one possible mode to avoid a warning in older clients
+                if (mode.isEmpty())
+                    mode.append(e->network()->prefixToMode(nick[0]));
+                //mode.append(e->network()->prefixToMode(nick[0]));
+                // Remove this mode from the nick
+                nick = nick.remove(0, 1);
+            }
+        } else if (e->network()->prefixes().contains(nick[0])) {
+            // Multi-prefix is disabled and a mode prefix was found.
             mode = e->network()->prefixToMode(nick[0]);
             nick = nick.mid(1);
         }
 
+        // If userhost-in-names capability is enabled, the following will be
+        // in the form "nick!user@host" rather than "nick".  This works without
+        // special handling as the following use nickFromHost() as needed.
+        // See: http://ircv3.net/specs/extensions/userhost-in-names-3.2.html
+
         nicks << nick;
         modes << mode;
     }