[Followup PR-495] Fixes backwards compatibility issues
[quassel.git] / src / common / ircchannel.cpp
index 89b3b33..b03f16a 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   Copyright (C) 2005-2020 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 
 #include "ircchannel.h"
 
-#include "network.h"
-#include "ircuser.h"
-#include "util.h"
-
-#include <QMapIterator>
+#include <QDebug>
 #include <QHashIterator>
+#include <QMapIterator>
 #include <QTextCodec>
 
-#include <QDebug>
-
-INIT_SYNCABLE_OBJECT(IrcChannel)
-IrcChannel::IrcChannel(const QString &channelname, Network *network)
-    : SyncableObject(network),
-    _initialized(false),
-    _name(channelname),
-    _topic(QString()),
-    _encrypted(false),
-    _network(network),
-    _codecForEncoding(0),
-    _codecForDecoding(0)
-{
-    setObjectName(QString::number(network->networkId().toInt()) + "/" +  channelname);
-}
-
+#include "ircuser.h"
+#include "network.h"
+#include "util.h"
 
-IrcChannel::~IrcChannel()
+IrcChannel::IrcChannel(const QString& channelname, Network* network)
+    : SyncableObject(network)
+    , _initialized(false)
+    , _name(channelname)
+    , _topic(QString())
+    , _encrypted(false)
+    , _network(network)
+    , _codecForEncoding(nullptr)
+    , _codecForDecoding(nullptr)
 {
+    setObjectName(QString::number(network->networkId().toInt()) + "/" + channelname);
 }
 
-
 // ====================
 //  PUBLIC:
 // ====================
-bool IrcChannel::isKnownUser(IrcUser *ircuser) const
+bool IrcChannel::isKnownUser(IrcUserircuser) const
 {
-    if (ircuser == 0) {
+    if (ircuser == nullptr) {
         qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
         return false;
     }
 
     if (!_userModes.contains(ircuser)) {
-        qWarning() << "Channel" << name() << "received data for unknown User" << ircuser->nick();
+        // This can happen e.g. when disconnecting from a network, so don't log a warning
         return false;
     }
 
     return true;
 }
 
-
-bool IrcChannel::isValidChannelUserMode(const QString &mode) const
+bool IrcChannel::isValidChannelUserMode(const QString& mode) const
 {
     bool isvalid = true;
     if (mode.size() > 1) {
-        qWarning() << "Channel" << name() << "received Channel User Mode which is longer then 1 Char:" << mode;
+        qWarning() << "Channel" << name() << "received Channel User Mode which is longer than 1 Char:" << mode;
         isvalid = false;
     }
     return isvalid;
 }
 
-
-QString IrcChannel::userModes(IrcUser *ircuser) const
+QString IrcChannel::userModes(IrcUser* ircuser) const
 {
     if (_userModes.contains(ircuser))
         return _userModes[ircuser];
@@ -88,45 +78,39 @@ QString IrcChannel::userModes(IrcUser *ircuser) const
         return QString();
 }
 
-
-QString IrcChannel::userModes(const QString &nick) const
+QString IrcChannel::userModes(const QString& nick) const
 {
     return userModes(network()->ircUser(nick));
 }
 
-
-void IrcChannel::setCodecForEncoding(const QString &name)
+void IrcChannel::setCodecForEncoding(const QString& name)
 {
     setCodecForEncoding(QTextCodec::codecForName(name.toLatin1()));
 }
 
-
-void IrcChannel::setCodecForEncoding(QTextCodec *codec)
+void IrcChannel::setCodecForEncoding(QTextCodec* codec)
 {
     _codecForEncoding = codec;
 }
 
-
-void IrcChannel::setCodecForDecoding(const QString &name)
+void IrcChannel::setCodecForDecoding(const QString& name)
 {
     setCodecForDecoding(QTextCodec::codecForName(name.toLatin1()));
 }
 
-
-void IrcChannel::setCodecForDecoding(QTextCodec *codec)
+void IrcChannel::setCodecForDecoding(QTextCodec* codec)
 {
     _codecForDecoding = codec;
 }
 
-
-QString IrcChannel::decodeString(const QByteArray &text) const
+QString IrcChannel::decodeString(const QByteArray& text) const
 {
-    if (!codecForDecoding()) return network()->decodeString(text);
+    if (!codecForDecoding())
+        return network()->decodeString(text);
     return ::decodeString(text, _codecForDecoding);
 }
 
-
-QByteArray IrcChannel::encodeString(const QString &string) const
+QByteArray IrcChannel::encodeString(const QString& string) const
 {
     if (codecForEncoding()) {
         return _codecForEncoding->fromUnicode(string);
@@ -134,19 +118,17 @@ QByteArray IrcChannel::encodeString(const QString &string) const
     return network()->encodeString(string);
 }
 
-
 // ====================
 //  PUBLIC SLOTS:
 // ====================
-void IrcChannel::setTopic(const QString &topic)
+void IrcChannel::setTopic(const QStringtopic)
 {
     _topic = topic;
     SYNC(ARG(topic))
     emit topicSet(topic);
 }
 
-
-void IrcChannel::setPassword(const QString &password)
+void IrcChannel::setPassword(const QString& password)
 {
     _password = password;
     SYNC(ARG(password))
@@ -159,8 +141,7 @@ void IrcChannel::setEncrypted(bool encrypted)
     emit encryptedSet(encrypted);
 }
 
-
-void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes)
+void IrcChannel::joinIrcUsers(const QList<IrcUser*>& users, const QStringList& modes)
 {
     if (users.isEmpty())
         return;
@@ -170,28 +151,42 @@ void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &
         return;
     }
 
+    // Sort user modes first
+    const QStringList sortedModes = network()->sortPrefixModes(modes);
+
     QStringList newNicks;
     QStringList newModes;
-    QList<IrcUser *> newUsers;
+    QList<IrcUser*> newUsers;
 
-    IrcUser *ircuser;
+    IrcUserircuser;
     for (int i = 0; i < users.count(); i++) {
         ircuser = users[i];
-        if (!ircuser || _userModes.contains(ircuser)) {
-            addUserMode(ircuser, modes[i]);
+        if (!ircuser)
+            continue;
+        if (_userModes.contains(ircuser)) {
+            if (sortedModes[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 < sortedModes[i].count(); ++i_m) {
+                    addUserMode(ircuser, sortedModes[i][i_m]);
+                }
+            }
+            else {
+                addUserMode(ircuser, sortedModes[i]);
+            }
             continue;
         }
 
-        _userModes[ircuser] = modes[i];
-        ircuser->joinChannel(this);
-        connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
+        _userModes[ircuser] = sortedModes[i];
+        ircuser->joinChannel(this, true);
+        connect(ircuser, &IrcUser::nickSet, this, selectOverload<QString>(&IrcChannel::ircUserNickSet));
 
         // connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
-        // if you wonder why there is no counterpart to ircUserJoined:
-        // the joines are propagted 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
 
         newNicks << ircuser->nick();
-        newModes << modes[i];
+        newModes << sortedModes[i];
         newUsers << ircuser;
     }
 
@@ -202,44 +197,41 @@ void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &
     emit ircUsersJoined(newUsers);
 }
 
-
-void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes)
+void IrcChannel::joinIrcUsers(const QStringList& nicks, const QStringList& modes)
 {
-    QList<IrcUser *> users;
-    foreach(QString nick, nicks)
-    users << network()->newIrcUser(nick);
+    QList<IrcUser*> users;
+    foreach (QString nick, nicks)
+        users << network()->newIrcUser(nick);
     joinIrcUsers(users, modes);
 }
 
-
-void IrcChannel::joinIrcUser(IrcUser *ircuser)
+void IrcChannel::joinIrcUser(IrcUser* ircuser)
 {
-    QList<IrcUser *> users;
+    QList<IrcUser*> users;
     users << ircuser;
     QStringList modes;
     modes << QString();
     joinIrcUsers(users, modes);
 }
 
-
-void IrcChannel::part(IrcUser *ircuser)
+void IrcChannel::part(IrcUser* ircuser)
 {
     if (isKnownUser(ircuser)) {
         _userModes.remove(ircuser);
         ircuser->partChannel(this);
-        // if you wonder why there is no counterpart to ircUserParted:
-        // the joines are propagted by the ircuser. the signal ircUserParted is only for convenience
-        disconnect(ircuser, 0, this, 0);
+        // If you wonder why there is no counterpart to ircUserParted:
+        // the joins are propagted by the ircuser. The signal ircUserParted is only for convenience
+        disconnect(ircuser, nullptr, this, nullptr);
         emit ircUserParted(ircuser);
 
         if (network()->isMe(ircuser) || _userModes.isEmpty()) {
             // in either case we're no longer in the channel
             //  -> clean up the channel and destroy it
-            QList<IrcUser *> users = _userModes.keys();
+            QList<IrcUser*> users = _userModes.keys();
             _userModes.clear();
-            foreach(IrcUser *user, users) {
-                disconnect(user, 0, this, 0);
-                user->partChannel(this);
+            foreach (IrcUser* user, users) {
+                disconnect(user, nullptr, this, nullptr);
+                user->partChannelInternal(this, true);
             }
             emit parted();
             network()->removeIrcChannel(this);
@@ -247,59 +239,56 @@ void IrcChannel::part(IrcUser *ircuser)
     }
 }
 
-
-void IrcChannel::part(const QString &nick)
+void IrcChannel::part(const QString& nick)
 {
     part(network()->ircUser(nick));
 }
 
-
 // SET USER MODE
-void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
+void IrcChannel::setUserModes(IrcUser* ircuser, const QString& modes)
 {
     if (isKnownUser(ircuser)) {
-        _userModes[ircuser] = modes;
+        // Keep user modes sorted
+        _userModes[ircuser] = network()->sortPrefixModes(modes);
         QString nick = ircuser->nick();
         SYNC_OTHER(setUserModes, ARG(nick), ARG(modes))
         emit ircUserModesSet(ircuser, modes);
     }
 }
 
-
-void IrcChannel::setUserModes(const QString &nick, const QString &modes)
+void IrcChannel::setUserModes(const QString& nick, const QString& modes)
 {
     setUserModes(network()->ircUser(nick), modes);
 }
 
-
 // ADD USER MODE
-void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode)
+void IrcChannel::addUserMode(IrcUser* ircuser, const QString& mode)
 {
     if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
         return;
 
     if (!_userModes[ircuser].contains(mode)) {
-        _userModes[ircuser] += mode;
+        // Keep user modes sorted
+        _userModes[ircuser] = network()->sortPrefixModes(_userModes[ircuser] + mode);
         QString nick = ircuser->nick();
         SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
         emit ircUserModeAdded(ircuser, mode);
     }
 }
 
-
-void IrcChannel::addUserMode(const QString &nick, const QString &mode)
+void IrcChannel::addUserMode(const QString& nick, const QString& mode)
 {
     addUserMode(network()->ircUser(nick), mode);
 }
 
-
 // REMOVE USER MODE
-void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
+void IrcChannel::removeUserMode(IrcUser* ircuser, const QString& mode)
 {
     if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
         return;
 
     if (_userModes[ircuser].contains(mode)) {
+        // Removing modes shouldn't mess up ordering
         _userModes[ircuser].remove(mode);
         QString nick = ircuser->nick();
         SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
@@ -307,40 +296,37 @@ void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
     }
 }
 
-
-void IrcChannel::removeUserMode(const QString &nick, const QString &mode)
+void IrcChannel::removeUserMode(const QString& nick, const QString& mode)
 {
     removeUserMode(network()->ircUser(nick), mode);
 }
 
-
 // INIT SET USER MODES
 QVariantMap IrcChannel::initUserModes() const
 {
     QVariantMap usermodes;
-    QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
+    QHash<IrcUser*, QString>::const_iterator iter = _userModes.constBegin();
     while (iter != _userModes.constEnd()) {
         usermodes[iter.key()->nick()] = iter.value();
-        iter++;
+        ++iter;
     }
     return usermodes;
 }
 
-
-void IrcChannel::initSetUserModes(const QVariantMap &usermodes)
+void IrcChannel::initSetUserModes(const QVariantMap& usermodes)
 {
-    QList<IrcUser *> users;
+    QList<IrcUser*> users;
     QStringList modes;
     QVariantMap::const_iterator iter = usermodes.constBegin();
     while (iter != usermodes.constEnd()) {
         users << network()->newIrcUser(iter.key());
         modes << iter.value().toString();
-        iter++;
+        ++iter;
     }
+    // joinIrcUsers handles sorting modes
     joinIrcUsers(users, modes);
 }
 
-
 QVariantMap IrcChannel::initChanModes() const
 {
     QVariantMap channelModes;
@@ -349,7 +335,7 @@ QVariantMap IrcChannel::initChanModes() const
     QHash<QChar, QStringList>::const_iterator A_iter = _A_channelModes.constBegin();
     while (A_iter != _A_channelModes.constEnd()) {
         A_modes[A_iter.key()] = A_iter.value();
-        A_iter++;
+        ++A_iter;
     }
     channelModes["A"] = A_modes;
 
@@ -357,7 +343,7 @@ QVariantMap IrcChannel::initChanModes() const
     QHash<QChar, QString>::const_iterator B_iter = _B_channelModes.constBegin();
     while (B_iter != _B_channelModes.constEnd()) {
         B_modes[B_iter.key()] = B_iter.value();
-        B_iter++;
+        ++B_iter;
     }
     channelModes["B"] = B_modes;
 
@@ -365,7 +351,7 @@ QVariantMap IrcChannel::initChanModes() const
     QHash<QChar, QString>::const_iterator C_iter = _C_channelModes.constBegin();
     while (C_iter != _C_channelModes.constEnd()) {
         C_modes[C_iter.key()] = C_iter.value();
-        C_iter++;
+        ++C_iter;
     }
     channelModes["C"] = C_modes;
 
@@ -373,35 +359,34 @@ QVariantMap IrcChannel::initChanModes() const
     QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
     while (D_iter != _D_channelModes.constEnd()) {
         D_modes += *D_iter;
-        D_iter++;
+        ++D_iter;
     }
     channelModes["D"] = D_modes;
 
     return channelModes;
 }
 
-
-void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
+void IrcChannel::initSetChanModes(const QVariantMap& channelModes)
 {
     QVariantMap::const_iterator iter = channelModes["A"].toMap().constBegin();
     QVariantMap::const_iterator iterEnd = channelModes["A"].toMap().constEnd();
     while (iter != iterEnd) {
         _A_channelModes[iter.key()[0]] = iter.value().toStringList();
-        iter++;
+        ++iter;
     }
 
     iter = channelModes["B"].toMap().constBegin();
     iterEnd = channelModes["B"].toMap().constEnd();
     while (iter != iterEnd) {
         _B_channelModes[iter.key()[0]] = iter.value().toString();
-        iter++;
+        ++iter;
     }
 
     iter = channelModes["C"].toMap().constBegin();
     iterEnd = channelModes["C"].toMap().constEnd();
     while (iter != iterEnd) {
         _C_channelModes[iter.key()[0]] = iter.value().toString();
-        iter++;
+        ++iter;
     }
 
     QString D_modes = channelModes["D"].toString();
@@ -410,25 +395,22 @@ void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
     }
 }
 
-
 void IrcChannel::ircUserDestroyed()
 {
-    IrcUser *ircUser = static_cast<IrcUser *>(sender());
+    auto* ircUser = static_cast<IrcUser*>(sender());
     Q_ASSERT(ircUser);
     _userModes.remove(ircUser);
     // no further propagation.
     // this leads only to fuck ups.
 }
 
-
 void IrcChannel::ircUserNickSet(QString nick)
 {
-    IrcUser *ircUser = qobject_cast<IrcUser *>(sender());
+    auto* ircUser = qobject_cast<IrcUser*>(sender());
     Q_ASSERT(ircUser);
     emit ircUserNickSet(ircUser, nick);
 }
 
-
 /*******************************************************************************
  *
  * 3.3 CHANMODES
@@ -468,13 +450,13 @@ void IrcChannel::ircUserNickSet(QString nick)
  * C --> set value or remove
  * D --> on/off
  *
- * B and C behave very similar... we store the data in different datastructes
+ * B and C behave very similar... we store the data in different datastructures
  * for future compatibility
  ******************************************************************************/
 
 // NOTE: the behavior of addChannelMode and removeChannelMode depends on the type of mode
 // see list above for chanmode types
-void IrcChannel::addChannelMode(const QChar &mode, const QString &value)
+void IrcChannel::addChannelMode(const QChar& mode, const QString& value)
 {
     Network::ChannelModeType modeType = network()->channelModeType(mode);
 
@@ -503,8 +485,7 @@ void IrcChannel::addChannelMode(const QChar &mode, const QString &value)
     SYNC(ARG(mode), ARG(value))
 }
 
-
-void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
+void IrcChannel::removeChannelMode(const QChar& mode, const QString& value)
 {
     Network::ChannelModeType modeType = network()->channelModeType(mode);
 
@@ -531,8 +512,7 @@ void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
     SYNC(ARG(mode), ARG(value))
 }
 
-
-bool IrcChannel::hasMode(const QChar &mode) const
+bool IrcChannel::hasMode(const QChar& mode) const
 {
     Network::ChannelModeType modeType = network()->channelModeType(mode);
 
@@ -547,13 +527,11 @@ bool IrcChannel::hasMode(const QChar &mode) const
         return _C_channelModes.contains(mode);
     case Network::D_CHANMODE:
         return _D_channelModes.contains(mode);
-    default:
-        return false;
     }
+    return false;
 }
 
-
-QString IrcChannel::modeValue(const QChar &mode) const
+QString IrcChannel::modeValue(const QChar& mode) const
 {
     Network::ChannelModeType modeType = network()->channelModeType(mode);
 
@@ -573,8 +551,7 @@ QString IrcChannel::modeValue(const QChar &mode) const
     }
 }
 
-
-QStringList IrcChannel::modeValueList(const QChar &mode) const
+QStringList IrcChannel::modeValueList(const QChar& mode) const
 {
     Network::ChannelModeType modeType = network()->channelModeType(mode);
 
@@ -582,12 +559,13 @@ QStringList IrcChannel::modeValueList(const QChar &mode) const
     case Network::A_CHANMODE:
         if (_A_channelModes.contains(mode))
             return _A_channelModes[mode];
+        break;
     default:
-        return QStringList();
+        ;
     }
+    return {};
 }
 
-
 QString IrcChannel::channelModeString() const
 {
     QStringList params;
@@ -596,21 +574,21 @@ QString IrcChannel::channelModeString() const
     QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
     while (D_iter != _D_channelModes.constEnd()) {
         modeString += *D_iter;
-        D_iter++;
+        ++D_iter;
     }
 
     QHash<QChar, QString>::const_iterator BC_iter = _C_channelModes.constBegin();
     while (BC_iter != _C_channelModes.constEnd()) {
         modeString += BC_iter.key();
         params << BC_iter.value();
-        BC_iter++;
+        ++BC_iter;
     }
 
     BC_iter = _B_channelModes.constBegin();
     while (BC_iter != _B_channelModes.constEnd()) {
         modeString += BC_iter.key();
         params << BC_iter.value();
-        BC_iter++;
+        ++BC_iter;
     }
     if (modeString.isEmpty())
         return modeString;