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