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