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