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