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