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