X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fsettingspages%2Fnetworkssettingspage.cpp;h=dfd1f62f756bad482fc7f088d688ff82e5e114e0;hp=618636a4f069f6f03012918f8deb6e0d596a59c3;hb=HEAD;hpb=b571f515e6e2ec9fcc69e600f4f6d5aecfe86f29 diff --git a/src/qtui/settingspages/networkssettingspage.cpp b/src/qtui/settingspages/networkssettingspage.cpp index 618636a4..dfd1f62f 100644 --- a/src/qtui/settingspages/networkssettingspage.cpp +++ b/src/qtui/settingspages/networkssettingspage.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2014 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 * @@ -18,57 +18,61 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "networkssettingspage.h" + +#include + #include #include #include -#include "networkssettingspage.h" - #include "client.h" -#include "iconloader.h" +#include "icon.h" #include "identity.h" #include "network.h" #include "presetnetworks.h" #include "settingspagedlg.h" #include "util.h" +#include "widgethelpers.h" + +// IRCv3 capabilities +#include "irccap.h" #include "settingspages/identitiessettingspage.h" -NetworksSettingsPage::NetworksSettingsPage(QWidget *parent) +NetworksSettingsPage::NetworksSettingsPage(QWidget* parent) : SettingsPage(tr("IRC"), tr("Networks"), parent) -#ifdef HAVE_SSL - , _cid(0) -#endif { ui.setupUi(this); // hide SASL options for older cores - if (!(Client::coreFeatures() & Quassel::SaslAuthentication)) + if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslAuthentication)) ui.sasl->hide(); - if (!(Client::coreFeatures() & Quassel::SaslExternal)) + if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal)) ui.saslExtInfo->hide(); -#ifndef HAVE_SSL - ui.saslExtInfo->hide(); -#endif // set up icons - ui.renameNetwork->setIcon(SmallIcon("edit-rename")); - ui.addNetwork->setIcon(SmallIcon("list-add")); - ui.deleteNetwork->setIcon(SmallIcon("edit-delete")); - ui.addServer->setIcon(SmallIcon("list-add")); - ui.deleteServer->setIcon(SmallIcon("edit-delete")); - ui.editServer->setIcon(SmallIcon("configure")); - ui.upServer->setIcon(SmallIcon("go-up")); - ui.downServer->setIcon(SmallIcon("go-down")); - ui.editIdentities->setIcon(SmallIcon("configure")); - - _ignoreWidgetChanges = false; - - connectedIcon = SmallIcon("network-connect"); - connectingIcon = SmallIcon("network-wired"); // FIXME network-connecting - disconnectedIcon = SmallIcon("network-disconnect"); - - foreach(int mib, QTextCodec::availableMibs()) { + ui.renameNetwork->setIcon(icon::get("edit-rename")); + ui.addNetwork->setIcon(icon::get("list-add")); + ui.deleteNetwork->setIcon(icon::get("edit-delete")); + ui.addServer->setIcon(icon::get("list-add")); + ui.deleteServer->setIcon(icon::get("edit-delete")); + ui.editServer->setIcon(icon::get("configure")); + ui.upServer->setIcon(icon::get("go-up")); + ui.downServer->setIcon(icon::get("go-down")); + ui.editIdentities->setIcon(icon::get("configure")); + + connectedIcon = icon::get("network-connect"); + connectingIcon = icon::get("network-wired"); // FIXME network-connecting + disconnectedIcon = icon::get("network-disconnect"); + + // Status icons + infoIcon = icon::get({"emblem-information", "dialog-information"}); + successIcon = icon::get({"emblem-success", "dialog-information"}); + unavailableIcon = icon::get({"emblem-unavailable", "dialog-warning"}); + questionIcon = icon::get({"emblem-question", "dialog-question", "dialog-information"}); + + foreach (int mib, QTextCodec::availableMibs()) { QByteArray codec = QTextCodec::codecForMib(mib)->name(); ui.sendEncoding->addItem(codec); ui.recvEncoding->addItem(codec); @@ -78,45 +82,36 @@ NetworksSettingsPage::NetworksSettingsPage(QWidget *parent) ui.recvEncoding->model()->sort(0); ui.serverEncoding->model()->sort(0); currentId = 0; - setEnabled(Client::isConnected()); // need a core connection! + setEnabled(Client::isConnected()); // need a core connection! setWidgetStates(); - connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool))); - connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), this, SLOT(clientNetworkAdded(NetworkId))); - connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), this, SLOT(clientNetworkRemoved(NetworkId))); - connect(Client::instance(), SIGNAL(identityCreated(IdentityId)), this, SLOT(clientIdentityAdded(IdentityId))); - connect(Client::instance(), SIGNAL(identityRemoved(IdentityId)), this, SLOT(clientIdentityRemoved(IdentityId))); - - connect(ui.identityList, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged())); - //connect(ui.randomServer, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.performEdit, SIGNAL(textChanged()), this, SLOT(widgetHasChanged())); - connect(ui.autoIdentify, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.autoIdentifyService, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged())); - connect(ui.autoIdentifyPassword, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged())); - connect(ui.sasl, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.saslAccount, SIGNAL(textEdited(QString)), this, SLOT(widgetHasChanged())); - connect(ui.saslPassword, SIGNAL(textEdited(QString)), this, SLOT(widgetHasChanged())); - connect(ui.useCustomEncodings, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.sendEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged())); - connect(ui.recvEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged())); - connect(ui.serverEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged())); - connect(ui.autoReconnect, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.reconnectInterval, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged())); - connect(ui.reconnectRetries, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged())); - connect(ui.unlimitedRetries, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.rejoinOnReconnect, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - //connect(ui., SIGNAL(), this, SLOT(widgetHasChanged())); - //connect(ui., SIGNAL(), this, SLOT(widgetHasChanged())); - - foreach(IdentityId id, Client::identityIds()) { + + connectToWidgetsChangedSignals({ui.identityList, ui.performEdit, ui.sasl, + ui.saslAccount, ui.saslPassword, ui.autoIdentify, + ui.autoIdentifyService, ui.autoIdentifyPassword, ui.useCustomEncodings, + ui.sendEncoding, ui.recvEncoding, ui.serverEncoding, + ui.autoReconnect, ui.reconnectInterval, ui.reconnectRetries, + ui.unlimitedRetries, ui.rejoinOnReconnect, ui.useCustomMessageRate, + ui.messageRateBurstSize, ui.messageRateDelay, ui.unlimitedMessageRate, + ui.enableCapServerTime}, + this, + &NetworksSettingsPage::widgetHasChanged); + + connect(Client::instance(), &Client::coreConnectionStateChanged, this, &NetworksSettingsPage::coreConnectionStateChanged); + connect(Client::instance(), &Client::networkCreated, this, &NetworksSettingsPage::clientNetworkAdded); + connect(Client::instance(), &Client::networkRemoved, this, &NetworksSettingsPage::clientNetworkRemoved); + connect(Client::instance(), &Client::identityCreated, this, &NetworksSettingsPage::clientIdentityAdded); + connect(Client::instance(), &Client::identityRemoved, this, &NetworksSettingsPage::clientIdentityRemoved); + + foreach (IdentityId id, Client::identityIds()) { clientIdentityAdded(id); } } - void NetworksSettingsPage::save() { setEnabled(false); - if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]); + if (currentId != 0) + saveToNetworkInfo(networkInfos[currentId]); QList toCreate, toUpdate; QList toRemove; @@ -125,13 +120,13 @@ void NetworksSettingsPage::save() NetworkId id = (*i).networkId; if (id < 0) { toCreate.append(*i); - //if(id == currentId) currentId = 0; - //QList items = ui.networkList->findItems((*i).networkName, Qt::MatchExactly); - //if(items.count()) { + // if(id == currentId) currentId = 0; + // QList items = ui.networkList->findItems((*i).networkName, Qt::MatchExactly); + // if(items.count()) { // Q_ASSERT(items[0]->data(Qt::UserRole).value() == id); // delete items[0]; //} - //i = networkInfos.erase(i); + // i = networkInfos.erase(i); ++i; } else { @@ -141,8 +136,9 @@ void NetworksSettingsPage::save() ++i; } } - foreach(NetworkId id, Client::networkIds()) { - if (!networkInfos.contains(id)) toRemove.append(id); + foreach (NetworkId id, Client::networkIds()) { + if (!networkInfos.contains(id)) + toRemove.append(id); } SaveNetworksDlg dlg(toCreate, toUpdate, toRemove, this); int ret = dlg.exec(); @@ -154,18 +150,65 @@ void NetworksSettingsPage::save() setEnabled(true); } - void NetworksSettingsPage::load() { reset(); - foreach(NetworkId netid, Client::networkIds()) { + + // Handle UI dependent on core feature flags here + if (Client::isCoreFeatureEnabled(Quassel::Feature::CustomRateLimits)) { + // Custom rate limiting supported, allow toggling + ui.useCustomMessageRate->setEnabled(true); + // Reset tooltip to default. + ui.useCustomMessageRate->setToolTip(QString("%1").arg(tr("

Override default message rate limiting.

" + "

Setting limits too low may get you disconnected" + " from the server!

"))); + // If changed, update the message below! + } + else { + // Custom rate limiting not supported, disallow toggling + ui.useCustomMessageRate->setEnabled(false); + // Split up the message to allow re-using translations: + // [Original tool-tip] + // [Bold 'does not support feature' message] + // [Specific version needed and feature details] + ui.useCustomMessageRate->setToolTip(QString("%1
%2
%3") + .arg(tr("

Override default message rate limiting.

" + "

Setting limits too low may get you disconnected" + " from the server!

"), + tr("Your Quassel core does not support this feature"), + tr("You need a Quassel core v0.13.0 or newer in order to " + "modify message rate limits."))); + } + + if (!Client::isConnected() || Client::isCoreFeatureEnabled(Quassel::Feature::SkipIrcCaps)) { + // Either disconnected or IRCv3 capability skippping supported, enable configuration and + // hide warning. Don't show the warning needlessly when disconnected. + ui.enableCapsConfigWidget->setEnabled(true); + ui.enableCapsStatusLabel->setText(tr("These features require support from the network")); + ui.enableCapsStatusIcon->setPixmap(infoIcon.pixmap(16)); + } + else { + // Core does not IRCv3 capability skipping, show warning and disable configuration + ui.enableCapsConfigWidget->setEnabled(false); + ui.enableCapsStatusLabel->setText(tr("Your Quassel core is too old to configure IRCv3 features")); + ui.enableCapsStatusIcon->setPixmap(unavailableIcon.pixmap(16)); + } + + // Hide the SASL EXTERNAL notice until a network's shown. Stops it from showing while loading + // backlog from the core. + sslUpdated(); + + // Reset network capability status in case no valid networks get selected (a rare situation) + resetNetworkCapStates(); + + foreach (NetworkId netid, Client::networkIds()) { clientNetworkAdded(netid); } ui.networkList->setCurrentRow(0); + setChangedState(false); } - void NetworksSettingsPage::reset() { currentId = 0; @@ -173,45 +216,50 @@ void NetworksSettingsPage::reset() networkInfos.clear(); } - bool NetworksSettingsPage::aboutToSave() { - if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]); + if (currentId != 0) + saveToNetworkInfo(networkInfos[currentId]); QList errors; - foreach(NetworkInfo info, networkInfos.values()) { - if (!info.serverList.count()) errors.append(1); + foreach (NetworkInfo info, networkInfos.values()) { + if (!info.serverList.count()) + errors.append(1); } - if (!errors.count()) return true; + if (!errors.count()) + return true; QString error(tr("The following problems need to be corrected before your changes can be applied:
    ")); - if (errors.contains(1)) error += tr("
  • All networks need at least one server defined
  • "); + if (errors.contains(1)) + error += tr("
  • All networks need at least one server defined
  • "); error += tr("
"); QMessageBox::warning(this, tr("Invalid Network Settings"), error); return false; } - void NetworksSettingsPage::widgetHasChanged() { - if (_ignoreWidgetChanges) return; + if (_ignoreWidgetChanges) + return; bool changed = testHasChanged(); - if (changed != hasChanged()) setChangedState(changed); + if (changed != hasChanged()) + setChangedState(changed); } - bool NetworksSettingsPage::testHasChanged() { if (currentId != 0) { saveToNetworkInfo(networkInfos[currentId]); } - if (Client::networkIds().count() != networkInfos.count()) return true; - foreach(NetworkId id, networkInfos.keys()) { - if (id < 0) return true; - if (Client::network(id)->networkInfo() != networkInfos[id]) return true; + if (Client::networkIds().count() != networkInfos.count()) + return true; + foreach (NetworkId id, networkInfos.keys()) { + if (id < 0) + return true; + if (Client::network(id)->networkInfo() != networkInfos[id]) + return true; } return false; } - void NetworksSettingsPage::setWidgetStates() { // network list @@ -242,7 +290,7 @@ void NetworksSettingsPage::setWidgetStates() else { ui.renameNetwork->setEnabled(false); ui.deleteNetwork->setEnabled(false); - //ui.connectNow->setEnabled(false); + // ui.connectNow->setEnabled(false); ui.detailsBox->setEnabled(false); } // network details @@ -260,13 +308,15 @@ void NetworksSettingsPage::setWidgetStates() } } - -void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem *item) +void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem* item) { - if (!item && !(item = networkItem(id))) return; - const Network *net = Client::network(id); - if (!net || net->isInitialized()) item->setFlags(item->flags() | Qt::ItemIsEnabled); - else item->setFlags(item->flags() & ~Qt::ItemIsEnabled); + if (!item && !(item = networkItem(id))) + return; + const Network* net = Client::network(id); + if (!net || net->isInitialized()) + item->setFlags(item->flags() | Qt::ItemIsEnabled); + else + item->setFlags(item->flags() & ~Qt::ItemIsEnabled); if (net && net->connectionState() == Network::Initialized) { item->setIcon(connectedIcon); } @@ -279,11 +329,12 @@ void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem *item) if (net) { bool select = false; // check if we already have another net of this name in the list, and replace it - QList items = ui.networkList->findItems(net->networkName(), Qt::MatchExactly); + QList items = ui.networkList->findItems(net->networkName(), Qt::MatchExactly); if (items.count()) { - foreach(QListWidgetItem *i, items) { + foreach (QListWidgetItem* i, items) { NetworkId oldid = i->data(Qt::UserRole).value(); - if (oldid > 0) continue; // only locally created nets should be replaced + if (oldid > 0) + continue; // only locally created nets should be replaced if (oldid == currentId) { select = true; currentId = 0; @@ -291,7 +342,7 @@ void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem *item) } int row = ui.networkList->row(i); if (row >= 0) { - QListWidgetItem *olditem = ui.networkList->takeItem(row); + QListWidgetItem* olditem = ui.networkList->takeItem(row); Q_ASSERT(olditem); delete olditem; } @@ -300,10 +351,57 @@ void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem *item) } } item->setText(net->networkName()); - if (select) item->setSelected(true); + if (select) + item->setSelected(true); } } +void NetworksSettingsPage::resetNetworkCapStates() +{ + // Set the status to a blank (invalid) network ID, resetting all UI + setNetworkCapStates(NetworkId()); +} + +void NetworksSettingsPage::setNetworkCapStates(NetworkId id) +{ + const Network* net = Client::network(id); + if (net && Client::isCoreFeatureEnabled(Quassel::Feature::CapNegotiation)) { + // Capability negotiation is supported, network exists. + // Check if the network is connected. Don't use net->isConnected() as that won't be true + // during capability negotiation when capabilities are added and removed. + if (net->connectionState() != Network::Disconnected) { + // Network exists and is connected, check available capabilities... + // [SASL] + // Quassel switches between SASL PLAIN and SASL EXTERNAL based on the existence of an + // SSL certificate on the identity - check EXTERNAL if CertID exists, PLAIN if not + bool usingSASLExternal = displayedNetworkHasCertId(); + if ((usingSASLExternal && net->saslMaybeSupports(IrcCap::SaslMech::EXTERNAL)) + || (!usingSASLExternal && net->saslMaybeSupports(IrcCap::SaslMech::PLAIN))) { + setCapSASLStatus(CapSupportStatus::MaybeSupported, usingSASLExternal); + } + else { + setCapSASLStatus(CapSupportStatus::MaybeUnsupported, usingSASLExternal); + } + + // Add additional capability-dependent interface updates here + } + else { + // Network is disconnected + // [SASL] + setCapSASLStatus(CapSupportStatus::Disconnected); + + // Add additional capability-dependent interface updates here + } + } + else { + // Capability negotiation is not supported and/or network doesn't exist. + // Don't assume anything and reset all capability-dependent interface elements to neutral. + // [SASL] + setCapSASLStatus(CapSupportStatus::Unknown); + + // Add additional capability-dependent interface updates here + } +} void NetworksSettingsPage::coreConnectionStateChanged(bool state) { @@ -313,15 +411,14 @@ void NetworksSettingsPage::coreConnectionStateChanged(bool state) } else { // reset - //currentId = 0; + // currentId = 0; } } - void NetworksSettingsPage::clientIdentityAdded(IdentityId id) { - const Identity *identity = Client::identity(id); - connect(identity, SIGNAL(updatedRemotely()), this, SLOT(clientIdentityUpdated())); + const Identity* identity = Client::identity(id); + connect(identity, &SyncableObject::updatedRemotely, this, &NetworksSettingsPage::clientIdentityUpdated); QString name = identity->identityName(); for (int j = 0; j < ui.identityList->count(); j++) { @@ -336,10 +433,9 @@ void NetworksSettingsPage::clientIdentityAdded(IdentityId id) widgetHasChanged(); } - void NetworksSettingsPage::clientIdentityUpdated() { - const Identity *identity = qobject_cast(sender()); + const auto* identity = qobject_cast(sender()); if (!identity) { qWarning() << "NetworksSettingsPage: Invalid identity to update!"; return; @@ -354,68 +450,73 @@ void NetworksSettingsPage::clientIdentityUpdated() } } - void NetworksSettingsPage::clientIdentityRemoved(IdentityId id) { IdentityId defaultId = defaultIdentity(); - if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]); - foreach(NetworkInfo info, networkInfos.values()) { + if (currentId != 0) + saveToNetworkInfo(networkInfos[currentId]); + foreach (NetworkInfo info, networkInfos.values()) { if (info.identity == id) { if (info.networkId == currentId) ui.identityList->setCurrentIndex(0); info.identity = defaultId; networkInfos[info.networkId] = info; - if (info.networkId > 0) Client::updateNetwork(info); + if (info.networkId > 0) + Client::updateNetwork(info); } } ui.identityList->removeItem(ui.identityList->findData(id.toInt())); widgetHasChanged(); } - -QListWidgetItem *NetworksSettingsPage::networkItem(NetworkId id) const +QListWidgetItem* NetworksSettingsPage::networkItem(NetworkId id) const { for (int i = 0; i < ui.networkList->count(); i++) { - QListWidgetItem *item = ui.networkList->item(i); - if (item->data(Qt::UserRole).value() == id) return item; + QListWidgetItem* item = ui.networkList->item(i); + if (item->data(Qt::UserRole).value() == id) + return item; } - return 0; + return nullptr; } - void NetworksSettingsPage::clientNetworkAdded(NetworkId id) { insertNetwork(id); - //connect(Client::network(id), SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated())); - connect(Client::network(id), SIGNAL(configChanged()), this, SLOT(clientNetworkUpdated())); + // connect(Client::network(id), &Network::updatedRemotely, this, &NetworksSettingsPage::clientNetworkUpdated); + connect(Client::network(id), &Network::configChanged, this, &NetworksSettingsPage::clientNetworkUpdated); - connect(Client::network(id), SIGNAL(connectionStateSet(Network::ConnectionState)), this, SLOT(networkConnectionStateChanged(Network::ConnectionState))); - connect(Client::network(id), SIGNAL(connectionError(const QString &)), this, SLOT(networkConnectionError(const QString &))); -} + connect(Client::network(id), &Network::connectionStateSet, this, &NetworksSettingsPage::networkConnectionStateChanged); + connect(Client::network(id), &Network::connectionError, this, &NetworksSettingsPage::networkConnectionError); + // Handle capability changes in case a server dis/connects with the settings window open. + connect(Client::network(id), &Network::capAdded, this, &NetworksSettingsPage::clientNetworkCapsUpdated); + connect(Client::network(id), &Network::capRemoved, this, &NetworksSettingsPage::clientNetworkCapsUpdated); +} void NetworksSettingsPage::clientNetworkUpdated() { - const Network *net = qobject_cast(sender()); + const auto* net = qobject_cast(sender()); if (!net) { qWarning() << "Update request for unknown network received!"; return; } networkInfos[net->networkId()] = net->networkInfo(); setItemState(net->networkId()); - if (net->networkId() == currentId) displayNetwork(net->networkId()); + if (net->networkId() == currentId) + displayNetwork(net->networkId()); setWidgetStates(); widgetHasChanged(); } - void NetworksSettingsPage::clientNetworkRemoved(NetworkId id) { - if (!networkInfos.contains(id)) return; - if (id == currentId) displayNetwork(0); + if (!networkInfos.contains(id)) + return; + if (id == currentId) + displayNetwork(0); NetworkInfo info = networkInfos.take(id); - QList items = ui.networkList->findItems(info.networkName, Qt::MatchExactly); - foreach(QListWidgetItem *item, items) { + QList items = ui.networkList->findItems(info.networkName, Qt::MatchExactly); + foreach (QListWidgetItem* item, items) { if (item->data(Qt::UserRole).value() == id) delete ui.networkList->takeItem(ui.networkList->row(item)); } @@ -423,55 +524,66 @@ void NetworksSettingsPage::clientNetworkRemoved(NetworkId id) widgetHasChanged(); } - void NetworksSettingsPage::networkConnectionStateChanged(Network::ConnectionState state) { Q_UNUSED(state); - const Network *net = qobject_cast(sender()); - if (!net) return; + const auto* net = qobject_cast(sender()); + if (!net) + return; /* if(net->networkId() == currentId) { ui.connectNow->setEnabled(state == Network::Initialized || state == Network::Disconnected); } */ setItemState(net->networkId()); + if (net->networkId() == currentId) { + // Network is currently shown. Update the capability-dependent UI in case capabilities have + // changed. + setNetworkCapStates(currentId); + } setWidgetStates(); } +void NetworksSettingsPage::networkConnectionError(const QString&) {} -void NetworksSettingsPage::networkConnectionError(const QString &) -{ -} - - -QListWidgetItem *NetworksSettingsPage::insertNetwork(NetworkId id) +QListWidgetItem* NetworksSettingsPage::insertNetwork(NetworkId id) { NetworkInfo info = Client::network(id)->networkInfo(); networkInfos[id] = info; return insertNetwork(info); } - -QListWidgetItem *NetworksSettingsPage::insertNetwork(const NetworkInfo &info) +QListWidgetItem* NetworksSettingsPage::insertNetwork(const NetworkInfo& info) { - QListWidgetItem *item = 0; - QList items = ui.networkList->findItems(info.networkName, Qt::MatchExactly); - if (!items.count()) item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList); + QListWidgetItem* item = nullptr; + QList items = ui.networkList->findItems(info.networkName, Qt::MatchExactly); + if (!items.count()) + item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList); else { // we overwrite an existing net if it a) has the same name and b) has a negative ID meaning we created it locally before // -> then we can be sure that this is the core-side replacement for the net we created - foreach(QListWidgetItem *i, items) { + foreach (QListWidgetItem* i, items) { NetworkId id = i->data(Qt::UserRole).value(); - if (id < 0) { item = i; break; } + if (id < 0) { + item = i; + break; + } } - if (!item) item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList); + if (!item) + item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList); } - item->setData(Qt::UserRole, QVariant::fromValue(info.networkId)); + item->setData(Qt::UserRole, QVariant::fromValue(info.networkId)); setItemState(info.networkId, item); widgetHasChanged(); return item; } +// Called when selecting 'Configure' from the buffer list +void NetworksSettingsPage::bufferList_Open(NetworkId netId) +{ + QListWidgetItem* item = networkItem(netId); + ui.networkList->setCurrentItem(item, QItemSelectionModel::SelectCurrent); +} void NetworksSettingsPage::displayNetwork(NetworkId id) { @@ -479,29 +591,38 @@ void NetworksSettingsPage::displayNetwork(NetworkId id) if (id != 0) { NetworkInfo info = networkInfos[id]; -#ifdef HAVE_SSL // this is only needed when the core supports SASL EXTERNAL - if (Client::coreFeatures() & Quassel::SaslExternal) { + if (Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal)) { if (_cid) { - disconnect(_cid, SIGNAL(sslSettingsUpdated()), this, SLOT(sslUpdated())); + // Clean up existing CertIdentity + disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated); delete _cid; + _cid = nullptr; + } + auto *identity = Client::identity(info.identity); + if (identity) { + // Connect new CertIdentity + _cid = new CertIdentity(*identity, this); + _cid->enableEditSsl(true); + connect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated); + } + else { + qWarning() << "NetworksSettingsPage::displayNetwork can't find Identity for IdentityId:" << info.identity; } - _cid = new CertIdentity(*Client::identity(info.identity), this); - _cid->enableEditSsl(true); - connect(_cid, SIGNAL(sslSettingsUpdated()), this, SLOT(sslUpdated())); } -#endif ui.identityList->setCurrentIndex(ui.identityList->findData(info.identity.toInt())); ui.serverList->clear(); - foreach(Network::Server server, info.serverList) { - QListWidgetItem *item = new QListWidgetItem(QString("%1:%2").arg(server.host).arg(server.port)); + foreach (Network::Server server, info.serverList) { + QListWidgetItem* item = new QListWidgetItem(QString("%1:%2").arg(server.host).arg(server.port)); if (server.useSsl) - item->setIcon(SmallIcon("document-encrypt")); + item->setIcon(icon::get("document-encrypt")); ui.serverList->addItem(item); } - //setItemState(id); - //ui.randomServer->setChecked(info.useRandomServer); + // setItemState(id); + // ui.randomServer->setChecked(info.useRandomServer); + // Update the capability-dependent UI in case capabilities have changed. + setNetworkCapStates(id); ui.performEdit->setPlainText(info.perform.join("\n")); ui.autoIdentify->setChecked(info.useAutoIdentify); ui.autoIdentifyService->setText(info.autoIdentifyService); @@ -526,15 +647,23 @@ void NetworksSettingsPage::displayNetwork(NetworkId id) ui.reconnectRetries->setValue(info.autoReconnectRetries); ui.unlimitedRetries->setChecked(info.unlimitedReconnectRetries); ui.rejoinOnReconnect->setChecked(info.rejoinChannels); + // Custom rate limiting + ui.unlimitedMessageRate->setChecked(info.unlimitedMessageRate); + // Set 'ui.useCustomMessageRate' after 'ui.unlimitedMessageRate' so if the latter is + // disabled, 'ui.messageRateDelayFrame' will remain disabled. + ui.useCustomMessageRate->setChecked(info.useCustomMessageRate); + ui.messageRateBurstSize->setValue(info.messageRateBurstSize); + // Convert milliseconds (integer) into seconds (double) + ui.messageRateDelay->setValue(info.messageRateDelay / 1000.0f); + // Skipped IRCv3 capabilities + ui.enableCapServerTime->setChecked(!info.skipCaps.contains(IrcCap::SERVER_TIME)); } else { // just clear widgets -#ifdef HAVE_SSL if (_cid) { - disconnect(_cid, SIGNAL(sslSettingsUpdated()), this, SLOT(sslUpdated())); + disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated); delete _cid; } -#endif ui.identityList->setCurrentIndex(-1); ui.serverList->clear(); ui.performEdit->clear(); @@ -548,11 +677,10 @@ void NetworksSettingsPage::displayNetwork(NetworkId id) currentId = id; } - -void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo &info) +void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo& info) { info.identity = ui.identityList->itemData(ui.identityList->currentIndex()).toInt(); - //info.useRandomServer = ui.randomServer->isChecked(); + // info.useRandomServer = ui.randomServer->isChecked(); info.perform = ui.performEdit->toPlainText().split("\n"); info.useAutoIdentify = ui.autoIdentify->isChecked(); info.autoIdentifyService = ui.autoIdentifyService->text(); @@ -575,28 +703,107 @@ void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo &info) info.autoReconnectRetries = ui.reconnectRetries->value(); info.unlimitedReconnectRetries = ui.unlimitedRetries->isChecked(); info.rejoinChannels = ui.rejoinOnReconnect->isChecked(); + // Custom rate limiting + info.useCustomMessageRate = ui.useCustomMessageRate->isChecked(); + info.messageRateBurstSize = ui.messageRateBurstSize->value(); + // Convert seconds (double) into milliseconds (integer) + info.messageRateDelay = static_cast((ui.messageRateDelay->value() * 1000)); + info.unlimitedMessageRate = ui.unlimitedMessageRate->isChecked(); + // Skipped IRCv3 capabilities + if (ui.enableCapServerTime->isChecked()) { + // Capability enabled, remove it from the skip list + info.skipCaps.removeAll(IrcCap::SERVER_TIME); + } else if (!info.skipCaps.contains(IrcCap::SERVER_TIME)) { + // Capability disabled and not in the skip list, add it + info.skipCaps.append(IrcCap::SERVER_TIME); + } } +void NetworksSettingsPage::clientNetworkCapsUpdated() +{ + // Grab the updated network + const auto* net = qobject_cast(sender()); + if (!net) { + qWarning() << "Update request for unknown network received!"; + return; + } + if (net->networkId() == currentId) { + // Network is currently shown. Update the capability-dependent UI in case capabilities have + // changed. + setNetworkCapStates(currentId); + } +} + +void NetworksSettingsPage::setCapSASLStatus(const CapSupportStatus saslStatus, bool usingSASLExternal) +{ + if (_capSaslStatusSelected != saslStatus || _capSaslStatusUsingExternal != usingSASLExternal) { + // Update the cached copy of SASL status used with the Details dialog + _capSaslStatusSelected = saslStatus; + _capSaslStatusUsingExternal = usingSASLExternal; + + // Update the user interface + switch (saslStatus) { + case CapSupportStatus::Unknown: + // There's no capability negotiation or network doesn't exist. Don't assume + // anything. + ui.saslStatusLabel->setText(QString("%1").arg(tr("Could not check if supported by network"))); + ui.saslStatusIcon->setPixmap(questionIcon.pixmap(16)); + break; + case CapSupportStatus::Disconnected: + // Disconnected from network, no way to check. + ui.saslStatusLabel->setText(QString("%1").arg(tr("Cannot check if supported when disconnected"))); + ui.saslStatusIcon->setPixmap(questionIcon.pixmap(16)); + break; + case CapSupportStatus::MaybeUnsupported: + // The network doesn't advertise support for SASL PLAIN/EXTERNAL. Here be dragons. + ui.saslStatusLabel->setText(QString("%1").arg(tr("Not currently supported by network"))); + ui.saslStatusIcon->setPixmap(unavailableIcon.pixmap(16)); + break; + case CapSupportStatus::MaybeSupported: + // The network advertises support for SASL PLAIN/EXTERNAL. Encourage using it! + // Unfortunately we don't know for sure if it's desired or functional. + if (usingSASLExternal) { + // SASL EXTERNAL is used + // With SASL v3.1, it's not possible to reliably tell if SASL EXTERNAL is supported, + // or just SASL PLAIN. Use less assertive phrasing. + ui.saslStatusLabel->setText(QString("%1").arg(tr("May be supported by network"))); + } + else { + // SASL PLAIN is used + ui.saslStatusLabel->setText(QString("%1").arg(tr("Supported by network"))); + } + ui.saslStatusIcon->setPixmap(successIcon.pixmap(16)); + break; + } + } +} -#ifdef HAVE_SSL void NetworksSettingsPage::sslUpdated() { - if (_cid && !_cid->sslKey().isNull()) { - ui.saslAccount->setDisabled(true); - ui.saslAccountLabel->setDisabled(true); - ui.saslPassword->setDisabled(true); - ui.saslPasswordLabel->setDisabled(true); + if (displayedNetworkHasCertId()) { + ui.saslPlainContents->setDisabled(true); ui.saslExtInfo->setHidden(false); - } else { - ui.saslAccount->setDisabled(false); - ui.saslAccountLabel->setDisabled(false); - ui.saslPassword->setDisabled(false); - ui.saslPasswordLabel->setDisabled(false); + } + else { + ui.saslPlainContents->setDisabled(false); + // Directly re-enabling causes the widgets to ignore the parent "Use SASL Authentication" + // state to indicate whether or not it's disabled. To workaround this, keep track of + // whether or not "Use SASL Authentication" is enabled, then quickly uncheck/recheck the + // group box. + if (!ui.sasl->isChecked()) { + // SASL is not enabled, uncheck/recheck the group box to re-disable saslPlainContents. + // Leaving saslPlainContents disabled doesn't work as that prevents it from re-enabling if + // sasl is later checked. + ui.sasl->setChecked(true); + ui.sasl->setChecked(false); + } ui.saslExtInfo->setHidden(true); } + // Update whether SASL PLAIN or SASL EXTERNAL is used to detect SASL status + if (currentId != 0) { + setNetworkCapStates(currentId); + } } -#endif - /*** Network list ***/ @@ -617,11 +824,11 @@ void NetworksSettingsPage::on_networkList_itemSelectionChanged() setWidgetStates(); } - void NetworksSettingsPage::on_addNetwork_clicked() { QStringList existing; - for (int i = 0; i < ui.networkList->count(); i++) existing << ui.networkList->item(i)->text(); + for (int i = 0; i < ui.networkList->count(); i++) + existing << ui.networkList->item(i)->text(); NetworkAddDlg dlg(existing, this); if (dlg.exec() == QDialog::Accepted) { NetworkInfo info = dlg.networkInfo(); @@ -631,44 +838,49 @@ void NetworksSettingsPage::on_addNetwork_clicked() NetworkId id; for (id = 1; id <= networkInfos.count(); id++) { widgetHasChanged(); - if (!networkInfos.keys().contains(-id.toInt())) break; + if (!networkInfos.keys().contains(-id.toInt())) + break; } id = -id.toInt(); info.networkId = id; info.identity = defaultIdentity(); networkInfos[id] = info; - QListWidgetItem *item = insertNetwork(info); + QListWidgetItem* item = insertNetwork(info); ui.networkList->setCurrentItem(item); setWidgetStates(); } } - void NetworksSettingsPage::on_deleteNetwork_clicked() { if (ui.networkList->selectedItems().count()) { NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value(); - int ret = QMessageBox::question(this, tr("Delete Network?"), - tr("Do you really want to delete the network \"%1\" and all related settings, including the backlog?").arg(networkInfos[netid].networkName), - QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + int ret + = QMessageBox::question(this, + tr("Delete Network?"), + tr("Do you really want to delete the network \"%1\" and all related settings, including the backlog?") + .arg(networkInfos[netid].networkName), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); if (ret == QMessageBox::Yes) { currentId = 0; networkInfos.remove(netid); delete ui.networkList->takeItem(ui.networkList->row(ui.networkList->selectedItems()[0])); - ui.networkList->setCurrentRow(qMin(ui.networkList->currentRow()+1, ui.networkList->count()-1)); + ui.networkList->setCurrentRow(qMin(ui.networkList->currentRow() + 1, ui.networkList->count() - 1)); setWidgetStates(); widgetHasChanged(); } } } - void NetworksSettingsPage::on_renameNetwork_clicked() { - if (!ui.networkList->selectedItems().count()) return; + if (!ui.networkList->selectedItems().count()) + return; QString old = ui.networkList->selectedItems()[0]->text(); QStringList existing; - for (int i = 0; i < ui.networkList->count(); i++) existing << ui.networkList->item(i)->text(); + for (int i = 0; i < ui.networkList->count(); i++) + existing << ui.networkList->item(i)->text(); NetworkEditDlg dlg(old, existing, this); if (dlg.exec() == QDialog::Accepted) { ui.networkList->selectedItems()[0]->setText(dlg.networkName()); @@ -678,7 +890,6 @@ void NetworksSettingsPage::on_renameNetwork_clicked() } } - /* void NetworksSettingsPage::on_connectNow_clicked() { if(!ui.networkList->selectedItems().count()) return; @@ -697,23 +908,23 @@ void NetworksSettingsPage::on_serverList_itemSelectionChanged() setWidgetStates(); } - void NetworksSettingsPage::on_addServer_clicked() { - if (currentId == 0) return; + if (currentId == 0) + return; ServerEditDlg dlg(Network::Server(), this); if (dlg.exec() == QDialog::Accepted) { networkInfos[currentId].serverList.append(dlg.serverData()); displayNetwork(currentId); - ui.serverList->setCurrentRow(ui.serverList->count()-1); + ui.serverList->setCurrentRow(ui.serverList->count() - 1); widgetHasChanged(); } } - void NetworksSettingsPage::on_editServer_clicked() { - if (currentId == 0) return; + if (currentId == 0) + return; int cur = ui.serverList->currentRow(); ServerEditDlg dlg(networkInfos[currentId].serverList[cur], this); if (dlg.exec() == QDialog::Accepted) { @@ -724,97 +935,283 @@ void NetworksSettingsPage::on_editServer_clicked() } } - void NetworksSettingsPage::on_deleteServer_clicked() { - if (currentId == 0) return; + if (currentId == 0) + return; int cur = ui.serverList->currentRow(); networkInfos[currentId].serverList.removeAt(cur); displayNetwork(currentId); - ui.serverList->setCurrentRow(qMin(cur, ui.serverList->count()-1)); + ui.serverList->setCurrentRow(qMin(cur, ui.serverList->count() - 1)); widgetHasChanged(); } - void NetworksSettingsPage::on_upServer_clicked() { int cur = ui.serverList->currentRow(); Network::Server server = networkInfos[currentId].serverList.takeAt(cur); - networkInfos[currentId].serverList.insert(cur-1, server); + networkInfos[currentId].serverList.insert(cur - 1, server); displayNetwork(currentId); - ui.serverList->setCurrentRow(cur-1); + ui.serverList->setCurrentRow(cur - 1); widgetHasChanged(); } - void NetworksSettingsPage::on_downServer_clicked() { int cur = ui.serverList->currentRow(); Network::Server server = networkInfos[currentId].serverList.takeAt(cur); - networkInfos[currentId].serverList.insert(cur+1, server); + networkInfos[currentId].serverList.insert(cur + 1, server); displayNetwork(currentId); - ui.serverList->setCurrentRow(cur+1); + ui.serverList->setCurrentRow(cur + 1); widgetHasChanged(); } - void NetworksSettingsPage::on_editIdentities_clicked() { SettingsPageDlg dlg(new IdentitiesSettingsPage(this), this); dlg.exec(); } +void NetworksSettingsPage::on_saslStatusDetails_clicked() +{ + if (ui.networkList->selectedItems().count()) { + NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value(); + QString& netName = networkInfos[netid].networkName; + + // If these strings are visible, one of the status messages wasn't detected below. + QString saslStatusHeader = "[header unintentionally left blank]"; + QString saslStatusExplanation = "[explanation unintentionally left blank]"; + + // If true, show a warning icon instead of an information icon + bool useWarningIcon = false; + + // Determine which explanation to show + switch (_capSaslStatusSelected) { + case CapSupportStatus::Unknown: + saslStatusHeader = tr("Could not check if SASL supported by network"); + saslStatusExplanation = tr("Quassel could not check if \"%1\" supports SASL. This may " + "be due to unsaved changes or an older Quassel core. You " + "can still try using SASL.") + .arg(netName); + break; + case CapSupportStatus::Disconnected: + saslStatusHeader = tr("Cannot check if SASL supported when disconnected"); + saslStatusExplanation = tr("Quassel cannot check if \"%1\" supports SASL when " + "disconnected. Connect to the network, or try using SASL " + "anyways.") + .arg(netName); + break; + case CapSupportStatus::MaybeUnsupported: + if (displayedNetworkHasCertId()) { + // SASL EXTERNAL is used + saslStatusHeader = tr("SASL EXTERNAL not currently supported by network"); + saslStatusExplanation = tr("The network \"%1\" does not currently support SASL " + "EXTERNAL for SSL certificate authentication. However, " + "support might be added later on.") + .arg(netName); + } + else { + // SASL PLAIN is used + saslStatusHeader = tr("SASL not currently supported by network"); + saslStatusExplanation = tr("The network \"%1\" does not currently support SASL. " + "However, support might be added later on.") + .arg(netName); + } + useWarningIcon = true; + break; + case CapSupportStatus::MaybeSupported: + if (displayedNetworkHasCertId()) { + // SASL EXTERNAL is used + // With SASL v3.1, it's not possible to reliably tell if SASL EXTERNAL is supported, + // or just SASL PLAIN. Caution about this in the details dialog. + saslStatusHeader = tr("SASL EXTERNAL may be supported by network"); + saslStatusExplanation = tr("The network \"%1\" may support SASL EXTERNAL for SSL " + "certificate authentication. In most cases, you should " + "use SASL instead of NickServ identification.") + .arg(netName); + } + else { + // SASL PLAIN is used + saslStatusHeader = tr("SASL supported by network"); + saslStatusExplanation = tr("The network \"%1\" supports SASL. In most cases, you " + "should use SASL instead of NickServ identification.") + .arg(netName); + } + break; + } + + // Process this in advance for reusability below + const QString saslStatusMsgTitle = tr("SASL support for \"%1\"").arg(netName); + const QString saslStatusMsgText = QString("

%1


%2


%3

") + .arg(saslStatusHeader, + saslStatusExplanation, + tr("SASL is a standardized way to log in and identify yourself to " + "IRC servers.")); + + if (useWarningIcon) { + // Show as a warning dialog box + QMessageBox::warning(this, saslStatusMsgTitle, saslStatusMsgText); + } + else { + // Show as an information dialog box + QMessageBox::information(this, saslStatusMsgTitle, saslStatusMsgText); + } + } +} + +void NetworksSettingsPage::on_enableCapsStatusDetails_clicked() +{ + if (!Client::isConnected() || Client::isCoreFeatureEnabled(Quassel::Feature::SkipIrcCaps)) { + // Either disconnected or IRCv3 capability skippping supported + + // Try to get a list of currently enabled features + QStringList sortedCapsEnabled; + // Check if a network is selected + if (ui.networkList->selectedItems().count()) { + // Get the underlying Network from the selected network + NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value(); + const Network* net = Client::network(netid); + if (net && Client::isCoreFeatureEnabled(Quassel::Feature::CapNegotiation)) { + // Capability negotiation is supported, network exists. + // If the network is disconnected, the list of enabled capabilities will be empty, + // no need to check for that specifically. + // Sorting isn't required, but it looks nicer. + sortedCapsEnabled = net->capsEnabled(); + sortedCapsEnabled.sort(); + } + } + + // Try to explain IRCv3 network features in a friendly way, including showing the currently + // enabled features if available + auto messageText = QString("

%1


%2

") + .arg(tr("Quassel makes use of newer IRC features when supported by the IRC network." + " If desired, you can disable unwanted or problematic features here."), + tr("The IRCv3 website provides more " + "technical details on the IRCv3 capabilities powering these features.")); + + if (!sortedCapsEnabled.isEmpty()) { + // Format the capabilities within blocks + auto formattedCaps = QString("%1") + .arg(sortedCapsEnabled.join(", ")); + + // Add the currently enabled capabilities to the list + // This creates a new QString, but this code is not performance-critical. + messageText = messageText.append(QString("

%1

").arg( + tr("Currently enabled IRCv3 capabilities for this " + "network: %1").arg(formattedCaps))); + } + + QMessageBox::information(this, tr("Configuring network features"), messageText); + } + else { + // Core does not IRCv3 capability skipping, show warning + QMessageBox::warning(this, tr("Configuring network features unsupported"), + QString("

%1


%2

") + .arg(tr("Your Quassel core is too old to configure IRCv3 network features"), + tr("You need a Quassel core v0.14.0 or newer to control what network " + "features Quassel will use."))); + } +} + +void NetworksSettingsPage::on_enableCapsAdvanced_clicked() +{ + if (currentId == 0) + return; + + CapsEditDlg dlg(networkInfos[currentId].skipCapsToString(), this); + if (dlg.exec() == QDialog::Accepted) { + networkInfos[currentId].skipCapsFromString(dlg.skipCapsString()); + displayNetwork(currentId); + widgetHasChanged(); + } +} IdentityId NetworksSettingsPage::defaultIdentity() const { IdentityId defaultId = 0; QList ids = Client::identityIds(); - foreach(IdentityId id, ids) { + foreach (IdentityId id, ids) { if (defaultId == 0 || id < defaultId) defaultId = id; } return defaultId; } +bool NetworksSettingsPage::displayedNetworkHasCertId() const +{ + // Check if the CertIdentity exists and that it has a non-null SSL key set + return (_cid && !_cid->sslKey().isNull()); +} /************************************************************************** -* NetworkAddDlg -*************************************************************************/ + * NetworkAddDlg + *************************************************************************/ -NetworkAddDlg::NetworkAddDlg(const QStringList &exist, QWidget *parent) : QDialog(parent), existing(exist) +NetworkAddDlg::NetworkAddDlg(QStringList exist, QWidget* parent) + : QDialog(parent) + , existing(std::move(exist)) { ui.setupUi(this); - ui.useSSL->setIcon(SmallIcon("document-encrypt")); + ui.useSSL->setIcon(icon::get("document-encrypt")); + + // Whenever useSSL is toggled, update the port number if not changed from the default + connect(ui.useSSL, &QAbstractButton::toggled, this, &NetworkAddDlg::updateSslPort); + // Do NOT call updateSslPort when loading settings, otherwise port settings may be overridden. + // If useSSL is later changed to be checked by default, change port's default value, too. + + if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) { + // Synchronize requiring SSL with the use SSL checkbox + ui.sslVerify->setEnabled(ui.useSSL->isChecked()); + connect(ui.useSSL, &QAbstractButton::toggled, ui.sslVerify, &QWidget::setEnabled); + } + else { + // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck + ui.sslVerify->setEnabled(false); + ui.sslVerify->setChecked(false); + // Split up the message to allow re-using translations: + // [Original tool-tip] + // [Bold 'does not support feature' message] + // [Specific version needed and feature details] + ui.sslVerify->setToolTip(QString("%1
%2
%3") + .arg(ui.sslVerify->toolTip(), + tr("Your Quassel core does not support this feature"), + tr("You need a Quassel core v0.13.0 or newer in order to " + "verify connection security."))); + } // read preset networks QStringList networks = PresetNetworks::names(); - foreach(QString s, existing) - networks.removeAll(s); + foreach (QString s, existing) + networks.removeAll(s); if (networks.count()) ui.presetList->addItems(networks); else { ui.useManual->setChecked(true); ui.usePreset->setEnabled(false); } - connect(ui.networkName, SIGNAL(textChanged(const QString &)), SLOT(setButtonStates())); - connect(ui.serverAddress, SIGNAL(textChanged(const QString &)), SLOT(setButtonStates())); + connect(ui.networkName, &QLineEdit::textChanged, this, &NetworkAddDlg::setButtonStates); + connect(ui.serverAddress, &QLineEdit::textChanged, this, &NetworkAddDlg::setButtonStates); + connect(ui.usePreset, &QRadioButton::toggled, this, &NetworkAddDlg::setButtonStates); + connect(ui.useManual, &QRadioButton::toggled, this, &NetworkAddDlg::setButtonStates); setButtonStates(); } - NetworkInfo NetworkAddDlg::networkInfo() const { if (ui.useManual->isChecked()) { NetworkInfo info; info.networkName = ui.networkName->text().trimmed(); - info.serverList << Network::Server(ui.serverAddress->text().trimmed(), ui.port->value(), ui.serverPassword->text(), ui.useSSL->isChecked()); + info.serverList << Network::Server(ui.serverAddress->text().trimmed(), + ui.port->value(), + ui.serverPassword->text(), + ui.useSSL->isChecked(), + ui.sslVerify->isChecked()); return info; } else return PresetNetworks::networkInfo(ui.presetList->currentText()); } - void NetworkAddDlg::setButtonStates() { bool ok = false; @@ -827,60 +1224,118 @@ void NetworkAddDlg::setButtonStates() ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok); } +void NetworkAddDlg::updateSslPort(bool isChecked) +{ + // "Use encrypted connection" was toggled, check the state... + if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) { + // Had been using the plain-text port, use the SSL default + ui.port->setValue(Network::PORT_SSL); + } + else if (!isChecked && ui.port->value() == Network::PORT_SSL) { + // Had been using the SSL port, use the plain-text default + ui.port->setValue(Network::PORT_PLAINTEXT); + } +} /************************************************************************** * NetworkEditDlg *************************************************************************/ -NetworkEditDlg::NetworkEditDlg(const QString &old, const QStringList &exist, QWidget *parent) : QDialog(parent), existing(exist) +NetworkEditDlg::NetworkEditDlg(const QString& old, QStringList exist, QWidget* parent) + : QDialog(parent) + , existing(std::move(exist)) { ui.setupUi(this); if (old.isEmpty()) { // new network setWindowTitle(tr("Add Network")); - on_networkEdit_textChanged(""); // disable ok button + on_networkEdit_textChanged(""); // disable ok button } - else ui.networkEdit->setText(old); + else + ui.networkEdit->setText(old); } - QString NetworkEditDlg::networkName() const { return ui.networkEdit->text().trimmed(); } - -void NetworkEditDlg::on_networkEdit_textChanged(const QString &text) +void NetworkEditDlg::on_networkEdit_textChanged(const QString& text) { ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty() || existing.contains(text.trimmed())); } - /************************************************************************** * ServerEditDlg *************************************************************************/ -ServerEditDlg::ServerEditDlg(const Network::Server &server, QWidget *parent) : QDialog(parent) +ServerEditDlg::ServerEditDlg(const Network::Server& server, QWidget* parent) + : QDialog(parent) { ui.setupUi(this); - ui.useSSL->setIcon(SmallIcon("document-encrypt")); + ui.useSSL->setIcon(icon::get("document-encrypt")); ui.host->setText(server.host); + ui.host->setFocus(); ui.port->setValue(server.port); ui.password->setText(server.password); ui.useSSL->setChecked(server.useSsl); + ui.sslVerify->setChecked(server.sslVerify); + ui.sslVersion->setCurrentIndex(server.sslVersion); ui.useProxy->setChecked(server.useProxy); ui.proxyType->setCurrentIndex(server.proxyType == QNetworkProxy::Socks5Proxy ? 0 : 1); ui.proxyHost->setText(server.proxyHost); ui.proxyPort->setValue(server.proxyPort); ui.proxyUsername->setText(server.proxyUser); ui.proxyPassword->setText(server.proxyPass); + + // This is a dirty hack to display the core->IRC SSL protocol dropdown + // only if the core won't use autonegotiation to determine the best + // protocol. When autonegotiation was introduced, it would have been + // a good idea to use the CoreFeatures enum to accomplish this. + // However, since multiple versions have been released since then, that + // is no longer possible. Instead, we rely on the fact that the + // Datastream protocol was introduced in the same version (0.10) as SSL + // autonegotiation. Because of that, we can display the dropdown only + // if the Legacy protocol is in use. If any other RemotePeer protocol + // is in use, that means a newer protocol is in use and therefore the + // core will use autonegotiation. + if (Client::coreConnection()->peer()->protocol() != Protocol::LegacyProtocol) { + ui.label_3->hide(); + ui.sslVersion->hide(); + } + + // Whenever useSSL is toggled, update the port number if not changed from the default + connect(ui.useSSL, &QAbstractButton::toggled, this, &ServerEditDlg::updateSslPort); + // Do NOT call updateSslPort when loading settings, otherwise port settings may be overridden. + // If useSSL is later changed to be checked by default, change port's default value, too. + + if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) { + // Synchronize requiring SSL with the use SSL checkbox + ui.sslVerify->setEnabled(ui.useSSL->isChecked()); + connect(ui.useSSL, &QAbstractButton::toggled, ui.sslVerify, &QWidget::setEnabled); + } + else { + // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck + ui.sslVerify->setEnabled(false); + ui.sslVerify->setChecked(false); + // Split up the message to allow re-using translations: + // [Original tool-tip] + // [Bold 'does not support feature' message] + // [Specific version needed and feature details] + ui.sslVerify->setToolTip(QString("%1
%2
%3") + .arg(ui.sslVerify->toolTip(), + tr("Your Quassel core does not support this feature"), + tr("You need a Quassel core v0.13.0 or newer in order to " + "verify connection security."))); + } + on_host_textChanged(); } - Network::Server ServerEditDlg::serverData() const { - Network::Server server(ui.host->text().trimmed(), ui.port->value(), ui.password->text(), ui.useSSL->isChecked()); + Network::Server server(ui.host->text().trimmed(), ui.port->value(), ui.password->text(), ui.useSSL->isChecked(), ui.sslVerify->isChecked()); + server.sslVersion = ui.sslVersion->currentIndex(); server.useProxy = ui.useProxy->isChecked(); server.proxyType = ui.proxyType->currentIndex() == 0 ? QNetworkProxy::Socks5Proxy : QNetworkProxy::HttpProxy; server.proxyHost = ui.proxyHost->text(); @@ -890,18 +1345,74 @@ Network::Server ServerEditDlg::serverData() const return server; } - void ServerEditDlg::on_host_textChanged() { ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(ui.host->text().trimmed().isEmpty()); } +void ServerEditDlg::updateSslPort(bool isChecked) +{ + // "Use encrypted connection" was toggled, check the state... + if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) { + // Had been using the plain-text port, use the SSL default + ui.port->setValue(Network::PORT_SSL); + } + else if (!isChecked && ui.port->value() == Network::PORT_SSL) { + // Had been using the SSL port, use the plain-text default + ui.port->setValue(Network::PORT_PLAINTEXT); + } +} + +/************************************************************************** + * CapsEditDlg + *************************************************************************/ + +CapsEditDlg::CapsEditDlg(const QString& oldSkipCapsString, QWidget* parent) + : QDialog(parent) + , oldSkipCapsString(oldSkipCapsString) +{ + ui.setupUi(this); + + // Connect to the reset button to reset the text + // This provides an explicit way to "get back to defaults" in case someone changes settings to + // experiment + QPushButton* defaultsButton = ui.buttonBox->button(QDialogButtonBox::RestoreDefaults); + connect(defaultsButton, &QPushButton::clicked, this, &CapsEditDlg::defaultSkipCaps); + + if (oldSkipCapsString.isEmpty()) { + // Disable Reset button + on_skipCapsEdit_textChanged(""); + } + else { + ui.skipCapsEdit->setText(oldSkipCapsString); + } +} + + +QString CapsEditDlg::skipCapsString() const +{ + return ui.skipCapsEdit->text(); +} + +void CapsEditDlg::defaultSkipCaps() +{ + ui.skipCapsEdit->setText(""); +} + +void CapsEditDlg::on_skipCapsEdit_textChanged(const QString& text) +{ + ui.buttonBox->button(QDialogButtonBox::RestoreDefaults)->setDisabled(text.isEmpty()); +} /************************************************************************** * SaveNetworksDlg *************************************************************************/ -SaveNetworksDlg::SaveNetworksDlg(const QList &toCreate, const QList &toUpdate, const QList &toRemove, QWidget *parent) : QDialog(parent) +SaveNetworksDlg::SaveNetworksDlg(const QList& toCreate, + const QList& toUpdate, + const QList& toRemove, + QWidget* parent) + : QDialog(parent) { ui.setupUi(this); @@ -911,24 +1422,24 @@ SaveNetworksDlg::SaveNetworksDlg(const QList &toCreate, const QList ui.progressBar->setMaximum(numevents); ui.progressBar->setValue(0); - connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), this, SLOT(clientEvent())); - connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), this, SLOT(clientEvent())); + connect(Client::instance(), &Client::networkCreated, this, &SaveNetworksDlg::clientEvent); + connect(Client::instance(), &Client::networkRemoved, this, &SaveNetworksDlg::clientEvent); - foreach(NetworkId id, toRemove) { + foreach (NetworkId id, toRemove) { Client::removeNetwork(id); } - foreach(NetworkInfo info, toCreate) { + foreach (NetworkInfo info, toCreate) { Client::createNetwork(info); } - foreach(NetworkInfo info, toUpdate) { - const Network *net = Client::network(info.networkId); + foreach (NetworkInfo info, toUpdate) { + const Network* net = Client::network(info.networkId); if (!net) { qWarning() << "Invalid client network!"; numevents--; continue; } // FIXME this only checks for one changed item rather than all! - connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientEvent())); + connect(net, &SyncableObject::updatedRemotely, this, &SaveNetworksDlg::clientEvent); Client::updateNetwork(info); } } @@ -938,9 +1449,9 @@ SaveNetworksDlg::SaveNetworksDlg(const QList &toCreate, const QList } } - void SaveNetworksDlg::clientEvent() { ui.progressBar->setValue(++rcvevents); - if (rcvevents >= numevents) accept(); + if (rcvevents >= numevents) + accept(); }