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