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