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