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