ldap: Don't use the backend's display name as identifier
[quassel.git] / src / qtui / coreconfigwizard.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 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 <QDebug>
22 #include <QAbstractButton>
23 #include <QFormLayout>
24 #include <QIcon>
25 #include <QSpinBox>
26
27 #include "coreconfigwizard.h"
28 #include "coreconnection.h"
29
30 #include "client.h"
31
32 CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, const QList<QVariant> &authenticators, QWidget *parent)
33     : QWizard(parent),
34     _connection(connection)
35 {
36     setModal(true);
37     setAttribute(Qt::WA_DeleteOnClose);
38
39     foreach(const QVariant &v, backends)
40         _backends[v.toMap()["DisplayName"].toString()] = v;
41
42     foreach(const QVariant &v, authenticators)
43         _authenticators[v.toMap()["BackendId"].toString()] = v;
44
45     setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this));
46     setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this));
47     setPage(AuthenticationSelectionPage, new CoreConfigWizardPages::AuthenticationSelectionPage(_authenticators, this));
48     setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this));
49     syncPage = new CoreConfigWizardPages::SyncPage(this);
50     connect(syncPage, SIGNAL(setupCore(const QString &, const QVariantMap &, const QString &, const QVariantMap &)),
51             SLOT(prepareCoreSetup(const QString &, const QVariantMap &, const QString &, const QVariantMap &)));
52     setPage(SyncPage, syncPage);
53     syncRelayPage = new CoreConfigWizardPages::SyncRelayPage(this);
54     connect(syncRelayPage, SIGNAL(startOver()), this, SLOT(startOver()));
55     setPage(SyncRelayPage, syncRelayPage);
56     //setPage(Page_StorageDetails, new StorageDetailsPage());
57     //setPage(Page_Conclusion, new ConclusionPage(storageProviders));
58
59     setStartId(IntroPage);
60     //setStartId(StorageSelectionPage);
61
62 #ifndef Q_OS_MAC
63     setWizardStyle(ModernStyle);
64 #endif
65
66     setOption(HaveHelpButton, false);
67     setOption(NoBackButtonOnStartPage, true);
68     setOption(HaveNextButtonOnLastPage, false);
69     setOption(HaveFinishButtonOnEarlyPages, false);
70     setOption(NoCancelButton, true);
71     setOption(IndependentPages, true);
72     //setOption(ExtendedWatermarkPixmap, true);
73
74     setModal(true);
75
76     setWindowTitle(tr("Core Configuration Wizard"));
77     setPixmap(QWizard::LogoPixmap, QIcon::fromTheme("quassel", QIcon(":/icons/quassel.png")).pixmap(48));
78
79     connect(connection, SIGNAL(coreSetupSuccess()), SLOT(coreSetupSuccess()));
80     connect(connection, SIGNAL(coreSetupFailed(QString)), SLOT(coreSetupFailed(QString)));
81     //connect(connection, SIGNAL(loginSuccess()), SLOT(loginSuccess()));
82     connect(connection, SIGNAL(synchronized()), SLOT(syncFinished()));
83     connect(this, SIGNAL(rejected()), connection, SLOT(disconnectFromCore()));
84 }
85
86
87 QHash<QString, QVariant> CoreConfigWizard::backends() const
88 {
89     return _backends;
90 }
91
92
93 QHash<QString, QVariant> CoreConfigWizard::authenticators() const
94 {
95     return _authenticators;
96 }
97
98
99 void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authenticator, const QVariantMap &authProperties)
100 {
101     // Prevent the user from changing any settings he already specified...
102     foreach(int idx, visitedPages())
103     page(idx)->setEnabled(false);
104
105     // FIXME? We need to be able to set up older cores that don't have auth backend support.
106     // So if the core doesn't support that feature, don't pass those parameters.
107     if (!(Client::coreFeatures() & Quassel::Authenticators)) {
108         coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties));
109     }
110     else {
111         coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties, authenticator, authProperties));
112     }
113 }
114
115
116 void CoreConfigWizard::coreSetupSuccess()
117 {
118     syncPage->setStatus(tr("Your core has been successfully configured. Logging you in..."));
119     syncPage->setError(false);
120     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
121     coreConnection()->loginToCore(field("adminUser.user").toString(), field("adminUser.password").toString(), field("adminUser.rememberPasswd").toBool());
122 }
123
124
125 void CoreConfigWizard::coreSetupFailed(const QString &error)
126 {
127     syncPage->setStatus(tr("Core configuration failed:<br><b>%1</b><br>Press <em>Next</em> to start over.").arg(error));
128     syncPage->setError(true);
129     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
130     //foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
131     //setStartId(SyncPage);
132     //restart();
133 }
134
135
136 void CoreConfigWizard::startOver()
137 {
138     foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
139     setStartId(CoreConfigWizard::AdminUserPage);
140     restart();
141 }
142
143
144 void CoreConfigWizard::loginSuccess()
145 {
146     syncPage->setStatus(tr("Your are now logged into your freshly configured Quassel Core!<br>"
147                            "Please remember to configure your identities and networks now."));
148     syncPage->setComplete(true);
149     syncPage->setFinalPage(true);
150 }
151
152
153 void CoreConfigWizard::syncFinished()
154 {
155     accept();
156 }
157
158
159 namespace CoreConfigWizardPages {
160 /*** Intro Page ***/
161
162 IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent)
163 {
164     ui.setupUi(this);
165     setTitle(tr("Introduction"));
166     //setSubTitle(tr("foobar"));
167     //setPixmap(QWizard::WatermarkPixmap, QPixmap(":icons/quassel-icon.png"));
168 }
169
170
171 int IntroPage::nextId() const
172 {
173     return CoreConfigWizard::AdminUserPage;
174 }
175
176
177 /*** Admin User Page ***/
178
179 AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent)
180 {
181     ui.setupUi(this);
182     setTitle(tr("Create Admin User"));
183     setSubTitle(tr("First, we will create a user on the core. This first user will have administrator privileges."));
184
185     registerField("adminUser.user*", ui.user);
186     registerField("adminUser.password*", ui.password);
187     registerField("adminUser.password2*", ui.password2);
188     registerField("adminUser.rememberPasswd", ui.rememberPasswd);
189
190     //ui.user->setText("foo");
191     //ui.password->setText("foo");
192     //ui.password2->setText("foo");
193 }
194
195
196 int AdminUserPage::nextId() const
197 {
198     // If the core doesn't support auth backends, skip that page!
199     if (!(Client::coreFeatures() & Quassel::Authenticators)) {
200         return CoreConfigWizard::StorageSelectionPage;
201     }
202     else {
203         return CoreConfigWizard::AuthenticationSelectionPage;
204     }
205 }
206
207
208 bool AdminUserPage::isComplete() const
209 {
210     bool ok = !ui.user->text().isEmpty() && !ui.password->text().isEmpty() && ui.password->text() == ui.password2->text();
211     return ok;
212 }
213
214 /*** Authentication Selection Page ***/
215
216 AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
217     : QWizardPage(parent),
218     _connectionBox(0),
219     _backends(backends)
220 {
221     ui.setupUi(this);
222
223     setTitle(tr("Select Authentication Backend"));
224     setSubTitle(tr("Please select a backend for Quassel Core to use for authenticating users."));
225
226     registerField("authentication.backend", ui.backendList);
227
228     foreach(QString key, _backends.keys()) {
229         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
230     }
231
232     on_backendList_currentIndexChanged();
233 }
234
235
236 int AuthenticationSelectionPage::nextId() const
237 {
238     return CoreConfigWizard::StorageSelectionPage;
239 }
240
241
242 QString AuthenticationSelectionPage::selectedBackend() const
243 {
244     return ui.backendList->currentText();
245 }
246
247
248 QVariantMap AuthenticationSelectionPage::authProperties() const
249 {
250     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
251
252     QVariantMap properties;
253     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
254     if (!setupKeys.isEmpty()) {
255         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
256         foreach(QString key, setupKeys) {
257             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
258             QVariant def;
259             if (defaults.contains(key)) {
260                 def = defaults[key];
261             }
262             switch (def.type()) {
263             case QVariant::Int:
264             {
265                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
266                 Q_ASSERT(spinbox);
267                 def = QVariant(spinbox->value());
268             }
269             break;
270             default:
271             {
272                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
273                 Q_ASSERT(lineEdit);
274                 def = QVariant(lineEdit->text());
275             }
276             }
277             properties[key] = def;
278         }
279     }
280     return properties;
281 }
282
283
284 void AuthenticationSelectionPage::on_backendList_currentIndexChanged()
285 {
286     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
287     ui.description->setText(_backends[backend].toMap()["Description"].toString());
288
289     if (_connectionBox) {
290         layout()->removeWidget(_connectionBox);
291         _connectionBox->deleteLater();
292         _connectionBox = 0;
293     }
294
295     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
296     if (!setupKeys.isEmpty()) {
297         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
298         QGroupBox *propertyBox = new QGroupBox(this);
299         propertyBox->setTitle(tr("Connection Properties"));
300         QFormLayout *formlayout = new QFormLayout;
301
302         foreach(QString key, setupKeys) {
303             QWidget *widget = 0;
304             QVariant def;
305             if (defaults.contains(key)) {
306                 def = defaults[key];
307             }
308             switch (def.type()) {
309             case QVariant::Int:
310             {
311                 QSpinBox *spinbox = new QSpinBox(propertyBox);
312                 spinbox->setMaximum(64000);
313                 spinbox->setValue(def.toInt());
314                 widget = spinbox;
315             }
316             break;
317             default:
318             {
319                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
320                 if (key.toLower().contains("password")) {
321                     lineEdit->setEchoMode(QLineEdit::Password);
322                 }
323                 widget = lineEdit;
324             }
325             }
326             widget->setObjectName(key);
327             formlayout->addRow(key + ":", widget);
328         }
329         propertyBox->setLayout(formlayout);
330         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
331         _connectionBox = propertyBox;
332     }
333 }
334
335 /*** Storage Selection Page ***/
336
337 StorageSelectionPage::StorageSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
338     : QWizardPage(parent),
339     _connectionBox(0),
340     _backends(backends)
341 {
342     ui.setupUi(this);
343
344     setTitle(tr("Select Storage Backend"));
345     setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in."));
346     setCommitPage(true);
347
348     registerField("storage.backend", ui.backendList);
349
350     int defaultIndex = 0;
351     foreach(QString key, _backends.keys()) {
352         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
353         if (_backends[key].toMap()["IsDefault"].toBool()) {
354             defaultIndex = ui.backendList->count() - 1;
355         }
356     }
357
358     ui.backendList->setCurrentIndex(defaultIndex);
359
360     on_backendList_currentIndexChanged();
361 }
362
363
364 int StorageSelectionPage::nextId() const
365 {
366     return CoreConfigWizard::SyncPage;
367 }
368
369
370 QString StorageSelectionPage::selectedBackend() const
371 {
372     return ui.backendList->currentText();
373 }
374
375
376 QVariantMap StorageSelectionPage::connectionProperties() const
377 {
378     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
379
380     QVariantMap properties;
381     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
382     if (!setupKeys.isEmpty()) {
383         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
384         foreach(QString key, setupKeys) {
385             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
386             QVariant def;
387             if (defaults.contains(key)) {
388                 def = defaults[key];
389             }
390             switch (def.type()) {
391             case QVariant::Int:
392             {
393                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
394                 Q_ASSERT(spinbox);
395                 def = QVariant(spinbox->value());
396             }
397             break;
398             default:
399             {
400                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
401                 Q_ASSERT(lineEdit);
402                 def = QVariant(lineEdit->text());
403             }
404             }
405             properties[key] = def;
406         }
407     }
408     qDebug() << properties;
409
410 //   QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap();
411 //   if(!properties.isEmpty() && _connectionBox) {
412 //     QVariantMap::iterator propertyIter = properties.begin();
413 //     while(propertyIter != properties.constEnd()) {
414 //       QWidget *widget = _connectionBox->findChild<QWidget *>(propertyIter.key());
415 //       switch(propertyIter.value().type()) {
416 //       case QVariant::Int:
417 //      {
418 //        QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
419 //        Q_ASSERT(spinbox);
420 //        propertyIter.value() = QVariant(spinbox->value());
421 //      }
422 //      break;
423 //       default:
424 //      {
425 //        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
426 //        Q_ASSERT(lineEdit);
427 //        propertyIter.value() = QVariant(lineEdit->text());
428 //      }
429 //       }
430 //       propertyIter++;
431 //     }
432 //   }
433     return properties;
434 }
435
436
437 void StorageSelectionPage::on_backendList_currentIndexChanged()
438 {
439     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
440     ui.description->setText(_backends[backend].toMap()["Description"].toString());
441
442     if (_connectionBox) {
443         layout()->removeWidget(_connectionBox);
444         _connectionBox->deleteLater();
445         _connectionBox = 0;
446     }
447
448     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
449     if (!setupKeys.isEmpty()) {
450         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
451         QGroupBox *propertyBox = new QGroupBox(this);
452         propertyBox->setTitle(tr("Connection Properties"));
453         QFormLayout *formlayout = new QFormLayout;
454
455         foreach(QString key, setupKeys) {
456             QWidget *widget = 0;
457             QVariant def;
458             if (defaults.contains(key)) {
459                 def = defaults[key];
460             }
461             switch (def.type()) {
462             case QVariant::Int:
463             {
464                 QSpinBox *spinbox = new QSpinBox(propertyBox);
465                 spinbox->setMaximum(64000);
466                 spinbox->setValue(def.toInt());
467                 widget = spinbox;
468             }
469             break;
470             default:
471             {
472                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
473                 if (key.toLower().contains("password")) {
474                     lineEdit->setEchoMode(QLineEdit::Password);
475                 }
476                 widget = lineEdit;
477             }
478             }
479             widget->setObjectName(key);
480             formlayout->addRow(key + ":", widget);
481         }
482         propertyBox->setLayout(formlayout);
483         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
484         _connectionBox = propertyBox;
485     }
486 }
487
488
489 /*** Sync Page ***/
490
491 SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent)
492 {
493     ui.setupUi(this);
494     setTitle(tr("Storing Your Settings"));
495     setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically."));
496 }
497
498
499 void SyncPage::initializePage()
500 {
501     complete = false;
502     hasError = false;
503
504     // Fill in sync info about the storage layer.
505     StorageSelectionPage *storagePage = qobject_cast<StorageSelectionPage *>(wizard()->page(CoreConfigWizard::StorageSelectionPage));
506     QString backend = storagePage->selectedBackend();
507     QVariantMap properties = storagePage->connectionProperties();
508     Q_ASSERT(!backend.isEmpty());
509     ui.backend->setText(backend);
510
511     // Fill in sync info about the authentication layer.
512     AuthenticationSelectionPage *authPage = qobject_cast<AuthenticationSelectionPage *>(wizard()->page(CoreConfigWizard::AuthenticationSelectionPage));
513     QString authenticator = authPage->selectedBackend();
514     QVariantMap authProperties = authPage->authProperties();
515     Q_ASSERT(!authenticator.isEmpty());
516     ui.authenticator->setText(authenticator);
517
518     ui.user->setText(wizard()->field("adminUser.user").toString());
519
520     emit setupCore(backend, properties, authenticator, authProperties);
521 }
522
523
524 int SyncPage::nextId() const
525 {
526     if (!hasError) return -1;
527     return CoreConfigWizard::SyncRelayPage;
528 }
529
530
531 bool SyncPage::isComplete() const
532 {
533     return complete;
534 }
535
536
537 void SyncPage::setStatus(const QString &status)
538 {
539     ui.status->setText(status);
540 }
541
542
543 void SyncPage::setError(bool e)
544 {
545     hasError = e;
546 }
547
548
549 void SyncPage::setComplete(bool c)
550 {
551     complete = c;
552     completeChanged();
553 }
554
555
556 /*** Sync Relay Page ***/
557
558 SyncRelayPage::SyncRelayPage(QWidget *parent) : QWizardPage(parent)
559 {
560     mode = Success;
561 }
562
563
564 void SyncRelayPage::setMode(Mode m)
565 {
566     mode = m;
567 }
568
569
570 /*
571 void SyncRelayPage::initializePage() {
572   return;
573   if(mode == Success) {
574     wizard()->accept();
575   } else {
576     emit startOver();
577   }
578 }
579 */
580
581 int SyncRelayPage::nextId() const
582 {
583     emit startOver();
584     return 0;
585 }
586 };  /* namespace CoreConfigWizardPages */