Rename strict-ident, apply to non-identd response
[quassel.git] / src / core / core.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 <algorithm>
22
23 #include <QCoreApplication>
24
25 #include "core.h"
26 #include "coreauthhandler.h"
27 #include "coresession.h"
28 #include "coresettings.h"
29 #include "logger.h"
30 #include "internalpeer.h"
31 #include "network.h"
32 #include "postgresqlstorage.h"
33 #include "quassel.h"
34 #include "sqlauthenticator.h"
35 #include "sqlitestorage.h"
36 #include "util.h"
37
38 // Currently building with LDAP bindings is optional.
39 #ifdef HAVE_LDAP
40 #include "ldapauthenticator.h"
41 #endif
42
43 // migration related
44 #include <QFile>
45 #ifdef Q_OS_WIN
46 #  include <windows.h>
47 #else
48 #  include <unistd.h>
49 #  include <termios.h>
50 #endif /* Q_OS_WIN */
51
52 #ifdef HAVE_UMASK
53 #  include <sys/types.h>
54 #  include <sys/stat.h>
55 #endif /* HAVE_UMASK */
56
57 // ==============================
58 //  Custom Events
59 // ==============================
60 const int Core::AddClientEventId = QEvent::registerEventType();
61
62 class AddClientEvent : public QEvent
63 {
64 public:
65     AddClientEvent(RemotePeer *p, UserId uid) : QEvent(QEvent::Type(Core::AddClientEventId)), peer(p), userId(uid) {}
66     RemotePeer *peer;
67     UserId userId;
68 };
69
70
71 // ==============================
72 //  Core
73 // ==============================
74 Core *Core::instanceptr = 0;
75
76 Core *Core::instance()
77 {
78     if (instanceptr) return instanceptr;
79     instanceptr = new Core();
80     instanceptr->init();
81     return instanceptr;
82 }
83
84
85 void Core::destroy()
86 {
87     delete instanceptr;
88     instanceptr = 0;
89 }
90
91
92 Core::Core()
93 {
94 #ifdef HAVE_UMASK
95     umask(S_IRWXG | S_IRWXO);
96 #endif
97     _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
98
99     Quassel::loadTranslation(QLocale::system());
100
101     // FIXME: MIGRATION 0.3 -> 0.4: Move database and core config to new location
102     // Move settings, note this does not delete the old files
103 #ifdef Q_OS_MAC
104     QSettings newSettings("quassel-irc.org", "quasselcore");
105 #else
106
107 # ifdef Q_OS_WIN
108     QSettings::Format format = QSettings::IniFormat;
109 # else
110     QSettings::Format format = QSettings::NativeFormat;
111 # endif
112     QString newFilePath = Quassel::configDirPath() + "quasselcore"
113                           + ((format == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"));
114     QSettings newSettings(newFilePath, format);
115 #endif /* Q_OS_MAC */
116
117     if (newSettings.value("Config/Version").toUInt() == 0) {
118 #   ifdef Q_OS_MAC
119         QString org = "quassel-irc.org";
120 #   else
121         QString org = "Quassel Project";
122 #   endif
123         QSettings oldSettings(org, "Quassel Core");
124         if (oldSettings.allKeys().count()) {
125             quWarning() << "\n\n*** IMPORTANT: Config and data file locations have changed. Attempting to auto-migrate your core settings...";
126             foreach(QString key, oldSettings.allKeys())
127             newSettings.setValue(key, oldSettings.value(key));
128             newSettings.setValue("Config/Version", 1);
129             quWarning() << "*   Your core settings have been migrated to" << newSettings.fileName();
130
131 #ifndef Q_OS_MAC /* we don't need to move the db and cert for mac */
132 #ifdef Q_OS_WIN
133             QString quasselDir = qgetenv("APPDATA") + "/quassel/";
134 #elif defined Q_OS_MAC
135             QString quasselDir = QDir::homePath() + "/Library/Application Support/Quassel/";
136 #else
137             QString quasselDir = QDir::homePath() + "/.quassel/";
138 #endif
139
140             QFileInfo info(Quassel::configDirPath() + "quassel-storage.sqlite");
141             if (!info.exists()) {
142                 // move database, if we found it
143                 QFile oldDb(quasselDir + "quassel-storage.sqlite");
144                 if (oldDb.exists()) {
145                     bool success = oldDb.rename(Quassel::configDirPath() + "quassel-storage.sqlite");
146                     if (success)
147                         quWarning() << "*   Your database has been moved to" << Quassel::configDirPath() + "quassel-storage.sqlite";
148                     else
149                         quWarning() << "!!! Moving your database has failed. Please move it manually into" << Quassel::configDirPath();
150                 }
151             }
152             // move certificate
153             QFileInfo certInfo(quasselDir + "quasselCert.pem");
154             if (certInfo.exists()) {
155                 QFile cert(quasselDir + "quasselCert.pem");
156                 bool success = cert.rename(Quassel::configDirPath() + "quasselCert.pem");
157                 if (success)
158                     quWarning() << "*   Your certificate has been moved to" << Quassel::configDirPath() + "quasselCert.pem";
159                 else
160                     quWarning() << "!!! Moving your certificate has failed. Please move it manually into" << Quassel::configDirPath();
161             }
162 #endif /* !Q_OS_MAC */
163             quWarning() << "*** Migration completed.\n\n";
164         }
165     }
166     // MIGRATION end
167
168     // check settings version
169     // so far, we only have 1
170     CoreSettings s;
171     if (s.version() != 1) {
172         qCritical() << "Invalid core settings version, terminating!";
173         exit(EXIT_FAILURE);
174     }
175
176     // Set up storage and authentication backends
177     registerStorageBackends();
178     registerAuthenticators();
179
180     connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
181     _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes
182 }
183
184
185 void Core::init()
186 {
187     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
188     bool config_from_environment = Quassel::isOptionSet("config-from-environment");
189
190     QString db_backend;
191     QVariantMap db_connectionProperties;
192
193     QString auth_authenticator;
194     QVariantMap auth_properties;
195
196     bool writeError = false;
197
198     if (config_from_environment) {
199         db_backend = environment.value("DB_BACKEND");
200         auth_authenticator = environment.value("AUTH_AUTHENTICATOR");
201     } else {
202         CoreSettings cs;
203
204         QVariantMap dbsettings = cs.storageSettings().toMap();
205         db_backend = dbsettings.value("Backend").toString();
206         db_connectionProperties = dbsettings.value("ConnectionProperties").toMap();
207
208         QVariantMap authSettings = cs.authSettings().toMap();
209         auth_authenticator = authSettings.value("Authenticator", "Database").toString();
210         auth_properties = authSettings.value("AuthProperties").toMap();
211
212         writeError = !cs.isWritable();
213     }
214
215     // legacy
216     _configured = initStorage(db_backend, db_connectionProperties, environment, config_from_environment);
217
218     // Not entirely sure what is 'legacy' about the above, but it seems to be the way things work!
219     if (_configured) {
220         initAuthenticator(auth_authenticator, auth_properties, environment, config_from_environment);
221     }
222
223     if (Quassel::isOptionSet("select-backend") || Quassel::isOptionSet("select-authenticator")) {
224         if (Quassel::isOptionSet("select-backend")) {
225             selectBackend(Quassel::optionValue("select-backend"));
226         }
227         if (Quassel::isOptionSet("select-authenticator")) {
228             selectAuthenticator(Quassel::optionValue("select-authenticator"));
229         }
230         exit(EXIT_SUCCESS);
231     }
232
233     if (!_configured) {
234         if (config_from_environment) {
235             _configured = initStorage(db_backend, db_connectionProperties, environment, config_from_environment, true);
236             initAuthenticator(auth_authenticator, auth_properties, environment, config_from_environment, true);
237
238             if (!_configured) {
239                 qWarning() << "Cannot configure from environment";
240                 exit(EXIT_FAILURE);
241             }
242         } else {
243             if (_registeredStorageBackends.empty()) {
244                 quWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting..."));
245                 quWarning()
246                         << qPrintable(tr("Currently, Quassel supports SQLite3 and PostgreSQL. You need to build your\n"
247                                          "Qt library with the sqlite or postgres plugin enabled in order for quasselcore\n"
248                                          "to work."));
249                 exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup)
250             }
251             quWarning() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
252
253             if (writeError) {
254                 qWarning() << "Cannot write quasselcore configuration; probably a permission problem.";
255                 exit(EXIT_FAILURE);
256             }
257         }
258     }
259
260     if (Quassel::isOptionSet("add-user")) {
261         exit(createUser() ? EXIT_SUCCESS : EXIT_FAILURE);
262
263     }
264
265     if (Quassel::isOptionSet("change-userpass")) {
266         exit(changeUserPass(Quassel::optionValue("change-userpass")) ?
267                        EXIT_SUCCESS : EXIT_FAILURE);
268     }
269
270     connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
271     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
272     if (!startListening()) exit(1);  // TODO make this less brutal
273
274     _strictIdentEnabled = Quassel::isOptionSet("strict-ident");
275     if (_strictIdentEnabled) {
276         cacheSysIdent();
277     }
278
279     if (Quassel::isOptionSet("oidentd")) {
280         _oidentdConfigGenerator = new OidentdConfigGenerator(this);
281     }
282 }
283
284
285 Core::~Core()
286 {
287     // FIXME do we need more cleanup for handlers?
288     foreach(CoreAuthHandler *handler, _connectingClients) {
289         handler->deleteLater(); // disconnect non authed clients
290     }
291     qDeleteAll(_sessions);
292 }
293
294
295 /*** Session Restore ***/
296
297 void Core::saveState()
298 {
299     QVariantList activeSessions;
300     foreach(UserId user, instance()->_sessions.keys())
301         activeSessions << QVariant::fromValue<UserId>(user);
302     instance()->_storage->setCoreState(activeSessions);
303 }
304
305
306 void Core::restoreState()
307 {
308     if (!instance()->_configured) {
309         // quWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!"));
310         return;
311     }
312     if (instance()->_sessions.count()) {
313         quWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!"));
314         return;
315     }
316     CoreSettings s;
317     /* We don't check, since we are at the first version since switching to Git
318     uint statever = s.coreState().toMap()["CoreStateVersion"].toUInt();
319     if(statever < 1) {
320       quWarning() << qPrintable(tr("Core state too old, ignoring..."));
321       return;
322     }
323     */
324
325     const QList<QVariant> &activeSessionsFallback = s.coreState().toMap()["ActiveSessions"].toList();
326     QVariantList activeSessions = instance()->_storage->getCoreState(activeSessionsFallback);
327
328     if (activeSessions.count() > 0) {
329         quInfo() << "Restoring previous core state...";
330         foreach(QVariant v, activeSessions) {
331             UserId user = v.value<UserId>();
332             instance()->sessionForUser(user, true);
333         }
334     }
335 }
336
337
338 /*** Core Setup ***/
339
340 QString Core::setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authenticator, const QVariantMap &authSetupData)
341 {
342     return instance()->setupCore(adminUser, adminPassword, backend, setupData, authenticator, authSetupData);
343 }
344
345
346 QString Core::setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authenticator, const QVariantMap &authSetupData)
347 {
348     if (_configured)
349         return tr("Core is already configured! Not configuring again...");
350
351     if (adminUser.isEmpty() || adminPassword.isEmpty()) {
352         return tr("Admin user or password not set.");
353     }
354     if (!(_configured = initStorage(backend, setupData, {}, false, true))) {
355         return tr("Could not setup storage!");
356     }
357
358     quInfo() << "Selected authenticator:" << authenticator;
359     if (!(_configured = initAuthenticator(authenticator, authSetupData, {}, false, true)))
360     {
361         return tr("Could not setup authenticator!");
362     }
363
364     if (!saveBackendSettings(backend, setupData)) {
365         return tr("Could not save backend settings, probably a permission problem.");
366     }
367     saveAuthenticatorSettings(authenticator, authSetupData);
368
369     quInfo() << qPrintable(tr("Creating admin user..."));
370     _storage->addUser(adminUser, adminPassword);
371     cacheSysIdent();
372     startListening(); // TODO check when we need this
373     return QString();
374 }
375
376
377 QString Core::setupCoreForInternalUsage()
378 {
379     Q_ASSERT(!_registeredStorageBackends.empty());
380
381     qsrand(QDateTime::currentDateTime().toMSecsSinceEpoch());
382     int pass = 0;
383     for (int i = 0; i < 10; i++) {
384         pass *= 10;
385         pass += qrand() % 10;
386     }
387
388     // mono client currently needs sqlite
389     return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap(), "Database", QVariantMap());
390 }
391
392
393 /*** Storage Handling ***/
394
395 template<typename Storage>
396 void Core::registerStorageBackend()
397 {
398     auto backend = makeDeferredShared<Storage>(this);
399     if (backend->isAvailable())
400         _registeredStorageBackends.emplace_back(std::move(backend));
401     else
402         backend->deleteLater();
403 }
404
405
406 void Core::registerStorageBackends()
407 {
408     if (_registeredStorageBackends.empty()) {
409         registerStorageBackend<SqliteStorage>();
410         registerStorageBackend<PostgreSqlStorage>();
411     }
412 }
413
414
415 DeferredSharedPtr<Storage> Core::storageBackend(const QString &backendId) const
416 {
417     auto it = std::find_if(_registeredStorageBackends.begin(), _registeredStorageBackends.end(),
418                            [backendId](const DeferredSharedPtr<Storage> &backend) {
419                                return backend->displayName() == backendId;
420                            });
421     return it != _registeredStorageBackends.end() ? *it : nullptr;
422 }
423
424 // old db settings:
425 // "Type" => "sqlite"
426 bool Core::initStorage(const QString &backend, const QVariantMap &settings,
427                        const QProcessEnvironment &environment, bool loadFromEnvironment, bool setup)
428 {
429     if (backend.isEmpty()) {
430         quWarning() << "No storage backend selected!";
431         return false;
432     }
433
434     auto storage = storageBackend(backend);
435     if (!storage) {
436         qCritical() << "Selected storage backend is not available:" << backend;
437         return false;
438     }
439
440     Storage::State storageState = storage->init(settings, environment, loadFromEnvironment);
441     switch (storageState) {
442     case Storage::NeedsSetup:
443         if (!setup)
444             return false;  // trigger setup process
445         if (storage->setup(settings, environment, loadFromEnvironment))
446             return initStorage(backend, settings, environment, loadFromEnvironment, false);
447         return false;
448
449     // if initialization wasn't successful, we quit to keep from coming up unconfigured
450     case Storage::NotAvailable:
451         qCritical() << "FATAL: Selected storage backend is not available:" << backend;
452         if (!setup)
453             exit(EXIT_FAILURE);
454         return false;
455
456     case Storage::IsReady:
457         // delete all other backends
458         _registeredStorageBackends.clear();
459         connect(storage.get(), SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)),
460                 this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)));
461         break;
462     }
463     _storage = std::move(storage);
464     return true;
465 }
466
467
468 void Core::syncStorage()
469 {
470     if (_storage)
471         _storage->sync();
472 }
473
474
475 /*** Storage Access ***/
476 bool Core::createNetwork(UserId user, NetworkInfo &info)
477 {
478     NetworkId networkId = instance()->_storage->createNetwork(user, info);
479     if (!networkId.isValid())
480         return false;
481
482     info.networkId = networkId;
483     return true;
484 }
485
486
487 /*** Authenticators ***/
488
489 // Authentication handling, now independent from storage.
490 template<typename Authenticator>
491 void Core::registerAuthenticator()
492 {
493     auto authenticator = makeDeferredShared<Authenticator>(this);
494     if (authenticator->isAvailable())
495         _registeredAuthenticators.emplace_back(std::move(authenticator));
496     else
497         authenticator->deleteLater();
498 }
499
500
501 void Core::registerAuthenticators()
502 {
503     if (_registeredAuthenticators.empty()) {
504         registerAuthenticator<SqlAuthenticator>();
505 #ifdef HAVE_LDAP
506         registerAuthenticator<LdapAuthenticator>();
507 #endif
508     }
509 }
510
511
512 DeferredSharedPtr<Authenticator> Core::authenticator(const QString &backendId) const
513 {
514     auto it = std::find_if(_registeredAuthenticators.begin(), _registeredAuthenticators.end(),
515                            [backendId](const DeferredSharedPtr<Authenticator> &authenticator) {
516                                return authenticator->backendId() == backendId;
517                            });
518     return it != _registeredAuthenticators.end() ? *it : nullptr;
519 }
520
521
522 // FIXME: Apparently, this is the legacy way of initting storage backends?
523 // If there's a not-legacy way, it should be used here
524 bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings,
525                              const QProcessEnvironment &environment, bool loadFromEnvironment,
526                              bool setup)
527 {
528     if (backend.isEmpty()) {
529         quWarning() << "No authenticator selected!";
530         return false;
531     }
532
533     auto auth = authenticator(backend);
534     if (!auth) {
535         qCritical() << "Selected auth backend is not available:" << backend;
536         return false;
537     }
538
539     Authenticator::State authState = auth->init(settings, environment, loadFromEnvironment);
540     switch (authState) {
541     case Authenticator::NeedsSetup:
542         if (!setup)
543             return false;  // trigger setup process
544         if (auth->setup(settings, environment, loadFromEnvironment))
545             return initAuthenticator(backend, settings, environment, loadFromEnvironment, false);
546         return false;
547
548     // if initialization wasn't successful, we quit to keep from coming up unconfigured
549     case Authenticator::NotAvailable:
550         qCritical() << "FATAL: Selected auth backend is not available:" << backend;
551         if (!setup)
552             exit(EXIT_FAILURE);
553         return false;
554
555     case Authenticator::IsReady:
556         // delete all other backends
557         _registeredAuthenticators.clear();
558         break;
559     }
560     _authenticator = std::move(auth);
561     return true;
562 }
563
564
565 /*** Network Management ***/
566
567 bool Core::sslSupported()
568 {
569 #ifdef HAVE_SSL
570     SslServer *sslServer = qobject_cast<SslServer *>(&instance()->_server);
571     return sslServer && sslServer->isCertValid();
572 #else
573     return false;
574 #endif
575 }
576
577
578 bool Core::reloadCerts()
579 {
580 #ifdef HAVE_SSL
581     SslServer *sslServerv4 = qobject_cast<SslServer *>(&instance()->_server);
582     bool retv4 = sslServerv4->reloadCerts();
583
584     SslServer *sslServerv6 = qobject_cast<SslServer *>(&instance()->_v6server);
585     bool retv6 = sslServerv6->reloadCerts();
586
587     return retv4 && retv6;
588 #else
589     // SSL not supported, don't mark configuration reload as failed
590     return true;
591 #endif
592 }
593
594
595 void Core::cacheSysIdent()
596 {
597     if (isConfigured()) {
598         instance()->_authUserNames = instance()->_storage->getAllAuthUserNames();
599     }
600 }
601
602
603 QString Core::strictSysIdent(UserId user) const
604 {
605     if (_authUserNames.contains(user)) {
606         return _authUserNames[user];
607     }
608
609     // A new user got added since we last pulled our cache from the database.
610     // There's no way to avoid a database hit - we don't even know the authname!
611     cacheSysIdent();
612
613     if (_authUserNames.contains(user)) {
614         return _authUserNames[user];
615     }
616
617     // ...something very weird is going on if we ended up here (an active CoreSession without a corresponding database entry?)
618     qWarning().nospace() << "Unable to find authusername for UserId " << user << ", this should never happen!";
619     return "unknown"; // Should we just terminate the program instead?
620 }
621
622
623 bool Core::startListening()
624 {
625     // in mono mode we only start a local port if a port is specified in the cli call
626     if (Quassel::runMode() == Quassel::Monolithic && !Quassel::isOptionSet("port"))
627         return true;
628
629     bool success = false;
630     uint port = Quassel::optionValue("port").toUInt();
631
632     const QString listen = Quassel::optionValue("listen");
633     const QStringList listen_list = listen.split(",", QString::SkipEmptyParts);
634     if (listen_list.size() > 0) {
635         foreach(const QString listen_term, listen_list) { // TODO: handle multiple interfaces for same TCP version gracefully
636             QHostAddress addr;
637             if (!addr.setAddress(listen_term)) {
638                 qCritical() << qPrintable(
639                     tr("Invalid listen address %1")
640                     .arg(listen_term)
641                     );
642             }
643             else {
644                 switch (addr.protocol()) {
645                 case QAbstractSocket::IPv6Protocol:
646                     if (_v6server.listen(addr, port)) {
647                         quInfo() << qPrintable(
648                             tr("Listening for GUI clients on IPv6 %1 port %2 using protocol version %3")
649                             .arg(addr.toString())
650                             .arg(_v6server.serverPort())
651                             .arg(Quassel::buildInfo().protocolVersion)
652                             );
653                         success = true;
654                     }
655                     else
656                         quWarning() << qPrintable(
657                             tr("Could not open IPv6 interface %1:%2: %3")
658                             .arg(addr.toString())
659                             .arg(port)
660                             .arg(_v6server.errorString()));
661                     break;
662                 case QAbstractSocket::IPv4Protocol:
663                     if (_server.listen(addr, port)) {
664                         quInfo() << qPrintable(
665                             tr("Listening for GUI clients on IPv4 %1 port %2 using protocol version %3")
666                             .arg(addr.toString())
667                             .arg(_server.serverPort())
668                             .arg(Quassel::buildInfo().protocolVersion)
669                             );
670                         success = true;
671                     }
672                     else {
673                         // if v6 succeeded on Any, the port will be already in use - don't display the error then
674                         if (!success || _server.serverError() != QAbstractSocket::AddressInUseError)
675                             quWarning() << qPrintable(
676                                 tr("Could not open IPv4 interface %1:%2: %3")
677                                 .arg(addr.toString())
678                                 .arg(port)
679                                 .arg(_server.errorString()));
680                     }
681                     break;
682                 default:
683                     qCritical() << qPrintable(
684                         tr("Invalid listen address %1, unknown network protocol")
685                         .arg(listen_term)
686                         );
687                     break;
688                 }
689             }
690         }
691     }
692     if (!success)
693         quError() << qPrintable(tr("Could not open any network interfaces to listen on!"));
694
695     return success;
696 }
697
698
699 void Core::stopListening(const QString &reason)
700 {
701     bool wasListening = false;
702     if (_server.isListening()) {
703         wasListening = true;
704         _server.close();
705     }
706     if (_v6server.isListening()) {
707         wasListening = true;
708         _v6server.close();
709     }
710     if (wasListening) {
711         if (reason.isEmpty())
712             quInfo() << "No longer listening for GUI clients.";
713         else
714             quInfo() << qPrintable(reason);
715     }
716 }
717
718
719 void Core::incomingConnection()
720 {
721     QTcpServer *server = qobject_cast<QTcpServer *>(sender());
722     Q_ASSERT(server);
723     while (server->hasPendingConnections()) {
724         QTcpSocket *socket = server->nextPendingConnection();
725
726         CoreAuthHandler *handler = new CoreAuthHandler(socket, this);
727         _connectingClients.insert(handler);
728
729         connect(handler, SIGNAL(disconnected()), SLOT(clientDisconnected()));
730         connect(handler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(socketError(QAbstractSocket::SocketError,QString)));
731         connect(handler, SIGNAL(handshakeComplete(RemotePeer*,UserId)), SLOT(setupClientSession(RemotePeer*,UserId)));
732
733         quInfo() << qPrintable(tr("Client connected from"))  << qPrintable(socket->peerAddress().toString());
734
735         if (!_configured) {
736             stopListening(tr("Closing server for basic setup."));
737         }
738     }
739 }
740
741
742 // Potentially called during the initialization phase (before handing the connection off to the session)
743 void Core::clientDisconnected()
744 {
745     CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
746     Q_ASSERT(handler);
747
748     quInfo() << qPrintable(tr("Non-authed client disconnected:")) << qPrintable(handler->socket()->peerAddress().toString());
749     _connectingClients.remove(handler);
750     handler->deleteLater();
751
752     // make server listen again if still not configured
753     if (!_configured) {
754         startListening();
755     }
756
757     // TODO remove unneeded sessions - if necessary/possible...
758     // Suggestion: kill sessions if they are not connected to any network and client.
759 }
760
761
762 void Core::setupClientSession(RemotePeer *peer, UserId uid)
763 {
764     CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
765     Q_ASSERT(handler);
766
767     // From now on everything is handled by the client session
768     disconnect(handler, 0, this, 0);
769     _connectingClients.remove(handler);
770     handler->deleteLater();
771
772     // Find or create session for validated user
773     sessionForUser(uid);
774
775     // as we are currently handling an event triggered by incoming data on this socket
776     // it is unsafe to directly move the socket to the client thread.
777     QCoreApplication::postEvent(this, new AddClientEvent(peer, uid));
778 }
779
780
781 void Core::customEvent(QEvent *event)
782 {
783     if (event->type() == AddClientEventId) {
784         AddClientEvent *addClientEvent = static_cast<AddClientEvent *>(event);
785         addClientHelper(addClientEvent->peer, addClientEvent->userId);
786         return;
787     }
788 }
789
790
791 void Core::addClientHelper(RemotePeer *peer, UserId uid)
792 {
793     // Find or create session for validated user
794     SessionThread *session = sessionForUser(uid);
795     session->addClient(peer);
796 }
797
798
799 void Core::setupInternalClientSession(InternalPeer *clientPeer)
800 {
801     if (!_configured) {
802         stopListening();
803         setupCoreForInternalUsage();
804     }
805
806     UserId uid;
807     if (_storage) {
808         uid = _storage->internalUser();
809     }
810     else {
811         quWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!";
812         return;
813     }
814
815     InternalPeer *corePeer = new InternalPeer(this);
816     corePeer->setPeer(clientPeer);
817     clientPeer->setPeer(corePeer);
818
819     // Find or create session for validated user
820     SessionThread *sessionThread = sessionForUser(uid);
821     sessionThread->addClient(corePeer);
822 }
823
824
825 SessionThread *Core::sessionForUser(UserId uid, bool restore)
826 {
827     if (_sessions.contains(uid))
828         return _sessions[uid];
829
830     SessionThread *session = new SessionThread(uid, restore, strictIdentEnabled(), this);
831     _sessions[uid] = session;
832     session->start();
833     return session;
834 }
835
836
837 void Core::socketError(QAbstractSocket::SocketError err, const QString &errorString)
838 {
839     quWarning() << QString("Socket error %1: %2").arg(err).arg(errorString);
840 }
841
842
843 QVariantList Core::backendInfo()
844 {
845     instance()->registerStorageBackends();
846
847     QVariantList backendInfos;
848     for (auto &&backend : instance()->_registeredStorageBackends) {
849         QVariantMap v;
850         v["BackendId"]   = backend->backendId();
851         v["DisplayName"] = backend->displayName();
852         v["Description"] = backend->description();
853         v["SetupData"]   = backend->setupData(); // ignored by legacy clients
854
855         // TODO Protocol Break: Remove legacy (cf. authenticatorInfo())
856         const auto &setupData = backend->setupData();
857         QStringList setupKeys;
858         QVariantMap setupDefaults;
859         for (int i = 0; i + 2 < setupData.size(); i += 3) {
860             setupKeys << setupData[i].toString();
861             setupDefaults[setupData[i].toString()] = setupData[i + 2];
862         }
863         v["SetupKeys"]     = setupKeys;
864         v["SetupDefaults"] = setupDefaults;
865         // TODO Protocol Break: Remove
866         v["IsDefault"]     = (backend->backendId() == "SQLite"); // newer clients will just use the first in the list
867
868         backendInfos << v;
869     }
870     return backendInfos;
871 }
872
873
874 QVariantList Core::authenticatorInfo()
875 {
876     instance()->registerAuthenticators();
877
878     QVariantList authInfos;
879     for(auto &&backend : instance()->_registeredAuthenticators) {
880         QVariantMap v;
881         v["BackendId"]   = backend->backendId();
882         v["DisplayName"] = backend->displayName();
883         v["Description"] = backend->description();
884         v["SetupData"]   = backend->setupData();
885         authInfos << v;
886     }
887     return authInfos;
888 }
889
890 // migration / backend selection
891 bool Core::selectBackend(const QString &backend)
892 {
893     // reregister all storage backends
894     registerStorageBackends();
895     auto storage = storageBackend(backend);
896     if (!storage) {
897         QStringList backends;
898         std::transform(_registeredStorageBackends.begin(), _registeredStorageBackends.end(),
899                        std::back_inserter(backends), [](const DeferredSharedPtr<Storage>& backend) {
900                            return backend->displayName();
901                        });
902         quWarning() << qPrintable(tr("Unsupported storage backend: %1").arg(backend));
903         quWarning() << qPrintable(tr("Supported backends are:")) << qPrintable(backends.join(", "));
904         return false;
905     }
906
907     QVariantMap settings = promptForSettings(storage.get());
908
909     Storage::State storageState = storage->init(settings);
910     switch (storageState) {
911     case Storage::IsReady:
912         if (!saveBackendSettings(backend, settings)) {
913             qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
914         }
915         quWarning() << qPrintable(tr("Switched storage backend to: %1").arg(backend));
916         quWarning() << qPrintable(tr("Backend already initialized. Skipping Migration..."));
917         return true;
918     case Storage::NotAvailable:
919         qCritical() << qPrintable(tr("Storage backend is not available: %1").arg(backend));
920         return false;
921     case Storage::NeedsSetup:
922         if (!storage->setup(settings)) {
923             quWarning() << qPrintable(tr("Unable to setup storage backend: %1").arg(backend));
924             return false;
925         }
926
927         if (storage->init(settings) != Storage::IsReady) {
928             quWarning() << qPrintable(tr("Unable to initialize storage backend: %1").arg(backend));
929             return false;
930         }
931
932         if (!saveBackendSettings(backend, settings)) {
933             qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
934         }
935         quWarning() << qPrintable(tr("Switched storage backend to: %1").arg(backend));
936         break;
937     }
938
939     // let's see if we have a current storage object we can migrate from
940     auto reader = getMigrationReader(_storage.get());
941     auto writer = getMigrationWriter(storage.get());
942     if (reader && writer) {
943         qDebug() << qPrintable(tr("Migrating storage backend %1 to %2...").arg(_storage->displayName(), storage->displayName()));
944         _storage.reset();
945         storage.reset();
946         if (reader->migrateTo(writer.get())) {
947             qDebug() << "Migration finished!";
948             qDebug() << qPrintable(tr("Migration finished!"));
949             if (!saveBackendSettings(backend, settings)) {
950                 qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
951                 return false;
952             }
953             return true;
954         }
955         quWarning() << qPrintable(tr("Unable to migrate storage backend! (No migration writer for %1)").arg(backend));
956         return false;
957     }
958
959     // inform the user why we cannot merge
960     if (!_storage) {
961         quWarning() << qPrintable(tr("No currently active storage backend. Skipping migration..."));
962     }
963     else if (!reader) {
964         quWarning() << qPrintable(tr("Currently active storage backend does not support migration: %1").arg(_storage->displayName()));
965     }
966     if (writer) {
967         quWarning() << qPrintable(tr("New storage backend does not support migration: %1").arg(backend));
968     }
969
970     // so we were unable to merge, but let's create a user \o/
971     _storage = std::move(storage);
972     createUser();
973     return true;
974 }
975
976 // TODO: I am not sure if this function is implemented correctly.
977 // There is currently no concept of migraiton between auth backends.
978 bool Core::selectAuthenticator(const QString &backend)
979 {
980     // Register all authentication backends.
981     registerAuthenticators();
982     auto auther = authenticator(backend);
983     if (!auther) {
984         QStringList authenticators;
985         std::transform(_registeredAuthenticators.begin(), _registeredAuthenticators.end(),
986                        std::back_inserter(authenticators), [](const DeferredSharedPtr<Authenticator>& authenticator) {
987                            return authenticator->displayName();
988                        });
989         quWarning() << qPrintable(tr("Unsupported authenticator: %1").arg(backend));
990         quWarning() << qPrintable(tr("Supported authenticators are:")) << qPrintable(authenticators.join(", "));
991         return false;
992     }
993
994     QVariantMap settings = promptForSettings(auther.get());
995
996     Authenticator::State state = auther->init(settings);
997     switch (state) {
998     case Authenticator::IsReady:
999         saveAuthenticatorSettings(backend, settings);
1000         quWarning() << qPrintable(tr("Switched authenticator to: %1").arg(backend));
1001         return true;
1002     case Authenticator::NotAvailable:
1003         qCritical() << qPrintable(tr("Authenticator is not available: %1").arg(backend));
1004         return false;
1005     case Authenticator::NeedsSetup:
1006         if (!auther->setup(settings)) {
1007             quWarning() << qPrintable(tr("Unable to setup authenticator: %1").arg(backend));
1008             return false;
1009         }
1010
1011         if (auther->init(settings) != Authenticator::IsReady) {
1012             quWarning() << qPrintable(tr("Unable to initialize authenticator: %1").arg(backend));
1013             return false;
1014         }
1015
1016         saveAuthenticatorSettings(backend, settings);
1017         quWarning() << qPrintable(tr("Switched authenticator to: %1").arg(backend));
1018     }
1019
1020     _authenticator = std::move(auther);
1021     return true;
1022 }
1023
1024
1025 bool Core::createUser()
1026 {
1027     QTextStream out(stdout);
1028     QTextStream in(stdin);
1029     out << "Add a new user:" << endl;
1030     out << "Username: ";
1031     out.flush();
1032     QString username = in.readLine().trimmed();
1033
1034     disableStdInEcho();
1035     out << "Password: ";
1036     out.flush();
1037     QString password = in.readLine().trimmed();
1038     out << endl;
1039     out << "Repeat Password: ";
1040     out.flush();
1041     QString password2 = in.readLine().trimmed();
1042     out << endl;
1043     enableStdInEcho();
1044
1045     if (password != password2) {
1046         quWarning() << "Passwords don't match!";
1047         return false;
1048     }
1049     if (password.isEmpty()) {
1050         quWarning() << "Password is empty!";
1051         return false;
1052     }
1053
1054     if (_configured && _storage->addUser(username, password).isValid()) {
1055         out << "Added user " << username << " successfully!" << endl;
1056         return true;
1057     }
1058     else {
1059         quWarning() << "Unable to add user:" << qPrintable(username);
1060         return false;
1061     }
1062 }
1063
1064
1065 bool Core::changeUserPass(const QString &username)
1066 {
1067     QTextStream out(stdout);
1068     QTextStream in(stdin);
1069     UserId userId = _storage->getUserId(username);
1070     if (!userId.isValid()) {
1071         out << "User " << username << " does not exist." << endl;
1072         return false;
1073     }
1074
1075     if (!canChangeUserPassword(userId)) {
1076         out << "User " << username << " is configured through an auth provider that has forbidden manual password changing." << endl;
1077         return false;
1078     }
1079
1080     out << "Change password for user: " << username << endl;
1081
1082     disableStdInEcho();
1083     out << "New Password: ";
1084     out.flush();
1085     QString password = in.readLine().trimmed();
1086     out << endl;
1087     out << "Repeat Password: ";
1088     out.flush();
1089     QString password2 = in.readLine().trimmed();
1090     out << endl;
1091     enableStdInEcho();
1092
1093     if (password != password2) {
1094         quWarning() << "Passwords don't match!";
1095         return false;
1096     }
1097     if (password.isEmpty()) {
1098         quWarning() << "Password is empty!";
1099         return false;
1100     }
1101
1102     if (_configured && _storage->updateUser(userId, password)) {
1103         out << "Password changed successfully!" << endl;
1104         return true;
1105     }
1106     else {
1107         quWarning() << "Failed to change password!";
1108         return false;
1109     }
1110 }
1111
1112
1113 bool Core::changeUserPassword(UserId userId, const QString &password)
1114 {
1115     if (!isConfigured() || !userId.isValid())
1116         return false;
1117
1118     if (!canChangeUserPassword(userId))
1119         return false;
1120
1121     return instance()->_storage->updateUser(userId, password);
1122 }
1123
1124 // TODO: this code isn't currently 100% optimal because the core
1125 // doesn't know it can have multiple auth providers configured (there aren't
1126 // multiple auth providers at the moment anyway) and we have hardcoded the
1127 // Database provider to be always allowed.
1128 bool Core::canChangeUserPassword(UserId userId)
1129 {
1130     QString authProvider = instance()->_storage->getUserAuthenticator(userId);
1131     if (authProvider != "Database") {
1132         if (authProvider != instance()->_authenticator->backendId()) {
1133             return false;
1134         }
1135         else if (instance()->_authenticator->canChangePassword()) {
1136             return false;
1137         }
1138     }
1139     return true;
1140 }
1141
1142
1143 std::unique_ptr<AbstractSqlMigrationReader> Core::getMigrationReader(Storage *storage)
1144 {
1145     if (!storage)
1146         return nullptr;
1147
1148     AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
1149     if (!sqlStorage) {
1150         qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
1151         return nullptr;
1152     }
1153
1154     return sqlStorage->createMigrationReader();
1155 }
1156
1157
1158 std::unique_ptr<AbstractSqlMigrationWriter> Core::getMigrationWriter(Storage *storage)
1159 {
1160     if (!storage)
1161         return nullptr;
1162
1163     AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
1164     if (!sqlStorage) {
1165         qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
1166         return nullptr;
1167     }
1168
1169     return sqlStorage->createMigrationWriter();
1170 }
1171
1172
1173 bool Core::saveBackendSettings(const QString &backend, const QVariantMap &settings)
1174 {
1175     QVariantMap dbsettings;
1176     dbsettings["Backend"] = backend;
1177     dbsettings["ConnectionProperties"] = settings;
1178     CoreSettings s = CoreSettings();
1179     s.setStorageSettings(dbsettings);
1180     return s.sync();
1181 }
1182
1183
1184 void Core::saveAuthenticatorSettings(const QString &backend, const QVariantMap &settings)
1185 {
1186     QVariantMap dbsettings;
1187     dbsettings["Authenticator"] = backend;
1188     dbsettings["AuthProperties"] = settings;
1189     CoreSettings().setAuthSettings(dbsettings);
1190 }
1191
1192 // Generic version of promptForSettings that doesn't care what *type* of
1193 // backend it runs over.
1194 template<typename Backend>
1195 QVariantMap Core::promptForSettings(const Backend *backend)
1196 {
1197     QVariantMap settings;
1198     const QVariantList& setupData = backend->setupData();
1199
1200     if (setupData.isEmpty())
1201         return settings;
1202
1203     QTextStream out(stdout);
1204     QTextStream in(stdin);
1205     out << "Default values are in brackets" << endl;
1206
1207     for (int i = 0; i + 2 < setupData.size(); i += 3) {
1208         QString key = setupData[i].toString();
1209         out << setupData[i+1].toString() << " [" << setupData[i+2].toString() << "]: " << flush;
1210
1211         bool noEcho = key.toLower().contains("password");
1212         if (noEcho) {
1213             disableStdInEcho();
1214         }
1215         QString input = in.readLine().trimmed();
1216         if (noEcho) {
1217             out << endl;
1218             enableStdInEcho();
1219         }
1220
1221         QVariant value{setupData[i+2]};
1222         if (!input.isEmpty()) {
1223             switch (value.type()) {
1224             case QVariant::Int:
1225                 value = input.toInt();
1226                 break;
1227             default:
1228                 value = input;
1229             }
1230         }
1231         settings[key] = value;
1232     }
1233     return settings;
1234 }
1235
1236
1237 #ifdef Q_OS_WIN
1238 void Core::stdInEcho(bool on)
1239 {
1240     HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
1241     DWORD mode = 0;
1242     GetConsoleMode(hStdin, &mode);
1243     if (on)
1244         mode |= ENABLE_ECHO_INPUT;
1245     else
1246         mode &= ~ENABLE_ECHO_INPUT;
1247     SetConsoleMode(hStdin, mode);
1248 }
1249
1250 #else
1251 void Core::stdInEcho(bool on)
1252 {
1253     termios t;
1254     tcgetattr(STDIN_FILENO, &t);
1255     if (on)
1256         t.c_lflag |= ECHO;
1257     else
1258         t.c_lflag &= ~ECHO;
1259     tcsetattr(STDIN_FILENO, TCSANOW, &t);
1260 }
1261
1262 #endif /* Q_OS_WIN */