modernize: Replace most remaining old-style connects by PMF ones
[quassel.git] / src / qtui / settingspages / networkssettingspage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include <QHeaderView>
22 #include <QMessageBox>
23 #include <QTextCodec>
24 #include <utility>
25
26 #include "networkssettingspage.h"
27
28 #include "client.h"
29 #include "icon.h"
30 #include "identity.h"
31 #include "network.h"
32 #include "presetnetworks.h"
33 #include "settingspagedlg.h"
34 #include "util.h"
35
36 // IRCv3 capabilities
37 #include "irccap.h"
38
39 #include "settingspages/identitiessettingspage.h"
40
41 NetworksSettingsPage::NetworksSettingsPage(QWidget *parent)
42     : SettingsPage(tr("IRC"), tr("Networks"), parent)
43 {
44     ui.setupUi(this);
45
46     // hide SASL options for older cores
47     if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslAuthentication))
48         ui.sasl->hide();
49     if (!Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal))
50         ui.saslExtInfo->hide();
51 #ifndef HAVE_SSL
52     ui.saslExtInfo->hide();
53 #endif
54
55     // set up icons
56     ui.renameNetwork->setIcon(icon::get("edit-rename"));
57     ui.addNetwork->setIcon(icon::get("list-add"));
58     ui.deleteNetwork->setIcon(icon::get("edit-delete"));
59     ui.addServer->setIcon(icon::get("list-add"));
60     ui.deleteServer->setIcon(icon::get("edit-delete"));
61     ui.editServer->setIcon(icon::get("configure"));
62     ui.upServer->setIcon(icon::get("go-up"));
63     ui.downServer->setIcon(icon::get("go-down"));
64     ui.editIdentities->setIcon(icon::get("configure"));
65
66     _ignoreWidgetChanges = false;
67
68     connectedIcon = icon::get("network-connect");
69     connectingIcon = icon::get("network-wired"); // FIXME network-connecting
70     disconnectedIcon = icon::get("network-disconnect");
71
72     // Status icons
73     infoIcon = icon::get("dialog-information");
74     warningIcon = icon::get("dialog-warning");
75
76     foreach(int mib, QTextCodec::availableMibs()) {
77         QByteArray codec = QTextCodec::codecForMib(mib)->name();
78         ui.sendEncoding->addItem(codec);
79         ui.recvEncoding->addItem(codec);
80         ui.serverEncoding->addItem(codec);
81     }
82     ui.sendEncoding->model()->sort(0);
83     ui.recvEncoding->model()->sort(0);
84     ui.serverEncoding->model()->sort(0);
85     currentId = 0;
86     setEnabled(Client::isConnected()); // need a core connection!
87     setWidgetStates();
88     connect(Client::instance(), &Client::coreConnectionStateChanged, this, &NetworksSettingsPage::coreConnectionStateChanged);
89     connect(Client::instance(), &Client::networkCreated, this, &NetworksSettingsPage::clientNetworkAdded);
90     connect(Client::instance(), &Client::networkRemoved, this, &NetworksSettingsPage::clientNetworkRemoved);
91     connect(Client::instance(), &Client::identityCreated, this, &NetworksSettingsPage::clientIdentityAdded);
92     connect(Client::instance(), &Client::identityRemoved, this, &NetworksSettingsPage::clientIdentityRemoved);
93
94     connect(ui.identityList, selectOverload<int>(&QComboBox::currentIndexChanged), this, &NetworksSettingsPage::widgetHasChanged);
95     //connect(ui.randomServer, &QAbstractButton::clicked, this, &NetworksSettingsPage::widgetHasChanged);
96     connect(ui.performEdit, &QTextEdit::textChanged, this, &NetworksSettingsPage::widgetHasChanged);
97     connect(ui.sasl, &QGroupBox::clicked, this, &NetworksSettingsPage::widgetHasChanged);
98     connect(ui.saslAccount, &QLineEdit::textEdited, this, &NetworksSettingsPage::widgetHasChanged);
99     connect(ui.saslPassword, &QLineEdit::textEdited, this, &NetworksSettingsPage::widgetHasChanged);
100     connect(ui.autoIdentify, &QGroupBox::clicked, this, &NetworksSettingsPage::widgetHasChanged);
101     connect(ui.autoIdentifyService, &QLineEdit::textEdited, this, &NetworksSettingsPage::widgetHasChanged);
102     connect(ui.autoIdentifyPassword, &QLineEdit::textEdited, this, &NetworksSettingsPage::widgetHasChanged);
103     connect(ui.useCustomEncodings, &QGroupBox::clicked, this, &NetworksSettingsPage::widgetHasChanged);
104     connect(ui.sendEncoding, selectOverload<int>(&QComboBox::currentIndexChanged), this, &NetworksSettingsPage::widgetHasChanged);
105     connect(ui.recvEncoding, selectOverload<int>(&QComboBox::currentIndexChanged), this, &NetworksSettingsPage::widgetHasChanged);
106     connect(ui.serverEncoding, selectOverload<int>(&QComboBox::currentIndexChanged), this, &NetworksSettingsPage::widgetHasChanged);
107     connect(ui.autoReconnect, &QGroupBox::clicked, this, &NetworksSettingsPage::widgetHasChanged);
108     connect(ui.reconnectInterval, selectOverload<int>(&QSpinBox::valueChanged), this, &NetworksSettingsPage::widgetHasChanged);
109     connect(ui.reconnectRetries, selectOverload<int>(&QSpinBox::valueChanged), this, &NetworksSettingsPage::widgetHasChanged);
110     connect(ui.unlimitedRetries, &QAbstractButton::clicked, this, &NetworksSettingsPage::widgetHasChanged);
111     connect(ui.rejoinOnReconnect, &QAbstractButton::clicked, this, &NetworksSettingsPage::widgetHasChanged);
112
113     // Core features can change during a reconnect.  Always connect these here, delaying testing for
114     // the core feature flag in load().
115     connect(ui.useCustomMessageRate, &QGroupBox::clicked, this, &NetworksSettingsPage::widgetHasChanged);
116     connect(ui.messageRateBurstSize, selectOverload<int>(&QSpinBox::valueChanged), this, &NetworksSettingsPage::widgetHasChanged);
117     connect(ui.messageRateDelay, selectOverload<double>(&QDoubleSpinBox::valueChanged), this, &NetworksSettingsPage::widgetHasChanged);
118     connect(ui.unlimitedMessageRate, &QAbstractButton::clicked, this, &NetworksSettingsPage::widgetHasChanged);
119
120     foreach(IdentityId id, Client::identityIds()) {
121         clientIdentityAdded(id);
122     }
123 }
124
125
126 void NetworksSettingsPage::save()
127 {
128     setEnabled(false);
129     if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]);
130
131     QList<NetworkInfo> toCreate, toUpdate;
132     QList<NetworkId> toRemove;
133     QHash<NetworkId, NetworkInfo>::iterator i = networkInfos.begin();
134     while (i != networkInfos.end()) {
135         NetworkId id = (*i).networkId;
136         if (id < 0) {
137             toCreate.append(*i);
138             //if(id == currentId) currentId = 0;
139             //QList<QListWidgetItem *> items = ui.networkList->findItems((*i).networkName, Qt::MatchExactly);
140             //if(items.count()) {
141             //  Q_ASSERT(items[0]->data(Qt::UserRole).value<NetworkId>() == id);
142             //  delete items[0];
143             //}
144             //i = networkInfos.erase(i);
145             ++i;
146         }
147         else {
148             if ((*i) != Client::network((*i).networkId)->networkInfo()) {
149                 toUpdate.append(*i);
150             }
151             ++i;
152         }
153     }
154     foreach(NetworkId id, Client::networkIds()) {
155         if (!networkInfos.contains(id)) toRemove.append(id);
156     }
157     SaveNetworksDlg dlg(toCreate, toUpdate, toRemove, this);
158     int ret = dlg.exec();
159     if (ret == QDialog::Rejected) {
160         // canceled -> reload everything to be safe
161         load();
162     }
163     setChangedState(false);
164     setEnabled(true);
165 }
166
167
168 void NetworksSettingsPage::load()
169 {
170     reset();
171
172     // Handle UI dependent on core feature flags here
173     if (Client::isCoreFeatureEnabled(Quassel::Feature::CustomRateLimits)) {
174         // Custom rate limiting supported, allow toggling
175         ui.useCustomMessageRate->setEnabled(true);
176         // Reset tooltip to default.
177         ui.useCustomMessageRate->setToolTip(QString("%1").arg(
178                                           tr("<p>Override default message rate limiting.</p>"
179                                              "<p><b>Setting limits too low may get you disconnected"
180                                              " from the server!</b></p>")));
181         // If changed, update the message below!
182     } else {
183         // Custom rate limiting not supported, disallow toggling
184         ui.useCustomMessageRate->setEnabled(false);
185         // Split up the message to allow re-using translations:
186         // [Original tool-tip]
187         // [Bold 'does not support feature' message]
188         // [Specific version needed and feature details]
189         ui.useCustomMessageRate->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
190                                           tr("<p>Override default message rate limiting.</p>"
191                                              "<p><b>Setting limits too low may get you disconnected"
192                                              " from the server!</b></p>"),
193                                           tr("Your Quassel core does not support this feature"),
194                                           tr("You need a Quassel core v0.13.0 or newer in order to "
195                                           "modify message rate limits.")));
196     }
197
198 #ifdef HAVE_SSL
199     // Hide the SASL EXTERNAL notice until a network's shown.  Stops it from showing while loading
200     // backlog from the core.
201     sslUpdated();
202 #endif
203
204     // Reset network capability status in case no valid networks get selected (a rare situation)
205     resetNetworkCapStates();
206
207     foreach(NetworkId netid, Client::networkIds()) {
208         clientNetworkAdded(netid);
209     }
210     ui.networkList->setCurrentRow(0);
211
212     setChangedState(false);
213 }
214
215
216 void NetworksSettingsPage::reset()
217 {
218     currentId = 0;
219     ui.networkList->clear();
220     networkInfos.clear();
221 }
222
223
224 bool NetworksSettingsPage::aboutToSave()
225 {
226     if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]);
227     QList<int> errors;
228     foreach(NetworkInfo info, networkInfos.values()) {
229         if (!info.serverList.count()) errors.append(1);
230     }
231     if (!errors.count()) return true;
232     QString error(tr("<b>The following problems need to be corrected before your changes can be applied:</b><ul>"));
233     if (errors.contains(1)) error += tr("<li>All networks need at least one server defined</li>");
234     error += tr("</ul>");
235     QMessageBox::warning(this, tr("Invalid Network Settings"), error);
236     return false;
237 }
238
239
240 void NetworksSettingsPage::widgetHasChanged()
241 {
242     if (_ignoreWidgetChanges) return;
243     bool changed = testHasChanged();
244     if (changed != hasChanged()) setChangedState(changed);
245 }
246
247
248 bool NetworksSettingsPage::testHasChanged()
249 {
250     if (currentId != 0) {
251         saveToNetworkInfo(networkInfos[currentId]);
252     }
253     if (Client::networkIds().count() != networkInfos.count()) return true;
254     foreach(NetworkId id, networkInfos.keys()) {
255         if (id < 0) return true;
256         if (Client::network(id)->networkInfo() != networkInfos[id]) return true;
257     }
258     return false;
259 }
260
261
262 void NetworksSettingsPage::setWidgetStates()
263 {
264     // network list
265     if (ui.networkList->selectedItems().count()) {
266         ui.detailsBox->setEnabled(true);
267         ui.renameNetwork->setEnabled(true);
268         ui.deleteNetwork->setEnabled(true);
269
270         /* button disabled for now
271         NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
272         const Network *net = id > 0 ? Client::network(id) : 0;
273         ui.connectNow->setEnabled(net);
274         //    && (Client::network(id)->connectionState() == Network::Initialized
275         //    || Client::network(id)->connectionState() == Network::Disconnected));
276         if(net) {
277           if(net->connectionState() == Network::Disconnected) {
278             ui.connectNow->setIcon(connectedIcon);
279             ui.connectNow->setText(tr("Connect"));
280           } else {
281             ui.connectNow->setIcon(disconnectedIcon);
282             ui.connectNow->setText(tr("Disconnect"));
283           }
284         } else {
285           ui.connectNow->setIcon(QIcon());
286           ui.connectNow->setText(tr("Apply first!"));
287         } */
288     }
289     else {
290         ui.renameNetwork->setEnabled(false);
291         ui.deleteNetwork->setEnabled(false);
292         //ui.connectNow->setEnabled(false);
293         ui.detailsBox->setEnabled(false);
294     }
295     // network details
296     if (ui.serverList->selectedItems().count()) {
297         ui.editServer->setEnabled(true);
298         ui.deleteServer->setEnabled(true);
299         ui.upServer->setEnabled(ui.serverList->currentRow() > 0);
300         ui.downServer->setEnabled(ui.serverList->currentRow() < ui.serverList->count() - 1);
301     }
302     else {
303         ui.editServer->setEnabled(false);
304         ui.deleteServer->setEnabled(false);
305         ui.upServer->setEnabled(false);
306         ui.downServer->setEnabled(false);
307     }
308 }
309
310
311 void NetworksSettingsPage::setItemState(NetworkId id, QListWidgetItem *item)
312 {
313     if (!item && !(item = networkItem(id))) return;
314     const Network *net = Client::network(id);
315     if (!net || net->isInitialized()) item->setFlags(item->flags() | Qt::ItemIsEnabled);
316     else item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
317     if (net && net->connectionState() == Network::Initialized) {
318         item->setIcon(connectedIcon);
319     }
320     else if (net && net->connectionState() != Network::Disconnected) {
321         item->setIcon(connectingIcon);
322     }
323     else {
324         item->setIcon(disconnectedIcon);
325     }
326     if (net) {
327         bool select = false;
328         // check if we already have another net of this name in the list, and replace it
329         QList<QListWidgetItem *> items = ui.networkList->findItems(net->networkName(), Qt::MatchExactly);
330         if (items.count()) {
331             foreach(QListWidgetItem *i, items) {
332                 NetworkId oldid = i->data(Qt::UserRole).value<NetworkId>();
333                 if (oldid > 0) continue;  // only locally created nets should be replaced
334                 if (oldid == currentId) {
335                     select = true;
336                     currentId = 0;
337                     ui.networkList->clearSelection();
338                 }
339                 int row = ui.networkList->row(i);
340                 if (row >= 0) {
341                     QListWidgetItem *olditem = ui.networkList->takeItem(row);
342                     Q_ASSERT(olditem);
343                     delete olditem;
344                 }
345                 networkInfos.remove(oldid);
346                 break;
347             }
348         }
349         item->setText(net->networkName());
350         if (select) item->setSelected(true);
351     }
352 }
353
354
355 void NetworksSettingsPage::resetNetworkCapStates()
356 {
357     // Set the status to a blank (invalid) network ID, reseting all UI
358     setNetworkCapStates(NetworkId());
359 }
360
361
362 void NetworksSettingsPage::setNetworkCapStates(NetworkId id)
363 {
364     const Network *net = Client::network(id);
365     if (net && Client::isCoreFeatureEnabled(Quassel::Feature::CapNegotiation)) {
366         // Capability negotiation is supported, network exists.
367         // Check if the network is connected.  Don't use net->isConnected() as that won't be true
368         // during capability negotiation when capabilities are added and removed.
369         if (net->connectionState() != Network::Disconnected) {
370             // Network exists and is connected, check available capabilities...
371             // [SASL]
372             if (net->saslMaybeSupports(IrcCap::SaslMech::PLAIN)) {
373                 setSASLStatus(CapSupportStatus::MaybeSupported);
374             } else {
375                 setSASLStatus(CapSupportStatus::MaybeUnsupported);
376             }
377
378             // Add additional capability-dependent interface updates here
379         } else {
380             // Network is disconnected
381             // [SASL]
382             setSASLStatus(CapSupportStatus::Disconnected);
383
384             // Add additional capability-dependent interface updates here
385         }
386     } else {
387         // Capability negotiation is not supported and/or network doesn't exist.
388         // Don't assume anything and reset all capability-dependent interface elements to neutral.
389         // [SASL]
390         setSASLStatus(CapSupportStatus::Unknown);
391
392         // Add additional capability-dependent interface updates here
393     }
394 }
395
396
397 void NetworksSettingsPage::coreConnectionStateChanged(bool state)
398 {
399     this->setEnabled(state);
400     if (state) {
401         load();
402     }
403     else {
404         // reset
405         //currentId = 0;
406     }
407 }
408
409
410 void NetworksSettingsPage::clientIdentityAdded(IdentityId id)
411 {
412     const Identity *identity = Client::identity(id);
413     connect(identity, &SyncableObject::updatedRemotely, this, &NetworksSettingsPage::clientIdentityUpdated);
414
415     QString name = identity->identityName();
416     for (int j = 0; j < ui.identityList->count(); j++) {
417         if ((j > 0 || ui.identityList->itemData(0).toInt() != 1) && name.localeAwareCompare(ui.identityList->itemText(j)) < 0) {
418             ui.identityList->insertItem(j, name, id.toInt());
419             widgetHasChanged();
420             return;
421         }
422     }
423     // append
424     ui.identityList->insertItem(ui.identityList->count(), name, id.toInt());
425     widgetHasChanged();
426 }
427
428
429 void NetworksSettingsPage::clientIdentityUpdated()
430 {
431     const auto *identity = qobject_cast<const Identity *>(sender());
432     if (!identity) {
433         qWarning() << "NetworksSettingsPage: Invalid identity to update!";
434         return;
435     }
436     int row = ui.identityList->findData(identity->id().toInt());
437     if (row < 0) {
438         qWarning() << "NetworksSettingsPage: Invalid identity to update!";
439         return;
440     }
441     if (ui.identityList->itemText(row) != identity->identityName()) {
442         ui.identityList->setItemText(row, identity->identityName());
443     }
444 }
445
446
447 void NetworksSettingsPage::clientIdentityRemoved(IdentityId id)
448 {
449     IdentityId defaultId = defaultIdentity();
450     if (currentId != 0) saveToNetworkInfo(networkInfos[currentId]);
451     foreach(NetworkInfo info, networkInfos.values()) {
452         if (info.identity == id) {
453             if (info.networkId == currentId)
454                 ui.identityList->setCurrentIndex(0);
455             info.identity = defaultId;
456             networkInfos[info.networkId] = info;
457             if (info.networkId > 0) Client::updateNetwork(info);
458         }
459     }
460     ui.identityList->removeItem(ui.identityList->findData(id.toInt()));
461     widgetHasChanged();
462 }
463
464
465 QListWidgetItem *NetworksSettingsPage::networkItem(NetworkId id) const
466 {
467     for (int i = 0; i < ui.networkList->count(); i++) {
468         QListWidgetItem *item = ui.networkList->item(i);
469         if (item->data(Qt::UserRole).value<NetworkId>() == id) return item;
470     }
471     return nullptr;
472 }
473
474
475 void NetworksSettingsPage::clientNetworkAdded(NetworkId id)
476 {
477     insertNetwork(id);
478     //connect(Client::network(id), &Network::updatedRemotely, this, &NetworksSettingsPage::clientNetworkUpdated);
479     connect(Client::network(id), &Network::configChanged, this, &NetworksSettingsPage::clientNetworkUpdated);
480
481     connect(Client::network(id), &Network::connectionStateSet, this, &NetworksSettingsPage::networkConnectionStateChanged);
482     connect(Client::network(id), &Network::connectionError, this, &NetworksSettingsPage::networkConnectionError);
483
484     // Handle capability changes in case a server dis/connects with the settings window open.
485     connect(Client::network(id), &Network::capAdded, this, &NetworksSettingsPage::clientNetworkCapsUpdated);
486     connect(Client::network(id), &Network::capRemoved, this, &NetworksSettingsPage::clientNetworkCapsUpdated);
487 }
488
489
490 void NetworksSettingsPage::clientNetworkUpdated()
491 {
492     const auto *net = qobject_cast<const Network *>(sender());
493     if (!net) {
494         qWarning() << "Update request for unknown network received!";
495         return;
496     }
497     networkInfos[net->networkId()] = net->networkInfo();
498     setItemState(net->networkId());
499     if (net->networkId() == currentId) displayNetwork(net->networkId());
500     setWidgetStates();
501     widgetHasChanged();
502 }
503
504
505 void NetworksSettingsPage::clientNetworkRemoved(NetworkId id)
506 {
507     if (!networkInfos.contains(id)) return;
508     if (id == currentId) displayNetwork(0);
509     NetworkInfo info = networkInfos.take(id);
510     QList<QListWidgetItem *> items = ui.networkList->findItems(info.networkName, Qt::MatchExactly);
511     foreach(QListWidgetItem *item, items) {
512         if (item->data(Qt::UserRole).value<NetworkId>() == id)
513             delete ui.networkList->takeItem(ui.networkList->row(item));
514     }
515     setWidgetStates();
516     widgetHasChanged();
517 }
518
519
520 void NetworksSettingsPage::networkConnectionStateChanged(Network::ConnectionState state)
521 {
522     Q_UNUSED(state);
523     const auto *net = qobject_cast<const Network *>(sender());
524     if (!net) return;
525     /*
526     if(net->networkId() == currentId) {
527       ui.connectNow->setEnabled(state == Network::Initialized || state == Network::Disconnected);
528     }
529     */
530     setItemState(net->networkId());
531     if (net->networkId() == currentId) {
532         // Network is currently shown.  Update the capability-dependent UI in case capabilities have
533         // changed.
534         setNetworkCapStates(currentId);
535     }
536     setWidgetStates();
537 }
538
539
540 void NetworksSettingsPage::networkConnectionError(const QString &)
541 {
542 }
543
544
545 QListWidgetItem *NetworksSettingsPage::insertNetwork(NetworkId id)
546 {
547     NetworkInfo info = Client::network(id)->networkInfo();
548     networkInfos[id] = info;
549     return insertNetwork(info);
550 }
551
552
553 QListWidgetItem *NetworksSettingsPage::insertNetwork(const NetworkInfo &info)
554 {
555     QListWidgetItem *item = nullptr;
556     QList<QListWidgetItem *> items = ui.networkList->findItems(info.networkName, Qt::MatchExactly);
557     if (!items.count()) item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
558     else {
559         // we overwrite an existing net if it a) has the same name and b) has a negative ID meaning we created it locally before
560         // -> then we can be sure that this is the core-side replacement for the net we created
561         foreach(QListWidgetItem *i, items) {
562             NetworkId id = i->data(Qt::UserRole).value<NetworkId>();
563             if (id < 0) { item = i; break; }
564         }
565         if (!item) item = new QListWidgetItem(disconnectedIcon, info.networkName, ui.networkList);
566     }
567     item->setData(Qt::UserRole, QVariant::fromValue<NetworkId>(info.networkId));
568     setItemState(info.networkId, item);
569     widgetHasChanged();
570     return item;
571 }
572
573
574 // Called when selecting 'Configure' from the buffer list
575 void NetworksSettingsPage::bufferList_Open(NetworkId netId)
576 {
577     QListWidgetItem *item = networkItem(netId);
578     ui.networkList->setCurrentItem(item, QItemSelectionModel::SelectCurrent);
579 }
580
581
582 void NetworksSettingsPage::displayNetwork(NetworkId id)
583 {
584     _ignoreWidgetChanges = true;
585     if (id != 0) {
586         NetworkInfo info = networkInfos[id];
587
588 #ifdef HAVE_SSL
589         // this is only needed when the core supports SASL EXTERNAL
590         if (Client::isCoreFeatureEnabled(Quassel::Feature::SaslExternal)) {
591             if (_cid) {
592                 disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
593                 delete _cid;
594             }
595             _cid = new CertIdentity(*Client::identity(info.identity), this);
596             _cid->enableEditSsl(true);
597             connect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
598         }
599 #endif
600
601         ui.identityList->setCurrentIndex(ui.identityList->findData(info.identity.toInt()));
602         ui.serverList->clear();
603         foreach(Network::Server server, info.serverList) {
604             QListWidgetItem *item = new QListWidgetItem(QString("%1:%2").arg(server.host).arg(server.port));
605             if (server.useSsl)
606                 item->setIcon(icon::get("document-encrypt"));
607             ui.serverList->addItem(item);
608         }
609         //setItemState(id);
610         //ui.randomServer->setChecked(info.useRandomServer);
611         // Update the capability-dependent UI in case capabilities have changed.
612         setNetworkCapStates(id);
613         ui.performEdit->setPlainText(info.perform.join("\n"));
614         ui.autoIdentify->setChecked(info.useAutoIdentify);
615         ui.autoIdentifyService->setText(info.autoIdentifyService);
616         ui.autoIdentifyPassword->setText(info.autoIdentifyPassword);
617         ui.sasl->setChecked(info.useSasl);
618         ui.saslAccount->setText(info.saslAccount);
619         ui.saslPassword->setText(info.saslPassword);
620         if (info.codecForEncoding.isEmpty()) {
621             ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(Network::defaultCodecForEncoding()));
622             ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(Network::defaultCodecForDecoding()));
623             ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(Network::defaultCodecForServer()));
624             ui.useCustomEncodings->setChecked(false);
625         }
626         else {
627             ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(info.codecForEncoding));
628             ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(info.codecForDecoding));
629             ui.serverEncoding->setCurrentIndex(ui.serverEncoding->findText(info.codecForServer));
630             ui.useCustomEncodings->setChecked(true);
631         }
632         ui.autoReconnect->setChecked(info.useAutoReconnect);
633         ui.reconnectInterval->setValue(info.autoReconnectInterval);
634         ui.reconnectRetries->setValue(info.autoReconnectRetries);
635         ui.unlimitedRetries->setChecked(info.unlimitedReconnectRetries);
636         ui.rejoinOnReconnect->setChecked(info.rejoinChannels);
637         // Custom rate limiting
638         ui.unlimitedMessageRate->setChecked(info.unlimitedMessageRate);
639         // Set 'ui.useCustomMessageRate' after 'ui.unlimitedMessageRate' so if the latter is
640         // disabled, 'ui.messageRateDelayFrame' will remain disabled.
641         ui.useCustomMessageRate->setChecked(info.useCustomMessageRate);
642         ui.messageRateBurstSize->setValue(info.messageRateBurstSize);
643         // Convert milliseconds (integer) into seconds (double)
644         ui.messageRateDelay->setValue(info.messageRateDelay / 1000.0f);
645     }
646     else {
647         // just clear widgets
648 #ifdef HAVE_SSL
649         if (_cid) {
650             disconnect(_cid, &CertIdentity::sslSettingsUpdated, this, &NetworksSettingsPage::sslUpdated);
651             delete _cid;
652         }
653 #endif
654         ui.identityList->setCurrentIndex(-1);
655         ui.serverList->clear();
656         ui.performEdit->clear();
657         ui.autoIdentifyService->clear();
658         ui.autoIdentifyPassword->clear();
659         ui.saslAccount->clear();
660         ui.saslPassword->clear();
661         setWidgetStates();
662     }
663     _ignoreWidgetChanges = false;
664     currentId = id;
665 }
666
667
668 void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo &info)
669 {
670     info.identity = ui.identityList->itemData(ui.identityList->currentIndex()).toInt();
671     //info.useRandomServer = ui.randomServer->isChecked();
672     info.perform = ui.performEdit->toPlainText().split("\n");
673     info.useAutoIdentify = ui.autoIdentify->isChecked();
674     info.autoIdentifyService = ui.autoIdentifyService->text();
675     info.autoIdentifyPassword = ui.autoIdentifyPassword->text();
676     info.useSasl = ui.sasl->isChecked();
677     info.saslAccount = ui.saslAccount->text();
678     info.saslPassword = ui.saslPassword->text();
679     if (!ui.useCustomEncodings->isChecked()) {
680         info.codecForEncoding.clear();
681         info.codecForDecoding.clear();
682         info.codecForServer.clear();
683     }
684     else {
685         info.codecForEncoding = ui.sendEncoding->currentText().toLatin1();
686         info.codecForDecoding = ui.recvEncoding->currentText().toLatin1();
687         info.codecForServer = ui.serverEncoding->currentText().toLatin1();
688     }
689     info.useAutoReconnect = ui.autoReconnect->isChecked();
690     info.autoReconnectInterval = ui.reconnectInterval->value();
691     info.autoReconnectRetries = ui.reconnectRetries->value();
692     info.unlimitedReconnectRetries = ui.unlimitedRetries->isChecked();
693     info.rejoinChannels = ui.rejoinOnReconnect->isChecked();
694     // Custom rate limiting
695     info.useCustomMessageRate = ui.useCustomMessageRate->isChecked();
696     info.messageRateBurstSize = ui.messageRateBurstSize->value();
697     // Convert seconds (double) into milliseconds (integer)
698     info.messageRateDelay = static_cast<quint32>((ui.messageRateDelay->value() * 1000));
699     info.unlimitedMessageRate = ui.unlimitedMessageRate->isChecked();
700 }
701
702
703 void NetworksSettingsPage::clientNetworkCapsUpdated()
704 {
705     // Grab the updated network
706     const auto *net = qobject_cast<const Network *>(sender());
707     if (!net) {
708         qWarning() << "Update request for unknown network received!";
709         return;
710     }
711     if (net->networkId() == currentId) {
712         // Network is currently shown.  Update the capability-dependent UI in case capabilities have
713         // changed.
714         setNetworkCapStates(currentId);
715     }
716 }
717
718
719 void NetworksSettingsPage::setSASLStatus(const CapSupportStatus saslStatus)
720 {
721     if (_saslStatusSelected != saslStatus) {
722         // Update the cached copy of SASL status used with the Details dialog
723         _saslStatusSelected = saslStatus;
724
725         // Update the user interface
726         switch (saslStatus) {
727             case CapSupportStatus::Unknown:
728                 // There's no capability negotiation or network doesn't exist.  Don't assume
729                 // anything.
730                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(
731                                             tr("Could not check if supported by network")));
732                 ui.saslStatusIcon->setPixmap(infoIcon.pixmap(16));
733                 break;
734             case CapSupportStatus::Disconnected:
735                 // Disconnected from network, no way to check.
736                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(
737                                                 tr("Cannot check if supported when disconnected")));
738                 ui.saslStatusIcon->setPixmap(infoIcon.pixmap(16));
739                 break;
740             case CapSupportStatus::MaybeUnsupported:
741                 // The network doesn't advertise support for SASL PLAIN.  Here be dragons.
742                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(
743                                                 tr("Not currently supported by network")));
744                 ui.saslStatusIcon->setPixmap(warningIcon.pixmap(16));
745                 break;
746             case CapSupportStatus::MaybeSupported:
747                 // The network advertises support for SASL PLAIN.  Encourage using it!
748                 // Unfortunately we don't know for sure if it's desired or functional.
749                 ui.saslStatusLabel->setText(QString("<i>%1</i>").arg(tr("Supported by network")));
750                 ui.saslStatusIcon->setPixmap(infoIcon.pixmap(16));
751                 break;
752         }
753     }
754 }
755
756
757 #ifdef HAVE_SSL
758 void NetworksSettingsPage::sslUpdated()
759 {
760     if (_cid && !_cid->sslKey().isNull()) {
761         ui.saslContents->setDisabled(true);
762         ui.saslExtInfo->setHidden(false);
763     } else {
764         ui.saslContents->setDisabled(false);
765         // Directly re-enabling causes the widgets to ignore the parent "Use SASL Authentication"
766         // state to indicate whether or not it's disabled.  To workaround this, keep track of
767         // whether or not "Use SASL Authentication" is enabled, then quickly uncheck/recheck the
768         // group box.
769         if (!ui.sasl->isChecked()) {
770             // SASL is not enabled, uncheck/recheck the group box to re-disable saslContents.
771             // Leaving saslContents disabled doesn't work as that prevents it from re-enabling if
772             // sasl is later checked.
773             ui.sasl->setChecked(true);
774             ui.sasl->setChecked(false);
775         }
776         ui.saslExtInfo->setHidden(true);
777     }
778 }
779 #endif
780
781
782 /*** Network list ***/
783
784 void NetworksSettingsPage::on_networkList_itemSelectionChanged()
785 {
786     if (currentId != 0) {
787         saveToNetworkInfo(networkInfos[currentId]);
788     }
789     if (ui.networkList->selectedItems().count()) {
790         NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
791         currentId = id;
792         displayNetwork(id);
793         ui.serverList->setCurrentRow(0);
794     }
795     else {
796         currentId = 0;
797     }
798     setWidgetStates();
799 }
800
801
802 void NetworksSettingsPage::on_addNetwork_clicked()
803 {
804     QStringList existing;
805     for (int i = 0; i < ui.networkList->count(); i++) existing << ui.networkList->item(i)->text();
806     NetworkAddDlg dlg(existing, this);
807     if (dlg.exec() == QDialog::Accepted) {
808         NetworkInfo info = dlg.networkInfo();
809         if (info.networkName.isEmpty())
810             return;  // sanity check
811
812         NetworkId id;
813         for (id = 1; id <= networkInfos.count(); id++) {
814             widgetHasChanged();
815             if (!networkInfos.keys().contains(-id.toInt())) break;
816         }
817         id = -id.toInt();
818         info.networkId = id;
819         info.identity = defaultIdentity();
820         networkInfos[id] = info;
821         QListWidgetItem *item = insertNetwork(info);
822         ui.networkList->setCurrentItem(item);
823         setWidgetStates();
824     }
825 }
826
827
828 void NetworksSettingsPage::on_deleteNetwork_clicked()
829 {
830     if (ui.networkList->selectedItems().count()) {
831         NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
832         int ret = QMessageBox::question(this, tr("Delete Network?"),
833             tr("Do you really want to delete the network \"%1\" and all related settings, including the backlog?").arg(networkInfos[netid].networkName),
834             QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
835         if (ret == QMessageBox::Yes) {
836             currentId = 0;
837             networkInfos.remove(netid);
838             delete ui.networkList->takeItem(ui.networkList->row(ui.networkList->selectedItems()[0]));
839             ui.networkList->setCurrentRow(qMin(ui.networkList->currentRow()+1, ui.networkList->count()-1));
840             setWidgetStates();
841             widgetHasChanged();
842         }
843     }
844 }
845
846
847 void NetworksSettingsPage::on_renameNetwork_clicked()
848 {
849     if (!ui.networkList->selectedItems().count()) return;
850     QString old = ui.networkList->selectedItems()[0]->text();
851     QStringList existing;
852     for (int i = 0; i < ui.networkList->count(); i++) existing << ui.networkList->item(i)->text();
853     NetworkEditDlg dlg(old, existing, this);
854     if (dlg.exec() == QDialog::Accepted) {
855         ui.networkList->selectedItems()[0]->setText(dlg.networkName());
856         NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
857         networkInfos[netid].networkName = dlg.networkName();
858         widgetHasChanged();
859     }
860 }
861
862
863 /*
864 void NetworksSettingsPage::on_connectNow_clicked() {
865   if(!ui.networkList->selectedItems().count()) return;
866   NetworkId id = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
867   const Network *net = Client::network(id);
868   if(!net) return;
869   if(net->connectionState() == Network::Disconnected) net->requestConnect();
870   else net->requestDisconnect();
871 }
872 */
873
874 /*** Server list ***/
875
876 void NetworksSettingsPage::on_serverList_itemSelectionChanged()
877 {
878     setWidgetStates();
879 }
880
881
882 void NetworksSettingsPage::on_addServer_clicked()
883 {
884     if (currentId == 0) return;
885     ServerEditDlg dlg(Network::Server(), this);
886     if (dlg.exec() == QDialog::Accepted) {
887         networkInfos[currentId].serverList.append(dlg.serverData());
888         displayNetwork(currentId);
889         ui.serverList->setCurrentRow(ui.serverList->count()-1);
890         widgetHasChanged();
891     }
892 }
893
894
895 void NetworksSettingsPage::on_editServer_clicked()
896 {
897     if (currentId == 0) return;
898     int cur = ui.serverList->currentRow();
899     ServerEditDlg dlg(networkInfos[currentId].serverList[cur], this);
900     if (dlg.exec() == QDialog::Accepted) {
901         networkInfos[currentId].serverList[cur] = dlg.serverData();
902         displayNetwork(currentId);
903         ui.serverList->setCurrentRow(cur);
904         widgetHasChanged();
905     }
906 }
907
908
909 void NetworksSettingsPage::on_deleteServer_clicked()
910 {
911     if (currentId == 0) return;
912     int cur = ui.serverList->currentRow();
913     networkInfos[currentId].serverList.removeAt(cur);
914     displayNetwork(currentId);
915     ui.serverList->setCurrentRow(qMin(cur, ui.serverList->count()-1));
916     widgetHasChanged();
917 }
918
919
920 void NetworksSettingsPage::on_upServer_clicked()
921 {
922     int cur = ui.serverList->currentRow();
923     Network::Server server = networkInfos[currentId].serverList.takeAt(cur);
924     networkInfos[currentId].serverList.insert(cur-1, server);
925     displayNetwork(currentId);
926     ui.serverList->setCurrentRow(cur-1);
927     widgetHasChanged();
928 }
929
930
931 void NetworksSettingsPage::on_downServer_clicked()
932 {
933     int cur = ui.serverList->currentRow();
934     Network::Server server = networkInfos[currentId].serverList.takeAt(cur);
935     networkInfos[currentId].serverList.insert(cur+1, server);
936     displayNetwork(currentId);
937     ui.serverList->setCurrentRow(cur+1);
938     widgetHasChanged();
939 }
940
941
942 void NetworksSettingsPage::on_editIdentities_clicked()
943 {
944     SettingsPageDlg dlg(new IdentitiesSettingsPage(this), this);
945     dlg.exec();
946 }
947
948
949 void NetworksSettingsPage::on_saslStatusDetails_clicked()
950 {
951     if (ui.networkList->selectedItems().count()) {
952         NetworkId netid = ui.networkList->selectedItems()[0]->data(Qt::UserRole).value<NetworkId>();
953         QString &netName = networkInfos[netid].networkName;
954
955         // If these strings are visible, one of the status messages wasn't detected below.
956         QString saslStatusHeader = "[header unintentionally left blank]";
957         QString saslStatusExplanation = "[explanation unintentionally left blank]";
958
959         // If true, show a warning icon instead of an information icon
960         bool useWarningIcon = false;
961
962         // Determine which explanation to show
963         switch (_saslStatusSelected) {
964         case CapSupportStatus::Unknown:
965             saslStatusHeader = tr("Could not check if SASL supported by network");
966             saslStatusExplanation = tr("Quassel could not check if \"%1\" supports SASL.  This may "
967                                        "be due to unsaved changes or an older Quassel core.  You "
968                                        "can still try using SASL.").arg(netName);
969             break;
970         case CapSupportStatus::Disconnected:
971             saslStatusHeader = tr("Cannot check if SASL supported when disconnected");
972             saslStatusExplanation = tr("Quassel cannot check if \"%1\" supports SASL when "
973                                        "disconnected.  Connect to the network, or try using SASL "
974                                        "anyways.").arg(netName);
975             break;
976         case CapSupportStatus::MaybeUnsupported:
977             saslStatusHeader = tr("SASL not currently supported by network");
978             saslStatusExplanation = tr("The network \"%1\" does not currently support SASL.  "
979                                        "However, support might be added later on.").arg(netName);
980             useWarningIcon = true;
981             break;
982         case CapSupportStatus::MaybeSupported:
983             saslStatusHeader = tr("SASL supported by network");
984             saslStatusExplanation = tr("The network \"%1\" supports SASL.  In most cases, you "
985                                        "should use SASL instead of NickServ identification."
986                                        ).arg(netName);
987             break;
988         }
989
990         // Process this in advance for reusability below
991         const QString saslStatusMsgTitle = tr("SASL support for \"%1\"").arg(netName);
992         const QString saslStatusMsgText =
993                 QString("<p><b>%1</b></p></br><p>%2</p></br><p><i>%3</i></p>"
994                         ).arg(saslStatusHeader,
995                               saslStatusExplanation,
996                               tr("SASL is a standardized way to log in and identify yourself to "
997                                  "IRC servers."));
998
999         if (useWarningIcon) {
1000             // Show as a warning dialog box
1001             QMessageBox::warning(this, saslStatusMsgTitle, saslStatusMsgText);
1002         } else {
1003             // Show as an information dialog box
1004             QMessageBox::information(this, saslStatusMsgTitle, saslStatusMsgText);
1005         }
1006     }
1007 }
1008
1009
1010 IdentityId NetworksSettingsPage::defaultIdentity() const
1011 {
1012     IdentityId defaultId = 0;
1013     QList<IdentityId> ids = Client::identityIds();
1014     foreach(IdentityId id, ids) {
1015         if (defaultId == 0 || id < defaultId)
1016             defaultId = id;
1017     }
1018     return defaultId;
1019 }
1020
1021
1022 /**************************************************************************
1023 * NetworkAddDlg
1024 *************************************************************************/
1025
1026 NetworkAddDlg::NetworkAddDlg(QStringList exist, QWidget *parent) : QDialog(parent), existing(std::move(exist))
1027 {
1028     ui.setupUi(this);
1029     ui.useSSL->setIcon(icon::get("document-encrypt"));
1030
1031     // Whenever useSSL is toggled, update the port number if not changed from the default
1032     connect(ui.useSSL, &QAbstractButton::toggled, this, &NetworkAddDlg::updateSslPort);
1033     // Do NOT call updateSslPort when loading settings, otherwise port settings may be overriden.
1034     // If useSSL is later changed to be checked by default, change port's default value, too.
1035
1036     if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) {
1037         // Synchronize requiring SSL with the use SSL checkbox
1038         ui.sslVerify->setEnabled(ui.useSSL->isChecked());
1039         connect(ui.useSSL, &QAbstractButton::toggled, ui.sslVerify, &QWidget::setEnabled);
1040     } else {
1041         // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck
1042         ui.sslVerify->setEnabled(false);
1043         ui.sslVerify->setChecked(false);
1044         // Split up the message to allow re-using translations:
1045         // [Original tool-tip]
1046         // [Bold 'does not support feature' message]
1047         // [Specific version needed and feature details]
1048         ui.sslVerify->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
1049                                        ui.sslVerify->toolTip(),
1050                                        tr("Your Quassel core does not support this feature"),
1051                                        tr("You need a Quassel core v0.13.0 or newer in order to "
1052                                           "verify connection security.")));
1053     }
1054
1055     // read preset networks
1056     QStringList networks = PresetNetworks::names();
1057     foreach(QString s, existing)
1058     networks.removeAll(s);
1059     if (networks.count())
1060         ui.presetList->addItems(networks);
1061     else {
1062         ui.useManual->setChecked(true);
1063         ui.usePreset->setEnabled(false);
1064     }
1065     connect(ui.networkName, &QLineEdit::textChanged, this, &NetworkAddDlg::setButtonStates);
1066     connect(ui.serverAddress, &QLineEdit::textChanged, this, &NetworkAddDlg::setButtonStates);
1067     connect(ui.usePreset, &QRadioButton::toggled, this, &NetworkAddDlg::setButtonStates);
1068     connect(ui.useManual, &QRadioButton::toggled, this, &NetworkAddDlg::setButtonStates);
1069     setButtonStates();
1070 }
1071
1072
1073 NetworkInfo NetworkAddDlg::networkInfo() const
1074 {
1075     if (ui.useManual->isChecked()) {
1076         NetworkInfo info;
1077         info.networkName = ui.networkName->text().trimmed();
1078         info.serverList << Network::Server(ui.serverAddress->text().trimmed(), ui.port->value(),
1079                                            ui.serverPassword->text(), ui.useSSL->isChecked(),
1080                                            ui.sslVerify->isChecked());
1081         return info;
1082     }
1083     else
1084         return PresetNetworks::networkInfo(ui.presetList->currentText());
1085 }
1086
1087
1088 void NetworkAddDlg::setButtonStates()
1089 {
1090     bool ok = false;
1091     if (ui.usePreset->isChecked() && ui.presetList->count())
1092         ok = true;
1093     else if (ui.useManual->isChecked()) {
1094         ok = !ui.networkName->text().trimmed().isEmpty() && !existing.contains(ui.networkName->text().trimmed())
1095              && !ui.serverAddress->text().isEmpty();
1096     }
1097     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
1098 }
1099
1100
1101 void NetworkAddDlg::updateSslPort(bool isChecked)
1102 {
1103     // "Use encrypted connection" was toggled, check the state...
1104     if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) {
1105         // Had been using the plain-text port, use the SSL default
1106         ui.port->setValue(Network::PORT_SSL);
1107     } else if (!isChecked && ui.port->value() == Network::PORT_SSL) {
1108         // Had been using the SSL port, use the plain-text default
1109         ui.port->setValue(Network::PORT_PLAINTEXT);
1110     }
1111 }
1112
1113
1114 /**************************************************************************
1115  * NetworkEditDlg
1116  *************************************************************************/
1117
1118 NetworkEditDlg::NetworkEditDlg(const QString &old, QStringList exist, QWidget *parent) : QDialog(parent), existing(std::move(exist))
1119 {
1120     ui.setupUi(this);
1121
1122     if (old.isEmpty()) {
1123         // new network
1124         setWindowTitle(tr("Add Network"));
1125         on_networkEdit_textChanged(""); // disable ok button
1126     }
1127     else ui.networkEdit->setText(old);
1128 }
1129
1130
1131 QString NetworkEditDlg::networkName() const
1132 {
1133     return ui.networkEdit->text().trimmed();
1134 }
1135
1136
1137 void NetworkEditDlg::on_networkEdit_textChanged(const QString &text)
1138 {
1139     ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty() || existing.contains(text.trimmed()));
1140 }
1141
1142
1143 /**************************************************************************
1144  * ServerEditDlg
1145  *************************************************************************/
1146 ServerEditDlg::ServerEditDlg(const Network::Server &server, QWidget *parent) : QDialog(parent)
1147 {
1148     ui.setupUi(this);
1149     ui.useSSL->setIcon(icon::get("document-encrypt"));
1150     ui.host->setText(server.host);
1151     ui.host->setFocus();
1152     ui.port->setValue(server.port);
1153     ui.password->setText(server.password);
1154     ui.useSSL->setChecked(server.useSsl);
1155     ui.sslVerify->setChecked(server.sslVerify);
1156     ui.sslVersion->setCurrentIndex(server.sslVersion);
1157     ui.useProxy->setChecked(server.useProxy);
1158     ui.proxyType->setCurrentIndex(server.proxyType == QNetworkProxy::Socks5Proxy ? 0 : 1);
1159     ui.proxyHost->setText(server.proxyHost);
1160     ui.proxyPort->setValue(server.proxyPort);
1161     ui.proxyUsername->setText(server.proxyUser);
1162     ui.proxyPassword->setText(server.proxyPass);
1163
1164     // This is a dirty hack to display the core->IRC SSL protocol dropdown
1165     // only if the core won't use autonegotiation to determine the best
1166     // protocol.  When autonegotiation was introduced, it would have been
1167     // a good idea to use the CoreFeatures enum to accomplish this.
1168     // However, since multiple versions have been released since then, that
1169     // is no longer possible.  Instead, we rely on the fact that the
1170     // Datastream protocol was introduced in the same version (0.10) as SSL
1171     // autonegotiation.  Because of that, we can display the dropdown only
1172     // if the Legacy protocol is in use.  If any other RemotePeer protocol
1173     // is in use, that means a newer protocol is in use and therefore the
1174     // core will use autonegotiation.
1175     if (Client::coreConnection()->peer()->protocol() != Protocol::LegacyProtocol) {
1176         ui.label_3->hide();
1177         ui.sslVersion->hide();
1178     }
1179
1180     // Whenever useSSL is toggled, update the port number if not changed from the default
1181     connect(ui.useSSL, &QAbstractButton::toggled, this, &ServerEditDlg::updateSslPort);
1182     // Do NOT call updateSslPort when loading settings, otherwise port settings may be overriden.
1183     // If useSSL is later changed to be checked by default, change port's default value, too.
1184
1185     if (Client::isCoreFeatureEnabled(Quassel::Feature::VerifyServerSSL)) {
1186         // Synchronize requiring SSL with the use SSL checkbox
1187         ui.sslVerify->setEnabled(ui.useSSL->isChecked());
1188         connect(ui.useSSL, &QAbstractButton::toggled, ui.sslVerify, &QWidget::setEnabled);
1189     } else {
1190         // Core isn't new enough to allow requiring SSL; disable checkbox and uncheck
1191         ui.sslVerify->setEnabled(false);
1192         ui.sslVerify->setChecked(false);
1193         // Split up the message to allow re-using translations:
1194         // [Original tool-tip]
1195         // [Bold 'does not support feature' message]
1196         // [Specific version needed and feature details]
1197         ui.sslVerify->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
1198                                        ui.sslVerify->toolTip(),
1199                                        tr("Your Quassel core does not support this feature"),
1200                                        tr("You need a Quassel core v0.13.0 or newer in order to "
1201                                           "verify connection security.")));
1202     }
1203
1204     on_host_textChanged();
1205 }
1206
1207
1208 Network::Server ServerEditDlg::serverData() const
1209 {
1210     Network::Server server(ui.host->text().trimmed(), ui.port->value(), ui.password->text(),
1211                            ui.useSSL->isChecked(), ui.sslVerify->isChecked());
1212     server.sslVersion = ui.sslVersion->currentIndex();
1213     server.useProxy = ui.useProxy->isChecked();
1214     server.proxyType = ui.proxyType->currentIndex() == 0 ? QNetworkProxy::Socks5Proxy : QNetworkProxy::HttpProxy;
1215     server.proxyHost = ui.proxyHost->text();
1216     server.proxyPort = ui.proxyPort->value();
1217     server.proxyUser = ui.proxyUsername->text();
1218     server.proxyPass = ui.proxyPassword->text();
1219     return server;
1220 }
1221
1222
1223 void ServerEditDlg::on_host_textChanged()
1224 {
1225     ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(ui.host->text().trimmed().isEmpty());
1226 }
1227
1228
1229 void ServerEditDlg::updateSslPort(bool isChecked)
1230 {
1231     // "Use encrypted connection" was toggled, check the state...
1232     if (isChecked && ui.port->value() == Network::PORT_PLAINTEXT) {
1233         // Had been using the plain-text port, use the SSL default
1234         ui.port->setValue(Network::PORT_SSL);
1235     } else if (!isChecked && ui.port->value() == Network::PORT_SSL) {
1236         // Had been using the SSL port, use the plain-text default
1237         ui.port->setValue(Network::PORT_PLAINTEXT);
1238     }
1239 }
1240
1241
1242 /**************************************************************************
1243  * SaveNetworksDlg
1244  *************************************************************************/
1245
1246 SaveNetworksDlg::SaveNetworksDlg(const QList<NetworkInfo> &toCreate, const QList<NetworkInfo> &toUpdate, const QList<NetworkId> &toRemove, QWidget *parent) : QDialog(parent)
1247 {
1248     ui.setupUi(this);
1249
1250     numevents = toCreate.count() + toUpdate.count() + toRemove.count();
1251     rcvevents = 0;
1252     if (numevents) {
1253         ui.progressBar->setMaximum(numevents);
1254         ui.progressBar->setValue(0);
1255
1256         connect(Client::instance(), &Client::networkCreated, this, &SaveNetworksDlg::clientEvent);
1257         connect(Client::instance(), &Client::networkRemoved, this, &SaveNetworksDlg::clientEvent);
1258
1259         foreach(NetworkId id, toRemove) {
1260             Client::removeNetwork(id);
1261         }
1262         foreach(NetworkInfo info, toCreate) {
1263             Client::createNetwork(info);
1264         }
1265         foreach(NetworkInfo info, toUpdate) {
1266             const Network *net = Client::network(info.networkId);
1267             if (!net) {
1268                 qWarning() << "Invalid client network!";
1269                 numevents--;
1270                 continue;
1271             }
1272             // FIXME this only checks for one changed item rather than all!
1273             connect(net, &SyncableObject::updatedRemotely, this, &SaveNetworksDlg::clientEvent);
1274             Client::updateNetwork(info);
1275         }
1276     }
1277     else {
1278         qWarning() << "Sync dialog called without stuff to change!";
1279         accept();
1280     }
1281 }
1282
1283
1284 void SaveNetworksDlg::clientEvent()
1285 {
1286     ui.progressBar->setValue(++rcvevents);
1287     if (rcvevents >= numevents) accept();
1288 }