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