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