+ return insertNetwork(info);
+}
+
+QListWidgetItem* NetworksSettingsPage::insertNetwork(const NetworkInfo& info)
+{
+ QListWidgetItem* item = nullptr;
+ QList<QListWidgetItem*> 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) {
+ NetworkId id = i->data(Qt::UserRole).value<NetworkId>();
+ if (id < 0) {
+ item = i;
+ break;
+ }
+ }
+ if (!item)
+ item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
+ }
+ 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)
+{
+ _ignoreWidgetChanges = true;
+ if (id != 0) {
+ NetworkInfo info = networkInfos[id];
+
+ // this is only needed when the core supports SASL EXTERNAL
+ if (Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal)) {
+ if (_cid) {
+ // 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;
+ }
+ }
+
+ 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));
+ if (server.useSsl)
+ item->setIcon(icon::get("document-encrypt"));
+ ui.serverList->addItem(item);
+ }
+ // 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);
+ ui.autoIdentifyPassword->setText(info.autoIdentifyPassword);
+ ui.sasl->setChecked(info.useSasl);
+ ui.saslAccount->setText(info.saslAccount);
+ ui.saslPassword->setText(info.saslPassword);
+ if (info.codecForEncoding.isEmpty()) {
+ ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(Network::defaultCodecForEncoding()));
+ ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(Network::defaultCodecForDecoding()));
+ ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(Network::defaultCodecForServer()));
+ ui.useCustomEncodings->setChecked(false);
+ }
+ else {
+ ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(info.codecForEncoding));
+ ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(info.codecForDecoding));
+ ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(info.codecForServer));
+ ui.useCustomEncodings->setChecked(true);
+ }
+ ui.autoReconnect->setChecked(info.useAutoReconnect);
+ ui.reconnectInterval->setValue(info.autoReconnectInterval);
+ 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
+ if (_cid) {
+ disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
+ delete _cid;
+ }
+ ui.identityList->setCurrentIndex(-1);
+ ui.serverList->clear();
+ ui.performEdit->clear();
+ ui.autoIdentifyService->clear();
+ ui.autoIdentifyPassword->clear();
+ ui.saslAccount->clear();
+ ui.saslPassword->clear();
+ setWidgetStates();
+ }
+ _ignoreWidgetChanges = false;
+ currentId = id;
+}
+
+void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo& info)
+{
+ info.identity = ui.identityList->itemData(ui.identityList->currentIndex()).toInt();
+ // info.useRandomServer = ui.randomServer->isChecked();
+ info.perform = ui.performEdit->toPlainText().split("\n");
+ info.useAutoIdentify = ui.autoIdentify->isChecked();
+ info.autoIdentifyService = ui.autoIdentifyService->text();
+ info.autoIdentifyPassword = ui.autoIdentifyPassword->text();
+ info.useSasl = ui.sasl->isChecked();
+ info.saslAccount = ui.saslAccount->text();
+ info.saslPassword = ui.saslPassword->text();
+ if (!ui.useCustomEncodings->isChecked()) {
+ info.codecForEncoding.clear();
+ info.codecForDecoding.clear();
+ info.codecForServer.clear();
+ }
+ else {
+ info.codecForEncoding = ui.sendEncoding->currentText().toLatin1();
+ info.codecForDecoding = ui.recvEncoding->currentText().toLatin1();
+ info.codecForServer = ui.serverEncoding->currentText().toLatin1();
+ }
+ info.useAutoReconnect = ui.autoReconnect->isChecked();
+ info.autoReconnectInterval = ui.reconnectInterval->value();
+ 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<quint32>((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<const Network*>(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("<i>%1</i>").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("<i>%1</i>").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("<i>%1</i>").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("<i>%1</i>").arg(tr("May be supported by network")));
+ }
+ else {
+ // SASL PLAIN is used
+ ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Supported by network")));
+ }
+ ui.saslStatusIcon->setPixmap(successIcon.pixmap(16));
+ break;
+ }
+ }
+}
+
+void NetworksSettingsPage::sslUpdated()
+{
+ if (displayedNetworkHasCertId()) {
+ ui.saslPlainContents->setDisabled(true);
+ ui.saslExtInfo->setHidden(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);
+ }
+}
+
+/*** Network list ***/
+
+void NetworksSettingsPage::on_networkList_itemSelectionChanged()
+{
+ if (currentId != 0) {
+ saveToNetworkInfo(networkInfos[currentId]);
+ }
+ if (ui.networkList->selectedItems().count()) {
+ NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
+ currentId = id;
+ displayNetwork(id);
+ ui.serverList->setCurrentRow(0);
+ }
+ else {
+ currentId = 0;
+ }