- qWarning() << "unable to synchronize new IrcUser" << hostmask << "forgot to call Network::setProxy(SignalProxy *)?";
-
- connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickChanged(QString)));
- connect(ircuser, SIGNAL(initDone()), this, SLOT(ircUserInitDone()));
- connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
- _ircUsers[nick] = ircuser;
- emit ircUserAdded(hostmask);
- emit ircUserAdded(ircuser);
- }
- return _ircUsers[nick];
-}
-
-void Network::ircUserDestroyed() {
- IrcUser *ircUser = static_cast<IrcUser *>(sender());
- if(!ircUser)
- return;
-
- QHash<QString, IrcUser *>::iterator ircUserIter = _ircUsers.begin();
- while(ircUserIter != _ircUsers.end()) {
- if(ircUser == *ircUserIter) {
- ircUserIter = _ircUsers.erase(ircUserIter);
- break;
- }
- ircUserIter++;
- }
-}
-
-void Network::removeIrcUser(IrcUser *ircuser) {
- QString nick = _ircUsers.key(ircuser);
- if(nick.isNull())
- return;
-
- _ircUsers.remove(nick);
- disconnect(ircuser, 0, this, 0);
- emit ircUserRemoved(nick);
- emit ircUserRemoved(ircuser);
- ircuser->deleteLater();
-}
-
-void Network::removeIrcUser(const QString &nick) {
- IrcUser *ircuser;
- if((ircuser = ircUser(nick)) != 0)
- removeIrcUser(ircuser);
-}
-
-void Network::removeChansAndUsers() {
- QList<IrcUser *> users = ircUsers();
- foreach(IrcUser *user, users) {
- removeIrcUser(user);
- }
- QList<IrcChannel *> channels = ircChannels();
- foreach(IrcChannel *channel, channels) {
- removeIrcChannel(channel);
- }
-}
-
-IrcUser *Network::ircUser(QString nickname) const {
- nickname = nickname.toLower();
- if(_ircUsers.contains(nickname))
- return _ircUsers[nickname];
- else
- return 0;
-}
-
-IrcChannel *Network::newIrcChannel(const QString &channelname) {
- if(!_ircChannels.contains(channelname.toLower())) {
- IrcChannel *channel = new IrcChannel(channelname, this);
-
- if(proxy())
- proxy()->synchronize(channel);
+ return QString("#&!+").contains(channelname[0]);
+}
+
+
+bool Network::isStatusMsg(const QString &target) const
+{
+ if (target.isEmpty())
+ return false;
+
+ if (supports("STATUSMSG"))
+ return support("STATUSMSG").contains(target[0]);
+ else
+ return QString("@+").contains(target[0]);
+}
+
+
+NetworkInfo Network::networkInfo() const
+{
+ NetworkInfo info;
+ info.networkName = networkName();
+ info.networkId = networkId();
+ info.identity = identity();
+ info.codecForServer = codecForServer();
+ info.codecForEncoding = codecForEncoding();
+ info.codecForDecoding = codecForDecoding();
+ info.serverList = serverList();
+ info.useRandomServer = useRandomServer();
+ info.perform = perform();
+ info.useAutoIdentify = useAutoIdentify();
+ info.autoIdentifyService = autoIdentifyService();
+ info.autoIdentifyPassword = autoIdentifyPassword();
+ info.useSasl = useSasl();
+ info.saslAccount = saslAccount();
+ info.saslPassword = saslPassword();
+ info.useAutoReconnect = useAutoReconnect();
+ info.autoReconnectInterval = autoReconnectInterval();
+ info.autoReconnectRetries = autoReconnectRetries();
+ info.unlimitedReconnectRetries = unlimitedReconnectRetries();
+ info.rejoinChannels = rejoinChannels();
+ info.useCustomMessageRate = useCustomMessageRate();
+ info.messageRateBurstSize = messageRateBurstSize();
+ info.messageRateDelay = messageRateDelay();
+ info.unlimitedMessageRate = unlimitedMessageRate();
+ return info;
+}
+
+
+void Network::setNetworkInfo(const NetworkInfo &info)
+{
+ // we don't set our ID!
+ if (!info.networkName.isEmpty() && info.networkName != networkName()) setNetworkName(info.networkName);
+ if (info.identity > 0 && info.identity != identity()) setIdentity(info.identity);
+ if (info.codecForServer != codecForServer()) setCodecForServer(QTextCodec::codecForName(info.codecForServer));
+ if (info.codecForEncoding != codecForEncoding()) setCodecForEncoding(QTextCodec::codecForName(info.codecForEncoding));
+ if (info.codecForDecoding != codecForDecoding()) setCodecForDecoding(QTextCodec::codecForName(info.codecForDecoding));
+ if (info.serverList.count()) setServerList(toVariantList(info.serverList)); // FIXME compare components
+ if (info.useRandomServer != useRandomServer()) setUseRandomServer(info.useRandomServer);
+ if (info.perform != perform()) setPerform(info.perform);
+ if (info.useAutoIdentify != useAutoIdentify()) setUseAutoIdentify(info.useAutoIdentify);
+ if (info.autoIdentifyService != autoIdentifyService()) setAutoIdentifyService(info.autoIdentifyService);
+ if (info.autoIdentifyPassword != autoIdentifyPassword()) setAutoIdentifyPassword(info.autoIdentifyPassword);
+ if (info.useSasl != useSasl()) setUseSasl(info.useSasl);
+ if (info.saslAccount != saslAccount()) setSaslAccount(info.saslAccount);
+ if (info.saslPassword != saslPassword()) setSaslPassword(info.saslPassword);
+ if (info.useAutoReconnect != useAutoReconnect()) setUseAutoReconnect(info.useAutoReconnect);
+ if (info.autoReconnectInterval != autoReconnectInterval()) setAutoReconnectInterval(info.autoReconnectInterval);
+ if (info.autoReconnectRetries != autoReconnectRetries()) setAutoReconnectRetries(info.autoReconnectRetries);
+ if (info.unlimitedReconnectRetries != unlimitedReconnectRetries()) setUnlimitedReconnectRetries(info.unlimitedReconnectRetries);
+ if (info.rejoinChannels != rejoinChannels()) setRejoinChannels(info.rejoinChannels);
+ // Custom rate limiting
+ if (info.useCustomMessageRate != useCustomMessageRate())
+ setUseCustomMessageRate(info.useCustomMessageRate);
+ if (info.messageRateBurstSize != messageRateBurstSize())
+ setMessageRateBurstSize(info.messageRateBurstSize);
+ if (info.messageRateDelay != messageRateDelay())
+ setMessageRateDelay(info.messageRateDelay);
+ if (info.unlimitedMessageRate != unlimitedMessageRate())
+ setUnlimitedMessageRate(info.unlimitedMessageRate);
+}
+
+
+QString Network::prefixToMode(const QString &prefix) const
+{
+ if (prefixes().contains(prefix))
+ return QString(prefixModes()[prefixes().indexOf(prefix)]);
+ else
+ return QString();
+}
+
+
+QString Network::modeToPrefix(const QString &mode) const
+{
+ if (prefixModes().contains(mode))
+ return QString(prefixes()[prefixModes().indexOf(mode)]);
+ else
+ return QString();
+}
+
+
+QString Network::sortPrefixModes(const QString &modes) const
+{
+ // If modes is empty or we don't have any modes, nothing can be sorted, bail out early
+ if (modes.isEmpty() || prefixModes().isEmpty()) {
+ return modes;
+ }
+
+ // Store a copy of the modes for modification
+ // QString should be efficient and not copy memory if nothing changes, but if mistaken,
+ // std::is_sorted could be called first.
+ QString sortedModes = QString(modes);
+
+ // Sort modes as if a QChar array
+ // See https://en.cppreference.com/w/cpp/algorithm/sort
+ // Defining lambda with [&] implicitly captures variables by reference
+ std::sort(sortedModes.begin(), sortedModes.end(), [&](const QChar &lmode, const QChar &rmode) {
+ // Compare characters according to prefix modes
+ // Return true if lmode comes before rmode (is "less than")
+
+ // Check for unknown modes...
+ if (!prefixModes().contains(lmode)) {
+ // Left mode not in prefix list, send to end
+ return false;
+ } else if (!prefixModes().contains(rmode)) {
+ // Right mode not in prefix list, send to end
+ return true;
+ } else {
+ // Both characters known, sort according to index in prefixModes()
+ return (prefixModes().indexOf(lmode) < prefixModes().indexOf(rmode));
+ }
+ });
+
+ return sortedModes;
+}
+
+
+QStringList Network::nicks() const
+{
+ // we don't use _ircUsers.keys() since the keys may be
+ // not up to date after a nick change
+ QStringList nicks;
+ foreach(IrcUser *ircuser, _ircUsers.values()) {
+ nicks << ircuser->nick();
+ }
+ return nicks;
+}
+
+
+QString Network::prefixes() const
+{
+ if (_prefixes.isNull())
+ determinePrefixes();
+
+ return _prefixes;
+}
+
+
+QString Network::prefixModes() const
+{
+ if (_prefixModes.isNull())
+ determinePrefixes();
+
+ return _prefixModes;
+}
+
+
+// example Unreal IRCD: CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMTG
+Network::ChannelModeType Network::channelModeType(const QString &mode)
+{
+ if (mode.isEmpty())
+ return NOT_A_CHANMODE;
+
+ QString chanmodes = support("CHANMODES");
+ if (chanmodes.isEmpty())
+ return NOT_A_CHANMODE;
+
+ ChannelModeType modeType = A_CHANMODE;
+ for (int i = 0; i < chanmodes.count(); i++) {
+ if (chanmodes[i] == mode[0])
+ break;
+ else if (chanmodes[i] == ',')
+ modeType = (ChannelModeType)(modeType << 1);
+ }
+ if (modeType > D_CHANMODE) {
+ qWarning() << "Network" << networkId() << "supplied invalid CHANMODES:" << chanmodes;
+ modeType = NOT_A_CHANMODE;
+ }
+ return modeType;
+}
+
+
+QString Network::support(const QString ¶m) const
+{
+ QString support_ = param.toUpper();
+ if (_supports.contains(support_))
+ return _supports[support_];
+ else
+ return QString();
+}
+
+
+bool Network::saslMaybeSupports(const QString &saslMechanism) const
+{
+ if (!capAvailable(IrcCap::SASL)) {
+ // If SASL's not advertised at all, it's likely the mechanism isn't supported, as per specs.
+ // Unfortunately, we don't know for sure, but Quassel won't request SASL without it being
+ // advertised, anyways.
+ // This may also occur if the network's disconnected or negotiation hasn't yet happened.
+ return false;
+ }
+
+ // Get the SASL capability value
+ QString saslCapValue = capValue(IrcCap::SASL);
+ // SASL mechanisms are only specified in capability values as part of SASL 3.2. In SASL 3.1,
+ // it's handled differently. If we don't know via capability value, assume it's supported to
+ // reduce the risk of breaking existing setups.
+ // See: http://ircv3.net/specs/extensions/sasl-3.1.html
+ // And: http://ircv3.net/specs/extensions/sasl-3.2.html
+ return (saslCapValue.length() == 0)
+ || (saslCapValue.contains(saslMechanism, Qt::CaseInsensitive));
+}
+
+
+IrcUser *Network::newIrcUser(const QString &hostmask, const QVariantMap &initData)
+{
+ QString nick(nickFromMask(hostmask).toLower());
+ if (!_ircUsers.contains(nick)) {
+ IrcUser *ircuser = ircUserFactory(hostmask);
+ if (!initData.isEmpty()) {
+ ircuser->fromVariantMap(initData);
+ ircuser->setInitialized();
+ }
+
+ if (proxy())
+ proxy()->synchronize(ircuser);
+ else
+ qWarning() << "unable to synchronize new IrcUser" << hostmask << "forgot to call Network::setProxy(SignalProxy *)?";
+
+ connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickChanged(QString)));
+
+ _ircUsers[nick] = ircuser;
+
+ // This method will be called with a nick instead of hostmask by setInitIrcUsersAndChannels().
+ // Not a problem because initData contains all we need; however, making sure here to get the real
+ // hostmask out of the IrcUser afterwards.
+ QString mask = ircuser->hostmask();
+ SYNC_OTHER(addIrcUser, ARG(mask));
+ // emit ircUserAdded(mask);
+ emit ircUserAdded(ircuser);
+ }
+
+ return _ircUsers[nick];
+}
+
+
+IrcUser *Network::ircUser(QString nickname) const
+{
+ nickname = nickname.toLower();
+ if (_ircUsers.contains(nickname))
+ return _ircUsers[nickname];