66e2e62ab1609bc0b8dc21e515e3284c4759625b
[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     auto* sslServer = qobject_cast<SslServer*>(&instance()->_server);
576     return sslServer && sslServer->isCertValid();
577 }
578
579 bool Core::reloadCerts()
580 {
581     auto* sslServerv4 = qobject_cast<SslServer*>(&_server);
582     bool retv4 = sslServerv4->reloadCerts();
583
584     auto* sslServerv6 = qobject_cast<SslServer*>(&_v6server);
585     bool retv6 = sslServerv6->reloadCerts();
586
587     return retv4 && retv6;
588 }
589
590 void Core::cacheSysIdent()
591 {
592     if (isConfigured()) {
593         _authUserNames = _storage->getAllAuthUserNames();
594     }
595 }
596
597 QString Core::strictSysIdent(UserId user) const
598 {
599     if (_authUserNames.contains(user)) {
600         return _authUserNames[user];
601     }
602
603     // A new user got added since we last pulled our cache from the database.
604     // There's no way to avoid a database hit - we don't even know the authname!
605     instance()->cacheSysIdent();
606
607     if (_authUserNames.contains(user)) {
608         return _authUserNames[user];
609     }
610
611     // ...something very weird is going on if we ended up here (an active CoreSession without a corresponding database entry?)
612     qWarning().nospace() << "Unable to find authusername for UserId " << user << ", this should never happen!";
613     return "unknown";  // Should we just terminate the program instead?
614 }
615
616 bool Core::startListening()
617 {
618     // in mono mode we only start a local port if a port is specified in the cli call
619     if (Quassel::runMode() == Quassel::Monolithic && !Quassel::isOptionSet("port"))
620         return true;
621
622     bool success = false;
623     uint port = Quassel::optionValue("port").toUInt();
624
625     const QString listen = Quassel::optionValue("listen");
626     const QStringList listen_list = listen.split(",", QString::SkipEmptyParts);
627     if (listen_list.size() > 0) {
628         foreach (const QString listen_term, listen_list) {  // TODO: handle multiple interfaces for same TCP version gracefully
629             QHostAddress addr;
630             if (!addr.setAddress(listen_term)) {
631                 qCritical() << qPrintable(tr("Invalid listen address %1").arg(listen_term));
632             }
633             else {
634                 switch (addr.protocol()) {
635                 case QAbstractSocket::IPv6Protocol:
636                     if (_v6server.listen(addr, port)) {
637                         qInfo() << qPrintable(tr("Listening for GUI clients on IPv6 %1 port %2 using protocol version %3")
638                                               .arg(addr.toString())
639                                               .arg(_v6server.serverPort())
640                                               .arg(Quassel::buildInfo().protocolVersion));
641                         success = true;
642                     }
643                     else
644                         qWarning() << qPrintable(tr("Could not open IPv6 interface %1:%2: %3").arg(addr.toString()).arg(port).arg(_v6server.errorString()));
645                     break;
646                 case QAbstractSocket::IPv4Protocol:
647                     if (_server.listen(addr, port)) {
648                         qInfo() << qPrintable(tr("Listening for GUI clients on IPv4 %1 port %2 using protocol version %3")
649                                               .arg(addr.toString())
650                                               .arg(_server.serverPort())
651                                               .arg(Quassel::buildInfo().protocolVersion));
652                         success = true;
653                     }
654                     else {
655                         // if v6 succeeded on Any, the port will be already in use - don't display the error then
656                         if (!success || _server.serverError() != QAbstractSocket::AddressInUseError)
657                             qWarning() << qPrintable(tr("Could not open IPv4 interface %1:%2: %3").arg(addr.toString()).arg(port).arg(_server.errorString()));
658                     }
659                     break;
660                 default:
661                     qCritical() << qPrintable(tr("Invalid listen address %1, unknown network protocol").arg(listen_term));
662                     break;
663                 }
664             }
665         }
666     }
667     if (!success)
668         qCritical() << qPrintable(tr("Could not open any network interfaces to listen on!"));
669
670     if (_identServer) {
671         _identServer->startListening();
672     }
673
674     if (_metricsServer) {
675         _metricsServer->startListening();
676     }
677
678     return success;
679 }
680
681 void Core::stopListening(const QString& reason)
682 {
683     if (_identServer) {
684         _identServer->stopListening(reason);
685     }
686
687     if (_metricsServer) {
688         _metricsServer->stopListening(reason);
689     }
690
691     bool wasListening = false;
692     if (_server.isListening()) {
693         wasListening = true;
694         _server.close();
695     }
696     if (_v6server.isListening()) {
697         wasListening = true;
698         _v6server.close();
699     }
700     if (wasListening) {
701         if (reason.isEmpty())
702             qInfo() << "No longer listening for GUI clients.";
703         else
704             qInfo() << qPrintable(reason);
705     }
706 }
707
708 void Core::incomingConnection()
709 {
710     auto* server = qobject_cast<QTcpServer*>(sender());
711     Q_ASSERT(server);
712     while (server->hasPendingConnections()) {
713         QTcpSocket* socket = server->nextPendingConnection();
714
715         auto* handler = new CoreAuthHandler(socket, this);
716         _connectingClients.insert(handler);
717
718         connect(handler, &AuthHandler::disconnected, this, &Core::clientDisconnected);
719         connect(handler, &AuthHandler::socketError, this, &Core::socketError);
720         connect(handler, &CoreAuthHandler::handshakeComplete, this, &Core::setupClientSession);
721
722         qInfo() << qPrintable(tr("Client connected from")) << qPrintable(handler->hostAddress().toString());
723
724         if (!_configured) {
725             stopListening(tr("Closing server for basic setup."));
726         }
727     }
728 }
729
730 // Potentially called during the initialization phase (before handing the connection off to the session)
731 void Core::clientDisconnected()
732 {
733     auto* handler = qobject_cast<CoreAuthHandler*>(sender());
734     Q_ASSERT(handler);
735
736     qInfo() << qPrintable(tr("Non-authed client disconnected:")) << qPrintable(handler->hostAddress().toString());
737     _connectingClients.remove(handler);
738     handler->deleteLater();
739
740     // make server listen again if still not configured
741     if (!_configured) {
742         startListening();
743     }
744
745     // TODO remove unneeded sessions - if necessary/possible...
746     // Suggestion: kill sessions if they are not connected to any network and client.
747 }
748
749 void Core::setupClientSession(RemotePeer* peer, UserId uid)
750 {
751     auto* handler = qobject_cast<CoreAuthHandler*>(sender());
752     Q_ASSERT(handler);
753
754     // From now on everything is handled by the client session
755     disconnect(handler, nullptr, this, nullptr);
756     _connectingClients.remove(handler);
757     handler->deleteLater();
758
759     // Find or create session for validated user
760     sessionForUser(uid);
761
762     // as we are currently handling an event triggered by incoming data on this socket
763     // it is unsafe to directly move the socket to the client thread.
764     QCoreApplication::postEvent(this, new AddClientEvent(peer, uid));
765 }
766
767 void Core::customEvent(QEvent* event)
768 {
769     if (event->type() == AddClientEventId) {
770         auto* addClientEvent = static_cast<AddClientEvent*>(event);
771         addClientHelper(addClientEvent->peer, addClientEvent->userId);
772         return;
773     }
774 }
775
776 void Core::addClientHelper(RemotePeer* peer, UserId uid)
777 {
778     // Find or create session for validated user
779     SessionThread* session = sessionForUser(uid);
780     session->addClient(peer);
781 }
782
783 void Core::connectInternalPeer(QPointer<InternalPeer> peer)
784 {
785     if (_initialized && peer) {
786         setupInternalClientSession(peer);
787     }
788     else {
789         _pendingInternalConnection = peer;
790     }
791 }
792
793 void Core::setupInternalClientSession(QPointer<InternalPeer> clientPeer)
794 {
795     if (!_configured) {
796         stopListening();
797         auto errorString = setupCoreForInternalUsage();
798         if (!errorString.isEmpty()) {
799             emit exitRequested(EXIT_FAILURE, errorString);
800             return;
801         }
802     }
803
804     UserId uid;
805     if (_storage) {
806         uid = _storage->internalUser();
807     }
808     else {
809         qWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!";
810         emit exitRequested(EXIT_FAILURE, tr("Cannot setup storage backend."));
811         return;
812     }
813
814     if (!clientPeer) {
815         qWarning() << "Client peer went away, not starting a session";
816         return;
817     }
818
819     auto* corePeer = new InternalPeer(this);
820     corePeer->setPeer(clientPeer);
821     clientPeer->setPeer(corePeer);
822
823     // Find or create session for validated user
824     SessionThread* sessionThread = sessionForUser(uid);
825     sessionThread->addClient(corePeer);
826 }
827
828 SessionThread* Core::sessionForUser(UserId uid, bool restore)
829 {
830     if (_sessions.contains(uid))
831         return _sessions[uid];
832
833     return (_sessions[uid] = new SessionThread(uid, restore, strictIdentEnabled(), this));
834 }
835
836 void Core::socketError(QAbstractSocket::SocketError err, const QString& errorString)
837 {
838     qWarning() << QString("Socket error %1: %2").arg(err).arg(errorString);
839 }
840
841 QVariantList Core::backendInfo()
842 {
843     instance()->registerStorageBackends();
844
845     QVariantList backendInfos;
846     for (auto&& backend : instance()->_registeredStorageBackends) {
847         QVariantMap v;
848         v["BackendId"] = backend->backendId();
849         v["DisplayName"] = backend->displayName();
850         v["Description"] = backend->description();
851         v["SetupData"] = backend->setupData();  // ignored by legacy clients
852
853         // TODO Protocol Break: Remove legacy (cf. authenticatorInfo())
854         const auto& setupData = backend->setupData();
855         QStringList setupKeys;
856         QVariantMap setupDefaults;
857         for (int i = 0; i + 2 < setupData.size(); i += 3) {
858             setupKeys << setupData[i].toString();
859             setupDefaults[setupData[i].toString()] = setupData[i + 2];
860         }
861         v["SetupKeys"] = setupKeys;
862         v["SetupDefaults"] = setupDefaults;
863         // TODO Protocol Break: Remove
864         v["IsDefault"] = (backend->backendId() == "SQLite");  // newer clients will just use the first in the list
865
866         backendInfos << v;
867     }
868     return backendInfos;
869 }
870
871 QVariantList Core::authenticatorInfo()
872 {
873     instance()->registerAuthenticators();
874
875     QVariantList authInfos;
876     for (auto&& backend : instance()->_registeredAuthenticators) {
877         QVariantMap v;
878         v["BackendId"] = backend->backendId();
879         v["DisplayName"] = backend->displayName();
880         v["Description"] = backend->description();
881         v["SetupData"] = backend->setupData();
882         authInfos << v;
883     }
884     return authInfos;
885 }
886
887 // migration / backend selection
888 bool Core::selectBackend(const QString& backend)
889 {
890     // reregister all storage backends
891     registerStorageBackends();
892     auto storage = storageBackend(backend);
893     if (!storage) {
894         QStringList backends;
895         std::transform(_registeredStorageBackends.begin(),
896                        _registeredStorageBackends.end(),
897                        std::back_inserter(backends),
898                        [](const DeferredSharedPtr<Storage>& backend) { return backend->displayName(); });
899         qWarning() << qPrintable(tr("Unsupported storage backend: %1").arg(backend));
900         qWarning() << qPrintable(tr("Supported backends are:")) << qPrintable(backends.join(", "));
901         return false;
902     }
903
904     QVariantMap settings = promptForSettings(storage.get());
905
906     Storage::State storageState = storage->init(settings);
907     switch (storageState) {
908     case Storage::IsReady:
909         if (!saveBackendSettings(backend, settings)) {
910             qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
911         }
912         qWarning() << qPrintable(tr("Switched storage backend to: %1").arg(backend));
913         qWarning() << qPrintable(tr("Backend already initialized. Skipping Migration..."));
914         return true;
915     case Storage::NotAvailable:
916         qCritical() << qPrintable(tr("Storage backend is not available: %1").arg(backend));
917         return false;
918     case Storage::NeedsSetup:
919         if (!storage->setup(settings)) {
920             qWarning() << qPrintable(tr("Unable to setup storage backend: %1").arg(backend));
921             return false;
922         }
923
924         if (storage->init(settings) != Storage::IsReady) {
925             qWarning() << qPrintable(tr("Unable to initialize storage backend: %1").arg(backend));
926             return false;
927         }
928
929         if (!saveBackendSettings(backend, settings)) {
930             qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
931         }
932         qWarning() << qPrintable(tr("Switched storage backend to: %1").arg(backend));
933         break;
934     }
935
936     // let's see if we have a current storage object we can migrate from
937     auto reader = getMigrationReader(_storage.get());
938     auto writer = getMigrationWriter(storage.get());
939     if (reader && writer) {
940         qDebug() << qPrintable(tr("Migrating storage backend %1 to %2...").arg(_storage->displayName(), storage->displayName()));
941         _storage.reset();
942         storage.reset();
943         if (reader->migrateTo(writer.get())) {
944             qDebug() << "Migration finished!";
945             qDebug() << qPrintable(tr("Migration finished!"));
946             if (!saveBackendSettings(backend, settings)) {
947                 qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
948                 return false;
949             }
950             return true;
951         }
952         qWarning() << qPrintable(tr("Unable to migrate storage backend! (No migration writer for %1)").arg(backend));
953         return false;
954     }
955
956     // inform the user why we cannot merge
957     if (!_storage) {
958         qWarning() << qPrintable(tr("No currently active storage backend. Skipping migration..."));
959     }
960     else if (!reader) {
961         qWarning() << qPrintable(tr("Currently active storage backend does not support migration: %1").arg(_storage->displayName()));
962     }
963     if (writer) {
964         qWarning() << qPrintable(tr("New storage backend does not support migration: %1").arg(backend));
965     }
966
967     // so we were unable to merge, but let's create a user \o/
968     _storage = std::move(storage);
969     createUser();
970     return true;
971 }
972
973 // TODO: I am not sure if this function is implemented correctly.
974 // There is currently no concept of migraiton between auth backends.
975 bool Core::selectAuthenticator(const QString& backend)
976 {
977     // Register all authentication backends.
978     registerAuthenticators();
979     auto auther = authenticator(backend);
980     if (!auther) {
981         QStringList authenticators;
982         std::transform(_registeredAuthenticators.begin(),
983                        _registeredAuthenticators.end(),
984                        std::back_inserter(authenticators),
985                        [](const DeferredSharedPtr<Authenticator>& authenticator) { return authenticator->displayName(); });
986         qWarning() << qPrintable(tr("Unsupported authenticator: %1").arg(backend));
987         qWarning() << qPrintable(tr("Supported authenticators are:")) << qPrintable(authenticators.join(", "));
988         return false;
989     }
990
991     QVariantMap settings = promptForSettings(auther.get());
992
993     Authenticator::State state = auther->init(settings);
994     switch (state) {
995     case Authenticator::IsReady:
996         saveAuthenticatorSettings(backend, settings);
997         qWarning() << qPrintable(tr("Switched authenticator to: %1").arg(backend));
998         return true;
999     case Authenticator::NotAvailable:
1000         qCritical() << qPrintable(tr("Authenticator is not available: %1").arg(backend));
1001         return false;
1002     case Authenticator::NeedsSetup:
1003         if (!auther->setup(settings)) {
1004             qWarning() << qPrintable(tr("Unable to setup authenticator: %1").arg(backend));
1005             return false;
1006         }
1007
1008         if (auther->init(settings) != Authenticator::IsReady) {
1009             qWarning() << qPrintable(tr("Unable to initialize authenticator: %1").arg(backend));
1010             return false;
1011         }
1012
1013         saveAuthenticatorSettings(backend, settings);
1014         qWarning() << qPrintable(tr("Switched authenticator to: %1").arg(backend));
1015     }
1016
1017     _authenticator = std::move(auther);
1018     return true;
1019 }
1020
1021 bool Core::createUser()
1022 {
1023     QTextStream out(stdout);
1024     QTextStream in(stdin);
1025     out << "Add a new user:" << endl;
1026     out << "Username: ";
1027     out.flush();
1028     QString username = in.readLine().trimmed();
1029
1030     disableStdInEcho();
1031     out << "Password: ";
1032     out.flush();
1033     QString password = in.readLine().trimmed();
1034     out << endl;
1035     out << "Repeat Password: ";
1036     out.flush();
1037     QString password2 = in.readLine().trimmed();
1038     out << endl;
1039     enableStdInEcho();
1040
1041     if (password != password2) {
1042         qWarning() << "Passwords don't match!";
1043         return false;
1044     }
1045     if (password.isEmpty()) {
1046         qWarning() << "Password is empty!";
1047         return false;
1048     }
1049
1050     if (_configured && _storage->addUser(username, password).isValid()) {
1051         out << "Added user " << username << " successfully!" << endl;
1052         return true;
1053     }
1054     else {
1055         qWarning() << "Unable to add user:" << qPrintable(username);
1056         return false;
1057     }
1058 }
1059
1060 bool Core::changeUserPass(const QString& username)
1061 {
1062     QTextStream out(stdout);
1063     QTextStream in(stdin);
1064     UserId userId = _storage->getUserId(username);
1065     if (!userId.isValid()) {
1066         out << "User " << username << " does not exist." << endl;
1067         return false;
1068     }
1069
1070     if (!canChangeUserPassword(userId)) {
1071         out << "User " << username << " is configured through an auth provider that has forbidden manual password changing." << endl;
1072         return false;
1073     }
1074
1075     out << "Change password for user: " << username << endl;
1076
1077     disableStdInEcho();
1078     out << "New Password: ";
1079     out.flush();
1080     QString password = in.readLine().trimmed();
1081     out << endl;
1082     out << "Repeat Password: ";
1083     out.flush();
1084     QString password2 = in.readLine().trimmed();
1085     out << endl;
1086     enableStdInEcho();
1087
1088     if (password != password2) {
1089         qWarning() << "Passwords don't match!";
1090         return false;
1091     }
1092     if (password.isEmpty()) {
1093         qWarning() << "Password is empty!";
1094         return false;
1095     }
1096
1097     if (_configured && _storage->updateUser(userId, password)) {
1098         out << "Password changed successfully!" << endl;
1099         return true;
1100     }
1101     else {
1102         qWarning() << "Failed to change password!";
1103         return false;
1104     }
1105 }
1106
1107 bool Core::changeUserPassword(UserId userId, const QString& password)
1108 {
1109     if (!isConfigured() || !userId.isValid())
1110         return false;
1111
1112     if (!canChangeUserPassword(userId))
1113         return false;
1114
1115     return instance()->_storage->updateUser(userId, password);
1116 }
1117
1118 // TODO: this code isn't currently 100% optimal because the core
1119 // doesn't know it can have multiple auth providers configured (there aren't
1120 // multiple auth providers at the moment anyway) and we have hardcoded the
1121 // Database provider to be always allowed.
1122 bool Core::canChangeUserPassword(UserId userId)
1123 {
1124     QString authProvider = instance()->_storage->getUserAuthenticator(userId);
1125     if (authProvider != "Database") {
1126         if (authProvider != instance()->_authenticator->backendId()) {
1127             return false;
1128         }
1129         else if (instance()->_authenticator->canChangePassword()) {
1130             return false;
1131         }
1132     }
1133     return true;
1134 }
1135
1136 std::unique_ptr<AbstractSqlMigrationReader> Core::getMigrationReader(Storage* storage)
1137 {
1138     if (!storage)
1139         return nullptr;
1140
1141     auto* sqlStorage = qobject_cast<AbstractSqlStorage*>(storage);
1142     if (!sqlStorage) {
1143         qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
1144         return nullptr;
1145     }
1146
1147     return sqlStorage->createMigrationReader();
1148 }
1149
1150 std::unique_ptr<AbstractSqlMigrationWriter> Core::getMigrationWriter(Storage* storage)
1151 {
1152     if (!storage)
1153         return nullptr;
1154
1155     auto* sqlStorage = qobject_cast<AbstractSqlStorage*>(storage);
1156     if (!sqlStorage) {
1157         qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
1158         return nullptr;
1159     }
1160
1161     return sqlStorage->createMigrationWriter();
1162 }
1163
1164 bool Core::saveBackendSettings(const QString& backend, const QVariantMap& settings)
1165 {
1166     QVariantMap dbsettings;
1167     dbsettings["Backend"] = backend;
1168     dbsettings["ConnectionProperties"] = settings;
1169     CoreSettings s = CoreSettings();
1170     s.setStorageSettings(dbsettings);
1171     return s.sync();
1172 }
1173
1174 void Core::saveAuthenticatorSettings(const QString& backend, const QVariantMap& settings)
1175 {
1176     QVariantMap dbsettings;
1177     dbsettings["Authenticator"] = backend;
1178     dbsettings["AuthProperties"] = settings;
1179     CoreSettings().setAuthSettings(dbsettings);
1180 }
1181
1182 // Generic version of promptForSettings that doesn't care what *type* of
1183 // backend it runs over.
1184 template<typename Backend>
1185 QVariantMap Core::promptForSettings(const Backend* backend)
1186 {
1187     QVariantMap settings;
1188     const QVariantList& setupData = backend->setupData();
1189
1190     if (setupData.isEmpty())
1191         return settings;
1192
1193     QTextStream out(stdout);
1194     QTextStream in(stdin);
1195     out << "Default values are in brackets" << endl;
1196
1197     for (int i = 0; i + 2 < setupData.size(); i += 3) {
1198         QString key = setupData[i].toString();
1199         out << setupData[i + 1].toString() << " [" << setupData[i + 2].toString() << "]: " << flush;
1200
1201         bool noEcho = key.toLower().contains("password");
1202         if (noEcho) {
1203             disableStdInEcho();
1204         }
1205         QString input = in.readLine().trimmed();
1206         if (noEcho) {
1207             out << endl;
1208             enableStdInEcho();
1209         }
1210
1211         QVariant value{setupData[i + 2]};
1212         if (!input.isEmpty()) {
1213             switch (value.type()) {
1214             case QVariant::Int:
1215                 value = input.toInt();
1216                 break;
1217             default:
1218                 value = input;
1219             }
1220         }
1221         settings[key] = value;
1222     }
1223     return settings;
1224 }
1225
1226 #ifdef Q_OS_WIN
1227 void Core::stdInEcho(bool on)
1228 {
1229     HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
1230     DWORD mode = 0;
1231     GetConsoleMode(hStdin, &mode);
1232     if (on)
1233         mode |= ENABLE_ECHO_INPUT;
1234     else
1235         mode &= ~ENABLE_ECHO_INPUT;
1236     SetConsoleMode(hStdin, mode);
1237 }
1238
1239 #else
1240 void Core::stdInEcho(bool on)
1241 {
1242     termios t;
1243     tcgetattr(STDIN_FILENO, &t);
1244     if (on)
1245         t.c_lflag |= ECHO;
1246     else
1247         t.c_lflag &= ~ECHO;
1248     tcsetattr(STDIN_FILENO, TCSANOW, &t);
1249 }
1250
1251 #endif /* Q_OS_WIN */