/***************************************************************************
- * Copyright (C) 2005-2019 by the Quassel Project *
+ * Copyright (C) 2005-2022 by the Quassel Project *
* devel@quassel-irc.org *
* *
* This program is free software; you can redistribute it and/or modify *
info.serverList = serverList();
info.useRandomServer = useRandomServer();
info.perform = perform();
+ info.skipCaps = skipCaps();
info.useAutoIdentify = useAutoIdentify();
info.autoIdentifyService = autoIdentifyService();
info.autoIdentifyPassword = autoIdentifyPassword();
setUseRandomServer(info.useRandomServer);
if (info.perform != perform())
setPerform(info.perform);
+ if (info.skipCaps != skipCaps())
+ setSkipCaps(info.skipCaps);
if (info.useAutoIdentify != useAutoIdentify())
setUseAutoIdentify(info.useAutoIdentify);
if (info.autoIdentifyService != autoIdentifyService())
emit configChanged();
}
+void Network::setSkipCaps(const QStringList& skipCaps)
+{
+ _skipCaps = skipCaps;
+ // Ensure the list of skipped capabilities remains sorted
+ //
+ // This becomes important in CoreNetwork::beginCapNegotiation() when finding the intersection of
+ // available capabilities and skipped capabilities. It's a bit more efficient to sort on first
+ // initialization and changes afterwards instead of on every (re)connection to the IRC network.
+ _skipCaps.sort();
+ SYNC(ARG(skipCaps))
+ emit configChanged();
+}
+
void Network::setUseAutoIdentify(bool use)
{
_useAutoIdentify = use;
// Remove from the list of available capabilities.
_caps.remove(_capLowercase);
// Remove it from the acknowledged list if it was previously acknowledged. The SYNC call
- // ensures this propogates to the other side.
+ // ensures this propagates to the other side.
// Use removeOne() for speed; no more than one due to contains() check in acknowledgeCap().
_capsEnabled.removeOne(_capLowercase);
SYNC(ARG(capability))
* NetworkInfo
************************************************************************/
+QString NetworkInfo::skipCapsToString() const {
+ // Sort the list of capabilities when rendering to a string. This isn't required as
+ // Network::setSkipCaps() will sort as well, but this looks nicer when displayed to the user.
+ // This also results in the list being sorted before storing in the database, too.
+ auto sortedSkipCaps = skipCaps;
+ sortedSkipCaps.sort();
+
+ // IRCv3 capabilities are transmitted space-separated, so it should be safe to assume spaces
+ // won't ever be inside them
+ //
+ // See https://ircv3.net/specs/core/capability-negotiation
+ return sortedSkipCaps.join(" ");
+}
+
+void NetworkInfo::skipCapsFromString(const QString& flattenedSkipCaps) {
+ // IRCv3 capabilities should all use lowercase capability names, though it's not strictly
+ // required by the specification. Quassel currently converts all caps to lowercase before doing
+ // any comparisons.
+ //
+ // This would only become an issue if two capabilities have the same name and only differ by
+ // case, or if an IRC server transmits an uppercase capability and compares case-sensitively.
+ //
+ // (QString::toLower() is always done in the C locale, so locale-dependent case-sensitivity
+ // won't ever be an issue, thankfully.)
+ //
+ // See Network::addCap(), Network::acknowledgeCap(), and friends
+ // And https://ircv3.net/specs/core/capability-negotiation
+ skipCaps = flattenedSkipCaps.toLower().split(" ", QString::SplitBehavior::SkipEmptyParts);
+}
+
bool NetworkInfo::operator==(const NetworkInfo& other) const
{
return networkName == other.networkName
&& serverList == other.serverList
&& perform == other.perform
+ && skipCaps == other.skipCaps
&& autoIdentifyService == other.autoIdentifyService
&& autoIdentifyPassword == other.autoIdentifyPassword
&& saslAccount == other.saslAccount
i["NetworkName"] = info.networkName;
i["ServerList"] = toVariantList(info.serverList);
i["Perform"] = info.perform;
+ i["SkipCaps"] = info.skipCaps;
i["AutoIdentifyService"] = info.autoIdentifyService;
i["AutoIdentifyPassword"] = info.autoIdentifyPassword;
i["SaslAccount"] = info.saslAccount;
info.networkName = i["NetworkName"].toString();
info.serverList = fromVariantList<Network::Server>(i["ServerList"].toList());
info.perform = i["Perform"].toStringList();
+ info.skipCaps = i["SkipCaps"].toStringList();
info.autoIdentifyService = i["AutoIdentifyService"].toString();
info.autoIdentifyPassword = i["AutoIdentifyPassword"].toString();
info.saslAccount = i["SaslAccount"].toString();
dbg.nospace() << "(id = " << i.networkId << " name = " << i.networkName << " identity = " << i.identity
<< " codecForServer = " << i.codecForServer << " codecForEncoding = " << i.codecForEncoding
<< " codecForDecoding = " << i.codecForDecoding << " serverList = " << i.serverList
- << " useRandomServer = " << i.useRandomServer << " perform = " << i.perform << " useAutoIdentify = " << i.useAutoIdentify
+ << " useRandomServer = " << i.useRandomServer << " perform = " << i.perform
+ << " skipCaps = " << i.skipCaps << " useAutoIdentify = " << i.useAutoIdentify
<< " autoIdentifyService = " << i.autoIdentifyService << " autoIdentifyPassword = " << i.autoIdentifyPassword
<< " useSasl = " << i.useSasl << " saslAccount = " << i.saslAccount << " saslPassword = " << i.saslPassword
<< " useAutoReconnect = " << i.useAutoReconnect << " autoReconnectInterval = " << i.autoReconnectInterval