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