Send extra modes via non-breaking protocol change 180/head
authorShane Synan <digitalcircuit36939@gmail.com>
Fri, 19 Feb 2016 09:17:26 +0000 (03:17 -0600)
committerShane Synan <digitalcircuit36939@gmail.com>
Fri, 19 Feb 2016 09:25:45 +0000 (03:25 -0600)
Send extra user mode updates in backwards-compatible manner.

When multiple modes are received in IrcChannel::joinIrcUsers, call
addUserMode one time for each, then call ircuser->addUserModes() with
the new modes.  Perhaps less efficient, but avoids breaking protocol.

Reduce excess network traffic by only syncing IrcUser::setUserModes
and IrcUser::addUserModes when changes are made.

Preliminary test results on Ubuntu (more testing always welcome!)
* New core, new client - works fine (as one might hope)
* New core, old client - client prints warning to console but works
(IrcChannel modes might internally get out of sync, but visible
 channel list updates with mode changes, multiple modes, etc)
* New core, Quasseldroid alpha/NG - works fine
* Old core, new client - works fine

If desired, the warning and other potential risks could be avoided
entirely by implementing a new Feature flag and only sending the
additional mode information to clients that request it.  However,
this seemed excessive to avoid what in limited testing was only a
warning in the client console log, normally hidden away from sight.

src/common/ircchannel.cpp
src/common/ircuser.cpp
src/core/coresessioneventprocessor.cpp

index c111b9f..6673a79 100644 (file)
@@ -178,7 +178,15 @@ void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &
     for (int i = 0; i < users.count(); i++) {
         ircuser = users[i];
         if (!ircuser || _userModes.contains(ircuser)) {
     for (int i = 0; i < users.count(); i++) {
         ircuser = users[i];
         if (!ircuser || _userModes.contains(ircuser)) {
-            addUserMode(ircuser, modes[i]);
+            if (modes[i].count() > 1) {
+                // Multiple modes received, do it one at a time
+                // TODO Better way of syncing this without breaking protocol?
+                for (int i_m = 0; i_m < modes[i].count(); ++i_m) {
+                    addUserMode(ircuser, modes[i][i_m]);
+                }
+            } else {
+                addUserMode(ircuser, modes[i]);
+            }
             continue;
         }
 
             continue;
         }
 
@@ -190,6 +198,9 @@ void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &
         // If you wonder why there is no counterpart to ircUserJoined:
         // the joins are propagated by the ircuser. The signal ircUserJoined is only for convenience
 
         // If you wonder why there is no counterpart to ircUserJoined:
         // the joins are propagated by the ircuser. The signal ircUserJoined is only for convenience
 
+        // Also update the IRC user's record of modes; this allows easier tracking
+        ircuser->addUserModes(modes[i]);
+
         newNicks << ircuser->nick();
         newModes << modes[i];
         newUsers << ircuser;
         newNicks << ircuser->nick();
         newModes << modes[i];
         newUsers << ircuser;
@@ -280,6 +291,8 @@ void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode)
 
     if (!_userModes[ircuser].contains(mode)) {
         _userModes[ircuser] += mode;
 
     if (!_userModes[ircuser].contains(mode)) {
         _userModes[ircuser] += mode;
+        // Also update the IRC user's record of modes; this allows easier tracking
+        ircuser->addUserModes(mode);
         QString nick = ircuser->nick();
         SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
         emit ircUserModeAdded(ircuser, mode);
         QString nick = ircuser->nick();
         SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
         emit ircUserModeAdded(ircuser, mode);
@@ -301,6 +314,8 @@ void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
 
     if (_userModes[ircuser].contains(mode)) {
         _userModes[ircuser].remove(mode);
 
     if (_userModes[ircuser].contains(mode)) {
         _userModes[ircuser].remove(mode);
+        // Also update the IRC user's record of modes; this allows easier tracking
+        ircuser->removeUserModes(mode);
         QString nick = ircuser->nick();
         SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
         emit ircUserModeRemoved(ircuser, mode);
         QString nick = ircuser->nick();
         SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
         emit ircUserModeRemoved(ircuser, mode);
index 32ff794..15220f7 100644 (file)
@@ -346,9 +346,11 @@ void IrcUser::channelDestroyed()
 
 void IrcUser::setUserModes(const QString &modes)
 {
 
 void IrcUser::setUserModes(const QString &modes)
 {
-    _userModes = modes;
-    SYNC(ARG(modes))
-    emit userModesSet(modes);
+    if (_userModes != modes) {
+        _userModes = modes;
+        SYNC(ARG(modes))
+        emit userModesSet(modes);
+    }
 }
 
 
 }
 
 
@@ -357,13 +359,19 @@ void IrcUser::addUserModes(const QString &modes)
     if (modes.isEmpty())
         return;
 
     if (modes.isEmpty())
         return;
 
+    // Don't needlessly sync when no changes are made
+    bool changesMade = false;
     for (int i = 0; i < modes.count(); i++) {
     for (int i = 0; i < modes.count(); i++) {
-        if (!_userModes.contains(modes[i]))
+        if (!_userModes.contains(modes[i])) {
             _userModes += modes[i];
             _userModes += modes[i];
+            changesMade = true;
+        }
     }
 
     }
 
-    SYNC(ARG(modes))
-    emit userModesAdded(modes);
+    if (changesMade) {
+        SYNC(ARG(modes))
+        emit userModesAdded(modes);
+    }
 }
 
 
 }
 
 
index 43f44db..b5981dd 100644 (file)
@@ -987,10 +987,9 @@ void CoreSessionEventProcessor::processIrcEvent353(IrcEvent *e)
             // 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.
             // 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]));
+                // Note: sending multiple modes may cause a warning in older clients.
+                // In testing, the clients still seemed to function fine.
+                mode.append(e->network()->prefixToMode(nick[0]));
                 // Remove this mode from the nick
                 nick = nick.remove(0, 1);
             }
                 // Remove this mode from the nick
                 nick = nick.remove(0, 1);
             }