1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "networkssettingspage.h"
25 #include <QHeaderView>
26 #include <QMessageBox>
33 #include "presetnetworks.h"
34 #include "settingspagedlg.h"
36 #include "widgethelpers.h"
41 #include "settingspages/identitiessettingspage.h"
43 NetworksSettingsPage::NetworksSettingsPage(QWidget* parent)
44 : SettingsPage(tr("IRC"), tr("Networks"), parent)
48 // hide SASL options for older cores
49 if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslAuthentication))
51 if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal))
52 ui.saslExtInfo->hide();
55 ui.renameNetwork->setIcon(icon::get("edit-rename"));
56 ui.addNetwork->setIcon(icon::get("list-add"));
57 ui.deleteNetwork->setIcon(icon::get("edit-delete"));
58 ui.addServer->setIcon(icon::get("list-add"));
59 ui.deleteServer->setIcon(icon::get("edit-delete"));
60 ui.editServer->setIcon(icon::get("configure"));
61 ui.upServer->setIcon(icon::get("go-up"));
62 ui.downServer->setIcon(icon::get("go-down"));
63 ui.editIdentities->setIcon(icon::get("configure"));
65 connectedIcon = icon::get("network-connect");
66 connectingIcon = icon::get("network-wired"); // FIXME network-connecting
67 disconnectedIcon = icon::get("network-disconnect");
70 successIcon = icon::get({"emblem-success", "dialog-information"});
71 unavailableIcon = icon::get({"emblem-unavailable", "dialog-warning"});
72 questionIcon = icon::get({"emblem-question", "dialog-question", "dialog-information"});
74 foreach (int mib, QTextCodec::availableMibs()) {
75 QByteArray codec = QTextCodec::codecForMib(mib)->name();
76 ui.sendEncoding->addItem(codec);
77 ui.recvEncoding->addItem(codec);
78 ui.serverEncoding->addItem(codec);
80 ui.sendEncoding->model()->sort(0);
81 ui.recvEncoding->model()->sort(0);
82 ui.serverEncoding->model()->sort(0);
84 setEnabled(Client::isConnected()); // need a core connection!
87 connectToWidgetsChangedSignals({ui.identityList, ui.performEdit, ui.sasl,
88 ui.saslAccount, ui.saslPassword, ui.autoIdentify,
89 ui.autoIdentifyService, ui.autoIdentifyPassword, ui.useCustomEncodings,
90 ui.sendEncoding, ui.recvEncoding, ui.serverEncoding,
91 ui.autoReconnect, ui.reconnectInterval, ui.reconnectRetries,
92 ui.unlimitedRetries, ui.rejoinOnReconnect, ui.useCustomMessageRate,
93 ui.messageRateBurstSize, ui.messageRateDelay, ui.unlimitedMessageRate},
95 &NetworksSettingsPage::widgetHasChanged);
97 connect(Client::instance(), &Client::coreConnectionStateChanged, this, &NetworksSettingsPage::coreConnectionStateChanged);
98 connect(Client::instance(), &Client::networkCreated, this, &NetworksSettingsPage::clientNetworkAdded);
99 connect(Client::instance(), &Client::networkRemoved, this, &NetworksSettingsPage::clientNetworkRemoved);
100 connect(Client::instance(), &Client::identityCreated, this, &NetworksSettingsPage::clientIdentityAdded);
101 connect(Client::instance(), &Client::identityRemoved, this, &NetworksSettingsPage::clientIdentityRemoved);
103 foreach (IdentityId id, Client::identityIds()) {
104 clientIdentityAdded(id);
108 void NetworksSettingsPage::save()
112 saveToNetworkInfo(networkInfos[currentId]);
114 QList<NetworkInfo> toCreate, toUpdate;
115 QList<NetworkId> toRemove;
116 QHash<NetworkId, NetworkInfo>::iterator i = networkInfos.begin();
117 while (i != networkInfos.end()) {
118 NetworkId id = (*i).networkId;
121 // if(id == currentId) currentId = 0;
122 // QList<QListWidgetItem *> items = ui.networkList->findItems((*i).networkName, Qt::MatchExactly);
123 // if(items.count()) {
124 // Q_ASSERT(items[0]->data(Qt::UserRole).value<NetworkId>() == id);
127 // i = networkInfos.erase(i);
131 if ((*i) != Client::network((*i).networkId)->networkInfo()) {
137 foreach (NetworkId id, Client::networkIds()) {
138 if (!networkInfos.contains(id))
141 SaveNetworksDlg dlg(toCreate, toUpdate, toRemove, this);
142 int ret = dlg.exec();
143 if (ret == QDialog::Rejected) {
144 // canceled -> reload everything to be safe
147 setChangedState(false);
151 void NetworksSettingsPage::load()
155 // Handle UI dependent on core feature flags here
156 if (Client::isCoreFeatureEnabled(Quassel::Feature::CustomRateLimits)) {
157 // Custom rate limiting supported, allow toggling
158 ui.useCustomMessageRate->setEnabled(true);
159 // Reset tooltip to default.
160 ui.useCustomMessageRate->setToolTip(QString("%1").arg(tr("<p>Override default message rate limiting.</p>"
161 "<p><b>Setting limits too low may get you disconnected"
162 " from the server!</b></p>")));
163 // If changed, update the message below!
166 // Custom rate limiting not supported, disallow toggling
167 ui.useCustomMessageRate->setEnabled(false);
168 // Split up the message to allow re-using translations:
169 // [Original tool-tip]
170 // [Bold 'does not support feature' message]
171 // [Specific version needed and feature details]
172 ui.useCustomMessageRate->setToolTip(QString("%1<br/><b>%2</b><br/>%3")
173 .arg(tr("<p>Override default message rate limiting.</p>"
174 "<p><b>Setting limits too low may get you disconnected"
175 " from the server!</b></p>"),
176 tr("Your Quassel core does not support this feature"),
177 tr("You need a Quassel core v0.13.0 or newer in order to "
178 "modify message rate limits.")));
181 // Hide the SASL EXTERNAL notice until a network's shown. Stops it from showing while loading
182 // backlog from the core.
185 // Reset network capability status in case no valid networks get selected (a rare situation)
186 resetNetworkCapStates();
188 foreach (NetworkId netid, Client::networkIds()) {
189 clientNetworkAdded(netid);
191 ui.networkList->setCurrentRow(0);
193 setChangedState(false);
196 void NetworksSettingsPage::reset()
199 ui.networkList->clear();
200 networkInfos.clear();
203 bool NetworksSettingsPage::aboutToSave()
206 saveToNetworkInfo(networkInfos[currentId]);
208 foreach (NetworkInfo info, networkInfos.values()) {
209 if (!info.serverList.count())
214 QString error(tr("<b>The following problems need to be corrected before your changes can be applied:</b><ul>"));
215 if (errors.contains(1))
216 error += tr("<li>All networks need at least one server defined</li>");
217 error += tr("</ul>");
218 QMessageBox::warning(this, tr("Invalid Network Settings"), error);
222 void NetworksSettingsPage::widgetHasChanged()
224 if (_ignoreWidgetChanges)
226 bool changed = testHasChanged();
227 if (changed != hasChanged())
228 setChangedState(changed);
231 bool NetworksSettingsPage::testHasChanged()
233 if (currentId != 0) {
234 saveToNetworkInfo(networkInfos[currentId]);
236 if (Client::networkIds().count() != networkInfos.count())
238 foreach (NetworkId id, networkInfos.keys()) {
241 if (Client::network(id)->networkInfo() != networkInfos[id])
247 void NetworksSettingsPage::setWidgetStates()
250 if (ui.networkList->selectedItems().count()) {
251 ui.detailsBox->setEnabled(true);
252 ui.renameNetwork->setEnabled(true);
253 ui.deleteNetwork->setEnabled(true);
255 /* button disabled for now
256 NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
257 const Network *net = id > 0 ? Client::network(id) : 0;
258 ui.connectNow->setEnabled(net);
259 // && (Client::network(id)->connectionState() == Network::Initialized
260 // || Client::network(id)->connectionState() == Network::Disconnected));
262 if(net->connectionState() == Network::Disconnected) {
263 ui.connectNow->setIcon(connectedIcon);
264 ui.connectNow->setText(tr("Connect"));
266 ui.connectNow->setIcon(disconnectedIcon);
267 ui.connectNow->setText(tr("Disconnect"));
270 ui.connectNow->setIcon(QIcon());
271 ui.connectNow->setText(tr("Apply first!"));
275 ui.renameNetwork->setEnabled(false);
276 ui.deleteNetwork->setEnabled(false);
277 // ui.connectNow->setEnabled(false);
278 ui.detailsBox->setEnabled(false);
281 if (ui.serverList->selectedItems().count()) {
282 ui.editServer->setEnabled(true);
283 ui.deleteServer->setEnabled(true);
284 ui.upServer->setEnabled(ui.serverList->currentRow() > 0);
285 ui.downServer->setEnabled(ui.serverList->currentRow() < ui.serverList->count() - 1);
288 ui.editServer->setEnabled(false);
289 ui.deleteServer->setEnabled(false);
290 ui.upServer->setEnabled(false);
291 ui.downServer->setEnabled(false);
295 void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem* item)
297 if (!item && !(item = networkItem(id)))
299 const Network* net = Client::network(id);
300 if (!net || net->isInitialized())
301 item->setFlags(item->flags() | Qt::ItemIsEnabled);
303 item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
304 if (net && net->connectionState() == Network::Initialized) {
305 item->setIcon(connectedIcon);
307 else if (net && net->connectionState() != Network::Disconnected) {
308 item->setIcon(connectingIcon);
311 item->setIcon(disconnectedIcon);
315 // check if we already have another net of this name in the list, and replace it
316 QList<QListWidgetItem*> items = ui.networkList->findItems(net->networkName(), Qt::MatchExactly);
318 foreach (QListWidgetItem* i, items) {
319 NetworkId oldid = i->data(Qt::UserRole).value<NetworkId>();
321 continue; // only locally created nets should be replaced
322 if (oldid == currentId) {
325 ui.networkList->clearSelection();
327 int row = ui.networkList->row(i);
329 QListWidgetItem* olditem = ui.networkList->takeItem(row);
333 networkInfos.remove(oldid);
337 item->setText(net->networkName());
339 item->setSelected(true);
343 void NetworksSettingsPage::resetNetworkCapStates()
345 // Set the status to a blank (invalid) network ID, reseting all UI
346 setNetworkCapStates(NetworkId());
349 void NetworksSettingsPage::setNetworkCapStates(NetworkId id)
351 const Network* net = Client::network(id);
352 if (net && Client::isCoreFeatureEnabled(Quassel::Feature::CapNegotiation)) {
353 // Capability negotiation is supported, network exists.
354 // Check if the network is connected. Don't use net->isConnected() as that won't be true
355 // during capability negotiation when capabilities are added and removed.
356 if (net->connectionState() != Network::Disconnected) {
357 // Network exists and is connected, check available capabilities...
359 if (net->saslMaybeSupports(IrcCap::SaslMech::PLAIN)) {
360 setSASLStatus(CapSupportStatus::MaybeSupported);
363 setSASLStatus(CapSupportStatus::MaybeUnsupported);
366 // Add additional capability-dependent interface updates here
369 // Network is disconnected
371 setSASLStatus(CapSupportStatus::Disconnected);
373 // Add additional capability-dependent interface updates here
377 // Capability negotiation is not supported and/or network doesn't exist.
378 // Don't assume anything and reset all capability-dependent interface elements to neutral.
380 setSASLStatus(CapSupportStatus::Unknown);
382 // Add additional capability-dependent interface updates here
386 void NetworksSettingsPage::coreConnectionStateChanged(bool state)
388 this->setEnabled(state);
398 void NetworksSettingsPage::clientIdentityAdded(IdentityId id)
400 const Identity* identity = Client::identity(id);
401 connect(identity, &SyncableObject::updatedRemotely, this, &NetworksSettingsPage::clientIdentityUpdated);
403 QString name = identity->identityName();
404 for (int j = 0; j < ui.identityList->count(); j++) {
405 if ((j > 0 || ui.identityList->itemData(0).toInt() != 1) && name.localeAwareCompare(ui.identityList->itemText(j)) < 0) {
406 ui.identityList->insertItem(j, name, id.toInt());
412 ui.identityList->insertItem(ui.identityList->count(), name, id.toInt());
416 void NetworksSettingsPage::clientIdentityUpdated()
418 const auto* identity = qobject_cast<const Identity*>(sender());
420 qWarning() << "NetworksSettingsPage: Invalid identity to update!";
423 int row = ui.identityList->findData(identity->id().toInt());
425 qWarning() << "NetworksSettingsPage: Invalid identity to update!";
428 if (ui.identityList->itemText(row) != identity->identityName()) {
429 ui.identityList->setItemText(row, identity->identityName());
433 void NetworksSettingsPage::clientIdentityRemoved(IdentityId id)
435 IdentityId defaultId = defaultIdentity();
437 saveToNetworkInfo(networkInfos[currentId]);
438 foreach (NetworkInfo info, networkInfos.values()) {
439 if (info.identity == id) {
440 if (info.networkId == currentId)
441 ui.identityList->setCurrentIndex(0);
442 info.identity = defaultId;
443 networkInfos[info.networkId] = info;
444 if (info.networkId > 0)
445 Client::updateNetwork(info);
448 ui.identityList->removeItem(ui.identityList->findData(id.toInt()));
452 QListWidgetItem* NetworksSettingsPage::networkItem(NetworkId id) const
454 for (int i = 0; i < ui.networkList->count(); i++) {
455 QListWidgetItem* item = ui.networkList->item(i);
456 if (item->data(Qt::UserRole).value<NetworkId>() == id)
462 void NetworksSettingsPage::clientNetworkAdded(NetworkId id)
465 // connect(Client::network(id), &Network::updatedRemotely, this, &NetworksSettingsPage::clientNetworkUpdated);
466 connect(Client::network(id), &Network::configChanged, this, &NetworksSettingsPage::clientNetworkUpdated);
468 connect(Client::network(id), &Network::connectionStateSet, this, &NetworksSettingsPage::networkConnectionStateChanged);
469 connect(Client::network(id), &Network::connectionError, this, &NetworksSettingsPage::networkConnectionError);
471 // Handle capability changes in case a server dis/connects with the settings window open.
472 connect(Client::network(id), &Network::capAdded, this, &NetworksSettingsPage::clientNetworkCapsUpdated);
473 connect(Client::network(id), &Network::capRemoved, this, &NetworksSettingsPage::clientNetworkCapsUpdated);
476 void NetworksSettingsPage::clientNetworkUpdated()
478 const auto* net = qobject_cast<const Network*>(sender());
480 qWarning() << "Update request for unknown network received!";
483 networkInfos[net->networkId()] = net->networkInfo();
484 setItemState(net->networkId());
485 if (net->networkId() == currentId)
486 displayNetwork(net->networkId());
491 void NetworksSettingsPage::clientNetworkRemoved(NetworkId id)
493 if (!networkInfos.contains(id))
497 NetworkInfo info = networkInfos.take(id);
498 QList<QListWidgetItem*> items = ui.networkList->findItems(info.networkName, Qt::MatchExactly);
499 foreach (QListWidgetItem* item, items) {
500 if (item->data(Qt::UserRole).value<NetworkId>() == id)
501 delete ui.networkList->takeItem(ui.networkList->row(item));
507 void NetworksSettingsPage::networkConnectionStateChanged(Network::ConnectionState state)
510 const auto* net = qobject_cast<const Network*>(sender());
514 if(net->networkId() == currentId) {
515 ui.connectNow->setEnabled(state == Network::Initialized || state == Network::Disconnected);
518 setItemState(net->networkId());
519 if (net->networkId() == currentId) {
520 // Network is currently shown. Update the capability-dependent UI in case capabilities have
522 setNetworkCapStates(currentId);
527 void NetworksSettingsPage::networkConnectionError(const QString&) {}
529 QListWidgetItem* NetworksSettingsPage::insertNetwork(NetworkId id)
531 NetworkInfo info = Client::network(id)->networkInfo();
532 networkInfos[id] = info;
533 return insertNetwork(info);
536 QListWidgetItem* NetworksSettingsPage::insertNetwork(const NetworkInfo& info)
538 QListWidgetItem* item = nullptr;
539 QList<QListWidgetItem*> items = ui.networkList->findItems(info.networkName, Qt::MatchExactly);
541 item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
543 // we overwrite an existing net if it a) has the same name and b) has a negative ID meaning we created it locally before
544 // -> then we can be sure that this is the core-side replacement for the net we created
545 foreach (QListWidgetItem* i, items) {
546 NetworkId id = i->data(Qt::UserRole).value<NetworkId>();
553 item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
555 item->setData(Qt::UserRole, QVariant::fromValue(info.networkId));
556 setItemState(info.networkId, item);
561 // Called when selecting 'Configure' from the buffer list
562 void NetworksSettingsPage::bufferList_Open(NetworkId netId)
564 QListWidgetItem* item = networkItem(netId);
565 ui.networkList->setCurrentItem(item, QItemSelectionModel::SelectCurrent);
568 void NetworksSettingsPage::displayNetwork(NetworkId id)
570 _ignoreWidgetChanges = true;
572 NetworkInfo info = networkInfos[id];
574 // this is only needed when the core supports SASL EXTERNAL
575 if (Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal)) {
577 disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
580 _cid = new CertIdentity(*Client::identity(info.identity), this);
581 _cid->enableEditSsl(true);
582 connect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
585 ui.identityList->setCurrentIndex(ui.identityList->findData(info.identity.toInt()));
586 ui.serverList->clear();
587 foreach (Network::Server server, info.serverList) {
588 QListWidgetItem* item = new QListWidgetItem(QString("%1:%2").arg(server.host).arg(server.port));
590 item->setIcon(icon::get("document-encrypt"));
591 ui.serverList->addItem(item);
594 // ui.randomServer->setChecked(info.useRandomServer);
595 // Update the capability-dependent UI in case capabilities have changed.
596 setNetworkCapStates(id);
597 ui.performEdit->setPlainText(info.perform.join("\n"));
598 ui.autoIdentify->setChecked(info.useAutoIdentify);
599 ui.autoIdentifyService->setText(info.autoIdentifyService);
600 ui.autoIdentifyPassword->setText(info.autoIdentifyPassword);
601 ui.sasl->setChecked(info.useSasl);
602 ui.saslAccount->setText(info.saslAccount);
603 ui.saslPassword->setText(info.saslPassword);
604 if (info.codecForEncoding.isEmpty()) {
605 ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(Network::defaultCodecForEncoding()));
606 ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(Network::defaultCodecForDecoding()));
607 ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(Network::defaultCodecForServer()));
608 ui.useCustomEncodings->setChecked(false);
611 ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(info.codecForEncoding));
612 ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(info.codecForDecoding));
613 ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(info.codecForServer));
614 ui.useCustomEncodings->setChecked(true);
616 ui.autoReconnect->setChecked(info.useAutoReconnect);
617 ui.reconnectInterval->setValue(info.autoReconnectInterval);
618 ui.reconnectRetries->setValue(info.autoReconnectRetries);
619 ui.unlimitedRetries->setChecked(info.unlimitedReconnectRetries);
620 ui.rejoinOnReconnect->setChecked(info.rejoinChannels);
621 // Custom rate limiting
622 ui.unlimitedMessageRate->setChecked(info.unlimitedMessageRate);
623 // Set 'ui.useCustomMessageRate' after 'ui.unlimitedMessageRate' so if the latter is
624 // disabled, 'ui.messageRateDelayFrame' will remain disabled.
625 ui.useCustomMessageRate->setChecked(info.useCustomMessageRate);
626 ui.messageRateBurstSize->setValue(info.messageRateBurstSize);
627 // Convert milliseconds (integer) into seconds (double)
628 ui.messageRateDelay->setValue(info.messageRateDelay / 1000.0f);
631 // just clear widgets
633 disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
636 ui.identityList->setCurrentIndex(-1);
637 ui.serverList->clear();
638 ui.performEdit->clear();
639 ui.autoIdentifyService->clear();
640 ui.autoIdentifyPassword->clear();
641 ui.saslAccount->clear();
642 ui.saslPassword->clear();
645 _ignoreWidgetChanges = false;
649 void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo& info)
651 info.identity = ui.identityList->itemData(ui.identityList->currentIndex()).toInt();
652 // info.useRandomServer = ui.randomServer->isChecked();
653 info.perform = ui.performEdit->toPlainText().split("\n");
654 info.useAutoIdentify = ui.autoIdentify->isChecked();
655 info.autoIdentifyService = ui.autoIdentifyService->text();
656 info.autoIdentifyPassword = ui.autoIdentifyPassword->text();
657 info.useSasl = ui.sasl->isChecked();
658 info.saslAccount = ui.saslAccount->text();
659 info.saslPassword = ui.saslPassword->text();
660 if (!ui.useCustomEncodings->isChecked()) {
661 info.codecForEncoding.clear();
662 info.codecForDecoding.clear();
663 info.codecForServer.clear();
666 info.codecForEncoding = ui.sendEncoding->currentText().toLatin1();
667 info.codecForDecoding = ui.recvEncoding->currentText().toLatin1();
668 info.codecForServer = ui.serverEncoding->currentText().toLatin1();
670 info.useAutoReconnect = ui.autoReconnect->isChecked();
671 info.autoReconnectInterval = ui.reconnectInterval->value();
672 info.autoReconnectRetries = ui.reconnectRetries->value();
673 info.unlimitedReconnectRetries = ui.unlimitedRetries->isChecked();
674 info.rejoinChannels = ui.rejoinOnReconnect->isChecked();
675 // Custom rate limiting
676 info.useCustomMessageRate = ui.useCustomMessageRate->isChecked();
677 info.messageRateBurstSize = ui.messageRateBurstSize->value();
678 // Convert seconds (double) into milliseconds (integer)
679 info.messageRateDelay = static_cast<quint32>((ui.messageRateDelay->value() * 1000));
680 info.unlimitedMessageRate = ui.unlimitedMessageRate->isChecked();
683 void NetworksSettingsPage::clientNetworkCapsUpdated()
685 // Grab the updated network
686 const auto* net = qobject_cast<const Network*>(sender());
688 qWarning() << "Update request for unknown network received!";
691 if (net->networkId() == currentId) {
692 // Network is currently shown. Update the capability-dependent UI in case capabilities have
694 setNetworkCapStates(currentId);
698 void NetworksSettingsPage::setSASLStatus(const CapSupportStatus saslStatus)
700 if (_saslStatusSelected != saslStatus) {
701 // Update the cached copy of SASL status used with the Details dialog
702 _saslStatusSelected = saslStatus;
704 // Update the user interface
705 switch (saslStatus) {
706 case CapSupportStatus::Unknown:
707 // There's no capability negotiation or network doesn't exist. Don't assume
709 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Could not check if supported by network")));
710 ui.saslStatusIcon->setPixmap(questionIcon.pixmap(16));
712 case CapSupportStatus::Disconnected:
713 // Disconnected from network, no way to check.
714 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Cannot check if supported when disconnected")));
715 ui.saslStatusIcon->setPixmap(questionIcon.pixmap(16));
717 case CapSupportStatus::MaybeUnsupported:
718 // The network doesn't advertise support for SASL PLAIN. Here be dragons.
719 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Not currently supported by network")));
720 ui.saslStatusIcon->setPixmap(unavailableIcon.pixmap(16));
722 case CapSupportStatus::MaybeSupported:
723 // The network advertises support for SASL PLAIN. Encourage using it!
724 // Unfortunately we don't know for sure if it's desired or functional.
725 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Supported by network")));
726 ui.saslStatusIcon->setPixmap(successIcon.pixmap(16));
732 void NetworksSettingsPage::sslUpdated()
734 if (_cid && !_cid->sslKey().isNull()) {
735 ui.saslContents->setDisabled(true);
736 ui.saslExtInfo->setHidden(false);
739 ui.saslContents->setDisabled(false);
740 // Directly re-enabling causes the widgets to ignore the parent "Use SASL Authentication"
741 // state to indicate whether or not it's disabled. To workaround this, keep track of
742 // whether or not "Use SASL Authentication" is enabled, then quickly uncheck/recheck the
744 if (!ui.sasl->isChecked()) {
745 // SASL is not enabled, uncheck/recheck the group box to re-disable saslContents.
746 // Leaving saslContents disabled doesn't work as that prevents it from re-enabling if
747 // sasl is later checked.
748 ui.sasl->setChecked(true);
749 ui.sasl->setChecked(false);
751 ui.saslExtInfo->setHidden(true);
755 /*** Network list ***/
757 void NetworksSettingsPage::on_networkList_itemSelectionChanged()
759 if (currentId != 0) {
760 saveToNetworkInfo(networkInfos[currentId]);
762 if (ui.networkList->selectedItems().count()) {
763 NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
766 ui.serverList->setCurrentRow(0);
774 void NetworksSettingsPage::on_addNetwork_clicked()
776 QStringList existing;
777 for (int i = 0; i < ui.networkList->count(); i++)
778 existing << ui.networkList->item(i)->text();
779 NetworkAddDlg dlg(existing, this);
780 if (dlg.exec() == QDialog::Accepted) {
781 NetworkInfo info = dlg.networkInfo();
782 if (info.networkName.isEmpty())
783 return; // sanity check
786 for (id = 1; id <= networkInfos.count(); id++) {
788 if (!networkInfos.keys().contains(-id.toInt()))
793 info.identity = defaultIdentity();
794 networkInfos[id] = info;
795 QListWidgetItem* item = insertNetwork(info);
796 ui.networkList->setCurrentItem(item);
801 void NetworksSettingsPage::on_deleteNetwork_clicked()
803 if (ui.networkList->selectedItems().count()) {
804 NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
806 = QMessageBox::question(this,
807 tr("Delete Network?"),
808 tr("Do you really want to delete the network \"%1\" and all related settings, including the backlog?")
809 .arg(networkInfos[netid].networkName),
810 QMessageBox::Yes | QMessageBox::No,
812 if (ret == QMessageBox::Yes) {
814 networkInfos.remove(netid);
815 delete ui.networkList->takeItem(ui.networkList->row(ui.networkList->selectedItems()[0]));
816 ui.networkList->setCurrentRow(qMin(ui.networkList->currentRow() + 1, ui.networkList->count() - 1));
823 void NetworksSettingsPage::on_renameNetwork_clicked()
825 if (!ui.networkList->selectedItems().count())
827 QString old = ui.networkList->selectedItems()[0]->text();
828 QStringList existing;
829 for (int i = 0; i < ui.networkList->count(); i++)
830 existing << ui.networkList->item(i)->text();
831 NetworkEditDlg dlg(old, existing, this);
832 if (dlg.exec() == QDialog::Accepted) {
833 ui.networkList->selectedItems()[0]->setText(dlg.networkName());
834 NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
835 networkInfos[netid].networkName = dlg.networkName();
841 void NetworksSettingsPage::on_connectNow_clicked() {
842 if(!ui.networkList->selectedItems().count()) return;
843 NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
844 const Network *net = Client::network(id);
846 if(net->connectionState() == Network::Disconnected) net->requestConnect();
847 else net->requestDisconnect();
851 /*** Server list ***/
853 void NetworksSettingsPage::on_serverList_itemSelectionChanged()
858 void NetworksSettingsPage::on_addServer_clicked()
862 ServerEditDlg dlg(Network::Server(), this);
863 if (dlg.exec() == QDialog::Accepted) {
864 networkInfos[currentId].serverList.append(dlg.serverData());
865 displayNetwork(currentId);
866 ui.serverList->setCurrentRow(ui.serverList->count() - 1);
871 void NetworksSettingsPage::on_editServer_clicked()
875 int cur = ui.serverList->currentRow();
876 ServerEditDlg dlg(networkInfos[currentId].serverList[cur], this);
877 if (dlg.exec() == QDialog::Accepted) {
878 networkInfos[currentId].serverList[cur] = dlg.serverData();
879 displayNetwork(currentId);
880 ui.serverList->setCurrentRow(cur);
885 void NetworksSettingsPage::on_deleteServer_clicked()
889 int cur = ui.serverList->currentRow();
890 networkInfos[currentId].serverList.removeAt(cur);
891 displayNetwork(currentId);
892 ui.serverList->setCurrentRow(qMin(cur, ui.serverList->count() - 1));
896 void NetworksSettingsPage::on_upServer_clicked()
898 int cur = ui.serverList->currentRow();
899 Network::Server server = networkInfos[currentId].serverList.takeAt(cur);
900 networkInfos[currentId].serverList.insert(cur - 1, server);
901 displayNetwork(currentId);
902 ui.serverList->setCurrentRow(cur - 1);
906 void NetworksSettingsPage::on_downServer_clicked()
908 int cur = ui.serverList->currentRow();
909 Network::Server server = networkInfos[currentId].serverList.takeAt(cur);
910 networkInfos[currentId].serverList.insert(cur + 1, server);
911 displayNetwork(currentId);
912 ui.serverList->setCurrentRow(cur + 1);
916 void NetworksSettingsPage::on_editIdentities_clicked()
918 SettingsPageDlg dlg(new IdentitiesSettingsPage(this), this);
922 void NetworksSettingsPage::on_saslStatusDetails_clicked()
924 if (ui.networkList->selectedItems().count()) {
925 NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
926 QString& netName = networkInfos[netid].networkName;
928 // If these strings are visible, one of the status messages wasn't detected below.
929 QString saslStatusHeader = "[header unintentionally left blank]";
930 QString saslStatusExplanation = "[explanation unintentionally left blank]";
932 // If true, show a warning icon instead of an information icon
933 bool useWarningIcon = false;
935 // Determine which explanation to show
936 switch (_saslStatusSelected) {
937 case CapSupportStatus::Unknown:
938 saslStatusHeader = tr("Could not check if SASL supported by network");
939 saslStatusExplanation = tr("Quassel could not check if \"%1\" supports SASL. This may "
940 "be due to unsaved changes or an older Quassel core. You "
941 "can still try using SASL.")
944 case CapSupportStatus::Disconnected:
945 saslStatusHeader = tr("Cannot check if SASL supported when disconnected");
946 saslStatusExplanation = tr("Quassel cannot check if \"%1\" supports SASL when "
947 "disconnected. Connect to the network, or try using SASL "
951 case CapSupportStatus::MaybeUnsupported:
952 saslStatusHeader = tr("SASL not currently supported by network");
953 saslStatusExplanation = tr("The network \"%1\" does not currently support SASL. "
954 "However, support might be added later on.")
956 useWarningIcon = true;
958 case CapSupportStatus::MaybeSupported:
959 saslStatusHeader = tr("SASL supported by network");
960 saslStatusExplanation = tr("The network \"%1\" supports SASL. In most cases, you "
961 "should use SASL instead of NickServ identification.")
966 // Process this in advance for reusability below
967 const QString saslStatusMsgTitle = tr("SASL support for \"%1\"").arg(netName);
968 const QString saslStatusMsgText = QString("<p><b>%1</b></p></br><p>%2</p></br><p><i>%3</i></p>")
969 .arg(saslStatusHeader,
970 saslStatusExplanation,
971 tr("SASL is a standardized way to log in and identify yourself to "
974 if (useWarningIcon) {
975 // Show as a warning dialog box
976 QMessageBox::warning(this, saslStatusMsgTitle, saslStatusMsgText);
979 // Show as an information dialog box
980 QMessageBox::information(this, saslStatusMsgTitle, saslStatusMsgText);
985 IdentityId NetworksSettingsPage::defaultIdentity() const
987 IdentityId defaultId = 0;
988 QList<IdentityId> ids = Client::identityIds();
989 foreach (IdentityId id, ids) {
990 if (defaultId == 0 || id < defaultId)
996 /**************************************************************************
998 *************************************************************************/
1000 NetworkAddDlg::NetworkAddDlg(QStringList exist, QWidget* parent)
1002 , existing(std::move(exist))
1005 ui.useSSL->setIcon(icon::get("document-encrypt"));
1007 // Whenever useSSL is toggled, update the port number if not changed from the default
1008 connect(ui.useSSL, &QAbstractButton::toggled, this, &NetworkAddDlg::updateSslPort);
1009 // Do NOT call updateSslPort when loading settings, otherwise port settings may be overriden.
1010 // If useSSL is later changed to be checked by default, change port's default value, too.
1012 if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) {
1013 // Synchronize requiring SSL with the use SSL checkbox
1014 ui.sslVerify->setEnabled(ui.useSSL->isChecked());
1015 connect(ui.useSSL, &QAbstractButton::toggled, ui.sslVerify, &QWidget::setEnabled);
1018 // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck
1019 ui.sslVerify->setEnabled(false);
1020 ui.sslVerify->setChecked(false);
1021 // Split up the message to allow re-using translations:
1022 // [Original tool-tip]
1023 // [Bold 'does not support feature' message]
1024 // [Specific version needed and feature details]
1025 ui.sslVerify->setToolTip(QString("%1<br/><b>%2</b><br/>%3")
1026 .arg(ui.sslVerify->toolTip(),
1027 tr("Your Quassel core does not support this feature"),
1028 tr("You need a Quassel core v0.13.0 or newer in order to "
1029 "verify connection security.")));
1032 // read preset networks
1033 QStringList networks = PresetNetworks::names();
1034 foreach (QString s, existing)
1035 networks.removeAll(s);
1036 if (networks.count())
1037 ui.presetList->addItems(networks);
1039 ui.useManual->setChecked(true);
1040 ui.usePreset->setEnabled(false);
1042 connect(ui.networkName, &QLineEdit::textChanged, this, &NetworkAddDlg::setButtonStates);
1043 connect(ui.serverAddress, &QLineEdit::textChanged, this, &NetworkAddDlg::setButtonStates);
1044 connect(ui.usePreset, &QRadioButton::toggled, this, &NetworkAddDlg::setButtonStates);
1045 connect(ui.useManual, &QRadioButton::toggled, this, &NetworkAddDlg::setButtonStates);
1049 NetworkInfo NetworkAddDlg::networkInfo() const
1051 if (ui.useManual->isChecked()) {
1053 info.networkName = ui.networkName->text().trimmed();
1054 info.serverList << Network::Server(ui.serverAddress->text().trimmed(),
1056 ui.serverPassword->text(),
1057 ui.useSSL->isChecked(),
1058 ui.sslVerify->isChecked());
1062 return PresetNetworks::networkInfo(ui.presetList->currentText());
1065 void NetworkAddDlg::setButtonStates()
1068 if (ui.usePreset->isChecked() && ui.presetList->count())
1070 else if (ui.useManual->isChecked()) {
1071 ok = !ui.networkName->text().trimmed().isEmpty() && !existing.contains(ui.networkName->text().trimmed())
1072 && !ui.serverAddress->text().isEmpty();
1074 ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
1077 void NetworkAddDlg::updateSslPort(bool isChecked)
1079 // "Use encrypted connection" was toggled, check the state...
1080 if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) {
1081 // Had been using the plain-text port, use the SSL default
1082 ui.port->setValue(Network::PORT_SSL);
1084 else if (!isChecked && ui.port->value() == Network::PORT_SSL) {
1085 // Had been using the SSL port, use the plain-text default
1086 ui.port->setValue(Network::PORT_PLAINTEXT);
1090 /**************************************************************************
1092 *************************************************************************/
1094 NetworkEditDlg::NetworkEditDlg(const QString& old, QStringList exist, QWidget* parent)
1096 , existing(std::move(exist))
1100 if (old.isEmpty()) {
1102 setWindowTitle(tr("Add Network"));
1103 on_networkEdit_textChanged(""); // disable ok button
1106 ui.networkEdit->setText(old);
1109 QString NetworkEditDlg::networkName() const
1111 return ui.networkEdit->text().trimmed();
1114 void NetworkEditDlg::on_networkEdit_textChanged(const QString& text)
1116 ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty() || existing.contains(text.trimmed()));
1119 /**************************************************************************
1121 *************************************************************************/
1122 ServerEditDlg::ServerEditDlg(const Network::Server& server, QWidget* parent)
1126 ui.useSSL->setIcon(icon::get("document-encrypt"));
1127 ui.host->setText(server.host);
1128 ui.host->setFocus();
1129 ui.port->setValue(server.port);
1130 ui.password->setText(server.password);
1131 ui.useSSL->setChecked(server.useSsl);
1132 ui.sslVerify->setChecked(server.sslVerify);
1133 ui.sslVersion->setCurrentIndex(server.sslVersion);
1134 ui.useProxy->setChecked(server.useProxy);
1135 ui.proxyType->setCurrentIndex(server.proxyType == QNetworkProxy::Socks5Proxy ? 0 : 1);
1136 ui.proxyHost->setText(server.proxyHost);
1137 ui.proxyPort->setValue(server.proxyPort);
1138 ui.proxyUsername->setText(server.proxyUser);
1139 ui.proxyPassword->setText(server.proxyPass);
1141 // This is a dirty hack to display the core->IRC SSL protocol dropdown
1142 // only if the core won't use autonegotiation to determine the best
1143 // protocol. When autonegotiation was introduced, it would have been
1144 // a good idea to use the CoreFeatures enum to accomplish this.
1145 // However, since multiple versions have been released since then, that
1146 // is no longer possible. Instead, we rely on the fact that the
1147 // Datastream protocol was introduced in the same version (0.10) as SSL
1148 // autonegotiation. Because of that, we can display the dropdown only
1149 // if the Legacy protocol is in use. If any other RemotePeer protocol
1150 // is in use, that means a newer protocol is in use and therefore the
1151 // core will use autonegotiation.
1152 if (Client::coreConnection()->peer()->protocol() != Protocol::LegacyProtocol) {
1154 ui.sslVersion->hide();
1157 // Whenever useSSL is toggled, update the port number if not changed from the default
1158 connect(ui.useSSL, &QAbstractButton::toggled, this, &ServerEditDlg::updateSslPort);
1159 // Do NOT call updateSslPort when loading settings, otherwise port settings may be overriden.
1160 // If useSSL is later changed to be checked by default, change port's default value, too.
1162 if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) {
1163 // Synchronize requiring SSL with the use SSL checkbox
1164 ui.sslVerify->setEnabled(ui.useSSL->isChecked());
1165 connect(ui.useSSL, &QAbstractButton::toggled, ui.sslVerify, &QWidget::setEnabled);
1168 // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck
1169 ui.sslVerify->setEnabled(false);
1170 ui.sslVerify->setChecked(false);
1171 // Split up the message to allow re-using translations:
1172 // [Original tool-tip]
1173 // [Bold 'does not support feature' message]
1174 // [Specific version needed and feature details]
1175 ui.sslVerify->setToolTip(QString("%1<br/><b>%2</b><br/>%3")
1176 .arg(ui.sslVerify->toolTip(),
1177 tr("Your Quassel core does not support this feature"),
1178 tr("You need a Quassel core v0.13.0 or newer in order to "
1179 "verify connection security.")));
1182 on_host_textChanged();
1185 Network::Server ServerEditDlg::serverData() const
1187 Network::Server server(ui.host->text().trimmed(), ui.port->value(), ui.password->text(), ui.useSSL->isChecked(), ui.sslVerify->isChecked());
1188 server.sslVersion = ui.sslVersion->currentIndex();
1189 server.useProxy = ui.useProxy->isChecked();
1190 server.proxyType = ui.proxyType->currentIndex() == 0 ? QNetworkProxy::Socks5Proxy : QNetworkProxy::HttpProxy;
1191 server.proxyHost = ui.proxyHost->text();
1192 server.proxyPort = ui.proxyPort->value();
1193 server.proxyUser = ui.proxyUsername->text();
1194 server.proxyPass = ui.proxyPassword->text();
1198 void ServerEditDlg::on_host_textChanged()
1200 ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(ui.host->text().trimmed().isEmpty());
1203 void ServerEditDlg::updateSslPort(bool isChecked)
1205 // "Use encrypted connection" was toggled, check the state...
1206 if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) {
1207 // Had been using the plain-text port, use the SSL default
1208 ui.port->setValue(Network::PORT_SSL);
1210 else if (!isChecked && ui.port->value() == Network::PORT_SSL) {
1211 // Had been using the SSL port, use the plain-text default
1212 ui.port->setValue(Network::PORT_PLAINTEXT);
1216 /**************************************************************************
1218 *************************************************************************/
1220 SaveNetworksDlg::SaveNetworksDlg(const QList<NetworkInfo>& toCreate,
1221 const QList<NetworkInfo>& toUpdate,
1222 const QList<NetworkId>& toRemove,
1228 numevents = toCreate.count() + toUpdate.count() + toRemove.count();
1231 ui.progressBar->setMaximum(numevents);
1232 ui.progressBar->setValue(0);
1234 connect(Client::instance(), &Client::networkCreated, this, &SaveNetworksDlg::clientEvent);
1235 connect(Client::instance(), &Client::networkRemoved, this, &SaveNetworksDlg::clientEvent);
1237 foreach (NetworkId id, toRemove) {
1238 Client::removeNetwork(id);
1240 foreach (NetworkInfo info, toCreate) {
1241 Client::createNetwork(info);
1243 foreach (NetworkInfo info, toUpdate) {
1244 const Network* net = Client::network(info.networkId);
1246 qWarning() << "Invalid client network!";
1250 // FIXME this only checks for one changed item rather than all!
1251 connect(net, &SyncableObject::updatedRemotely, this, &SaveNetworksDlg::clientEvent);
1252 Client::updateNetwork(info);
1256 qWarning() << "Sync dialog called without stuff to change!";
1261 void SaveNetworksDlg::clientEvent()
1263 ui.progressBar->setValue(++rcvevents);
1264 if (rcvevents >= numevents)