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