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