Add LdapAuthenticator class for logging in users via LDAP
[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 }
249
250
251 /*** Session Restore ***/
252
253 void Core::saveState()
254 {
255     CoreSettings s;
256     QVariantMap state;
257     QVariantList activeSessions;
258     foreach(UserId user, instance()->_sessions.keys())
259         activeSessions << QVariant::fromValue<UserId>(user);
260     state["CoreStateVersion"] = 1;
261     state["ActiveSessions"] = activeSessions;
262     s.setCoreState(state);
263 }
264
265
266 void Core::restoreState()
267 {
268     if (!instance()->_configured) {
269         // qWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!"));
270         return;
271     }
272     if (instance()->_sessions.count()) {
273         qWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!"));
274         return;
275     }
276     CoreSettings s;
277     /* We don't check, since we are at the first version since switching to Git
278     uint statever = s.coreState().toMap()["CoreStateVersion"].toUInt();
279     if(statever < 1) {
280       qWarning() << qPrintable(tr("Core state too old, ignoring..."));
281       return;
282     }
283     */
284
285     QVariantList activeSessions = s.coreState().toMap()["ActiveSessions"].toList();
286     if (activeSessions.count() > 0) {
287         quInfo() << "Restoring previous core state...";
288         foreach(QVariant v, activeSessions) {
289             UserId user = v.value<UserId>();
290             instance()->sessionForUser(user, true);
291         }
292     }
293 }
294
295
296 /*** Core Setup ***/
297
298 QString Core::setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupData)
299 {
300     return instance()->setupCore(adminUser, adminPassword, backend, setupData, authBackend, authSetupData);
301 }
302
303
304 QString Core::setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupData)
305 {
306     if (_configured)
307         return tr("Core is already configured! Not configuring again...");
308
309     if (adminUser.isEmpty() || adminPassword.isEmpty()) {
310         return tr("Admin user or password not set.");
311     }
312     if (!(_configured = initStorage(backend, setupData, true))) {
313         return tr("Could not setup storage!");
314     }
315
316     if (!saveBackendSettings(backend, setupData)) {
317         return tr("Could not save backend settings, probably a permission problem.");
318     }
319         saveAuthBackendSettings(authBackend, authSetupData);
320
321     quInfo() << qPrintable(tr("Creating admin user..."));
322     _storage->addUser(adminUser, adminPassword);
323     startListening(); // TODO check when we need this
324     return QString();
325 }
326
327
328 QString Core::setupCoreForInternalUsage()
329 {
330     Q_ASSERT(!_storageBackends.isEmpty());
331
332     qsrand(QDateTime::currentDateTime().toTime_t());
333     int pass = 0;
334     for (int i = 0; i < 10; i++) {
335         pass *= 10;
336         pass += qrand() % 10;
337     }
338
339     // mono client currently needs sqlite
340     return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap(), "StorageAuth", QVariantMap());
341 }
342
343
344 /*** Storage Handling ***/
345 void Core::registerStorageBackends()
346 {
347     // Register storage backends here!
348     registerStorageBackend(new SqliteStorage(this));
349     registerStorageBackend(new PostgreSqlStorage(this));
350 }
351
352
353 bool Core::registerStorageBackend(Storage *backend)
354 {
355     if (backend->isAvailable()) {
356         _storageBackends[backend->displayName()] = backend;
357         return true;
358     }
359     else {
360         backend->deleteLater();
361         return false;
362     }
363 }
364
365 void Core::unregisterStorageBackends()
366 {
367     foreach(Storage *s, _storageBackends.values()) {
368         s->deleteLater();
369     }
370     _storageBackends.clear();
371 }
372
373
374 void Core::unregisterStorageBackend(Storage *backend)
375 {
376     _storageBackends.remove(backend->displayName());
377     backend->deleteLater();
378 }
379
380 // Authentication handling, now independent from storage.
381 // Register and unregister authenticators.
382
383 void Core::registerAuthenticatorBackends()
384 {
385     // Register new authentication backends here!
386         registerAuthenticatorBackend(new SqlAuthenticator(this));
387 #ifdef HAVE_LDAP
388     registerAuthenticatorBackend(new LdapAuthenticator(this));
389 #endif
390     
391 }
392
393 bool Core::registerAuthenticatorBackend(Authenticator *authenticator)
394 {
395         if (authenticator->isAvailable())
396         {
397                 _authenticatorBackends[authenticator->displayName()] = authenticator;
398                 return true;
399         } else {
400                 authenticator->deleteLater();
401                 return false;
402         }
403 }
404
405 void Core::unregisterAuthenticatorBackends()
406 {
407         foreach(Authenticator* a, _authenticatorBackends.values())
408         {
409                 a->deleteLater();
410         }
411         _authenticatorBackends.clear();
412 }
413
414 void Core::unregisterAuthenticatorBackend(Authenticator *backend)
415 {
416         _authenticatorBackends.remove(backend->displayName());
417         backend->deleteLater();
418 }
419
420 // old db settings:
421 // "Type" => "sqlite"
422 bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool setup)
423 {
424     _storage = 0;
425
426     if (backend.isEmpty()) {
427         return false;
428     }
429
430     Storage *storage = 0;
431     if (_storageBackends.contains(backend)) {
432         storage = _storageBackends[backend];
433     }
434     else {
435         qCritical() << "Selected storage backend is not available:" << backend;
436         return false;
437     }
438
439     Storage::State storageState = storage->init(settings);
440     switch (storageState) {
441     case Storage::NeedsSetup:
442         if (!setup)
443             return false;  // trigger setup process
444         if (storage->setup(settings))
445             return initStorage(backend, settings, false);
446     // if initialization wasn't successful, we quit to keep from coming up unconfigured
447     case Storage::NotAvailable:
448         qCritical() << "FATAL: Selected storage backend is not available:" << backend;
449         exit(EXIT_FAILURE);
450     case Storage::IsReady:
451         // delete all other backends
452         _storageBackends.remove(backend);
453         unregisterStorageBackends();
454         connect(storage, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)));
455     }
456     _storage = storage;
457     return true;
458 }
459
460 // XXX: TODO: Apparently, this is legacy?
461 bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings, bool setup)
462 {
463     _authenticator = 0;
464     
465     if (backend.isEmpty()) {
466             return false;
467     }
468     
469     Authenticator *authenticator = 0;
470     if (_authenticatorBackends.contains(backend)) {
471         authenticator = _authenticatorBackends[backend];
472     }
473     else {
474         qCritical() << "Selected auth backend is not available:" << backend;
475         return false;
476     }
477
478     Authenticator::State authState = authenticator->init(settings);
479     switch (authState) {
480     case Authenticator::NeedsSetup:
481         if (!setup)
482             return false;  // trigger setup process
483         if (authenticator->setup(settings))
484             return initAuthenticator(backend, settings, false);
485     // if initialization wasn't successful, we quit to keep from coming up unconfigured
486     case Authenticator::NotAvailable:
487         qCritical() << "FATAL: Selected auth backend is not available:" << backend;
488         exit(EXIT_FAILURE);
489     case Authenticator::IsReady:
490         // delete all other backends
491         _authenticatorBackends.remove(backend);
492         unregisterAuthenticatorBackends();
493     }
494     _authenticator = authenticator;
495     return true;        
496 }
497
498 void Core::syncStorage()
499 {
500     if (_storage)
501         _storage->sync();
502 }
503
504
505 /*** Storage Access ***/
506 bool Core::createNetwork(UserId user, NetworkInfo &info)
507 {
508     NetworkId networkId = instance()->_storage->createNetwork(user, info);
509     if (!networkId.isValid())
510         return false;
511
512     info.networkId = networkId;
513     return true;
514 }
515
516
517 /*** Network Management ***/
518
519 bool Core::sslSupported()
520 {
521 #ifdef HAVE_SSL
522     SslServer *sslServer = qobject_cast<SslServer *>(&instance()->_server);
523     return sslServer && sslServer->isCertValid();
524 #else
525     return false;
526 #endif
527 }
528
529
530 bool Core::reloadCerts()
531 {
532 #ifdef HAVE_SSL
533     SslServer *sslServerv4 = qobject_cast<SslServer *>(&instance()->_server);
534     bool retv4 = sslServerv4->reloadCerts();
535
536     SslServer *sslServerv6 = qobject_cast<SslServer *>(&instance()->_v6server);
537     bool retv6 = sslServerv6->reloadCerts();
538
539     return retv4 && retv6;
540 #else
541     // SSL not supported, don't mark configuration reload as failed
542     return true;
543 #endif
544 }
545
546
547 bool Core::startListening()
548 {
549     // in mono mode we only start a local port if a port is specified in the cli call
550     if (Quassel::runMode() == Quassel::Monolithic && !Quassel::isOptionSet("port"))
551         return true;
552
553     bool success = false;
554     uint port = Quassel::optionValue("port").toUInt();
555
556     const QString listen = Quassel::optionValue("listen");
557     const QStringList listen_list = listen.split(",", QString::SkipEmptyParts);
558     if (listen_list.size() > 0) {
559         foreach(const QString listen_term, listen_list) { // TODO: handle multiple interfaces for same TCP version gracefully
560             QHostAddress addr;
561             if (!addr.setAddress(listen_term)) {
562                 qCritical() << qPrintable(
563                     tr("Invalid listen address %1")
564                     .arg(listen_term)
565                     );
566             }
567             else {
568                 switch (addr.protocol()) {
569                 case QAbstractSocket::IPv6Protocol:
570                     if (_v6server.listen(addr, port)) {
571                         quInfo() << qPrintable(
572                             tr("Listening for GUI clients on IPv6 %1 port %2 using protocol version %3")
573                             .arg(addr.toString())
574                             .arg(_v6server.serverPort())
575                             .arg(Quassel::buildInfo().protocolVersion)
576                             );
577                         success = true;
578                     }
579                     else
580                         quWarning() << qPrintable(
581                             tr("Could not open IPv6 interface %1:%2: %3")
582                             .arg(addr.toString())
583                             .arg(port)
584                             .arg(_v6server.errorString()));
585                     break;
586                 case QAbstractSocket::IPv4Protocol:
587                     if (_server.listen(addr, port)) {
588                         quInfo() << qPrintable(
589                             tr("Listening for GUI clients on IPv4 %1 port %2 using protocol version %3")
590                             .arg(addr.toString())
591                             .arg(_server.serverPort())
592                             .arg(Quassel::buildInfo().protocolVersion)
593                             );
594                         success = true;
595                     }
596                     else {
597                         // if v6 succeeded on Any, the port will be already in use - don't display the error then
598                         if (!success || _server.serverError() != QAbstractSocket::AddressInUseError)
599                             quWarning() << qPrintable(
600                                 tr("Could not open IPv4 interface %1:%2: %3")
601                                 .arg(addr.toString())
602                                 .arg(port)
603                                 .arg(_server.errorString()));
604                     }
605                     break;
606                 default:
607                     qCritical() << qPrintable(
608                         tr("Invalid listen address %1, unknown network protocol")
609                         .arg(listen_term)
610                         );
611                     break;
612                 }
613             }
614         }
615     }
616     if (!success)
617         quError() << qPrintable(tr("Could not open any network interfaces to listen on!"));
618
619     return success;
620 }
621
622
623 void Core::stopListening(const QString &reason)
624 {
625     bool wasListening = false;
626     if (_server.isListening()) {
627         wasListening = true;
628         _server.close();
629     }
630     if (_v6server.isListening()) {
631         wasListening = true;
632         _v6server.close();
633     }
634     if (wasListening) {
635         if (reason.isEmpty())
636             quInfo() << "No longer listening for GUI clients.";
637         else
638             quInfo() << qPrintable(reason);
639     }
640 }
641
642
643 void Core::incomingConnection()
644 {
645     QTcpServer *server = qobject_cast<QTcpServer *>(sender());
646     Q_ASSERT(server);
647     while (server->hasPendingConnections()) {
648         QTcpSocket *socket = server->nextPendingConnection();
649
650         CoreAuthHandler *handler = new CoreAuthHandler(socket, this);
651         _connectingClients.insert(handler);
652
653         connect(handler, SIGNAL(disconnected()), SLOT(clientDisconnected()));
654         connect(handler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(socketError(QAbstractSocket::SocketError,QString)));
655         connect(handler, SIGNAL(handshakeComplete(RemotePeer*,UserId)), SLOT(setupClientSession(RemotePeer*,UserId)));
656
657         quInfo() << qPrintable(tr("Client connected from"))  << qPrintable(socket->peerAddress().toString());
658
659         if (!_configured) {
660             stopListening(tr("Closing server for basic setup."));
661         }
662     }
663 }
664
665
666 // Potentially called during the initialization phase (before handing the connection off to the session)
667 void Core::clientDisconnected()
668 {
669     CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
670     Q_ASSERT(handler);
671
672     quInfo() << qPrintable(tr("Non-authed client disconnected:")) << qPrintable(handler->socket()->peerAddress().toString());
673     _connectingClients.remove(handler);
674     handler->deleteLater();
675
676     // make server listen again if still not configured
677     if (!_configured) {
678         startListening();
679     }
680
681     // TODO remove unneeded sessions - if necessary/possible...
682     // Suggestion: kill sessions if they are not connected to any network and client.
683 }
684
685
686 void Core::setupClientSession(RemotePeer *peer, UserId uid)
687 {
688     CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
689     Q_ASSERT(handler);
690
691     // From now on everything is handled by the client session
692     disconnect(handler, 0, this, 0);
693     _connectingClients.remove(handler);
694     handler->deleteLater();
695
696     // Find or create session for validated user
697     sessionForUser(uid);
698
699     // as we are currently handling an event triggered by incoming data on this socket
700     // it is unsafe to directly move the socket to the client thread.
701     QCoreApplication::postEvent(this, new AddClientEvent(peer, uid));
702 }
703
704
705 void Core::customEvent(QEvent *event)
706 {
707     if (event->type() == AddClientEventId) {
708         AddClientEvent *addClientEvent = static_cast<AddClientEvent *>(event);
709         addClientHelper(addClientEvent->peer, addClientEvent->userId);
710         return;
711     }
712 }
713
714
715 void Core::addClientHelper(RemotePeer *peer, UserId uid)
716 {
717     // Find or create session for validated user
718     SessionThread *session = sessionForUser(uid);
719     session->addClient(peer);
720 }
721
722
723 void Core::setupInternalClientSession(InternalPeer *clientPeer)
724 {
725     if (!_configured) {
726         stopListening();
727         setupCoreForInternalUsage();
728     }
729
730     UserId uid;
731     if (_storage) {
732         uid = _storage->internalUser();
733     }
734     else {
735         qWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!";
736         return;
737     }
738
739     InternalPeer *corePeer = new InternalPeer(this);
740     corePeer->setPeer(clientPeer);
741     clientPeer->setPeer(corePeer);
742
743     // Find or create session for validated user
744     SessionThread *sessionThread = sessionForUser(uid);
745     sessionThread->addClient(corePeer);
746 }
747
748
749 SessionThread *Core::sessionForUser(UserId uid, bool restore)
750 {
751     if (_sessions.contains(uid))
752         return _sessions[uid];
753
754     SessionThread *session = new SessionThread(uid, restore, this);
755     _sessions[uid] = session;
756     session->start();
757     return session;
758 }
759
760
761 void Core::socketError(QAbstractSocket::SocketError err, const QString &errorString)
762 {
763     qWarning() << QString("Socket error %1: %2").arg(err).arg(errorString);
764 }
765
766
767 QVariantList Core::backendInfo()
768 {
769     QVariantList backends;
770     foreach(const Storage *backend, instance()->_storageBackends.values()) {
771         QVariantMap v;
772         v["DisplayName"] = backend->displayName();
773         v["Description"] = backend->description();
774         v["SetupKeys"] = backend->setupKeys();
775         v["SetupDefaults"] = backend->setupDefaults();
776         v["IsDefault"] = isStorageBackendDefault(backend);
777         backends.append(v);
778     }
779     return backends;
780 }
781
782 QVariantList Core::authenticatorInfo()
783 {
784     QVariantList backends;
785     foreach(const Authenticator *backend, instance()->_authenticatorBackends.values()) {
786         QVariantMap v;
787         v["DisplayName"] = backend->displayName();
788         v["Description"] = backend->description();
789         v["SetupKeys"] = backend->setupKeys();
790         v["SetupDefaults"] = backend->setupDefaults();
791         backends.append(v);
792     }
793     return backends;
794 }
795
796 // migration / backend selection
797 bool Core::selectBackend(const QString &backend)
798 {
799     // reregister all storage backends
800     registerStorageBackends();
801     if (!_storageBackends.contains(backend)) {
802         qWarning() << qPrintable(QString("Core::selectBackend(): unsupported backend: %1").arg(backend));
803         qWarning() << "    supported backends are:" << qPrintable(QStringList(_storageBackends.keys()).join(", "));
804         return false;
805     }
806
807     Storage *storage = _storageBackends[backend];
808     QVariantMap settings = promptForSettings(storage);
809
810     Storage::State storageState = storage->init(settings);
811     switch (storageState) {
812     case Storage::IsReady:
813         if (!saveBackendSettings(backend, settings)) {
814             qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
815         }
816         qWarning() << "Switched backend to:" << qPrintable(backend);
817         qWarning() << "Backend already initialized. Skipping Migration";
818         return true;
819     case Storage::NotAvailable:
820         qCritical() << "Backend is not available:" << qPrintable(backend);
821         return false;
822     case Storage::NeedsSetup:
823         if (!storage->setup(settings)) {
824             qWarning() << qPrintable(QString("Core::selectBackend(): unable to setup backend: %1").arg(backend));
825             return false;
826         }
827
828         if (storage->init(settings) != Storage::IsReady) {
829             qWarning() << qPrintable(QString("Core::migrateBackend(): unable to initialize backend: %1").arg(backend));
830             return false;
831         }
832
833         if (!saveBackendSettings(backend, settings)) {
834             qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
835         }
836         qWarning() << "Switched backend to:" << qPrintable(backend);
837         break;
838     }
839
840     // let's see if we have a current storage object we can migrate from
841     AbstractSqlMigrationReader *reader = getMigrationReader(_storage);
842     AbstractSqlMigrationWriter *writer = getMigrationWriter(storage);
843     if (reader && writer) {
844         qDebug() << qPrintable(QString("Migrating Storage backend %1 to %2...").arg(_storage->displayName(), storage->displayName()));
845         delete _storage;
846         _storage = 0;
847         delete storage;
848         storage = 0;
849         if (reader->migrateTo(writer)) {
850             qDebug() << "Migration finished!";
851             if (!saveBackendSettings(backend, settings)) {
852                 qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
853                 return false;
854             }
855             return true;
856         }
857         return false;
858         qWarning() << qPrintable(QString("Core::migrateDb(): unable to migrate storage backend! (No migration writer for %1)").arg(backend));
859     }
860
861     // inform the user why we cannot merge
862     if (!_storage) {
863         qWarning() << "No currently active backend. Skipping migration.";
864     }
865     else if (!reader) {
866         qWarning() << "Currently active backend does not support migration:" << qPrintable(_storage->displayName());
867     }
868     if (writer) {
869         qWarning() << "New backend does not support migration:" << qPrintable(backend);
870     }
871
872     // so we were unable to merge, but let's create a user \o/
873     _storage = storage;
874     createUser();
875     return true;
876 }
877
878
879 bool Core::createUser()
880 {
881     QTextStream out(stdout);
882     QTextStream in(stdin);
883     out << "Add a new user:" << endl;
884     out << "Username: ";
885     out.flush();
886     QString username = in.readLine().trimmed();
887
888     disableStdInEcho();
889     out << "Password: ";
890     out.flush();
891     QString password = in.readLine().trimmed();
892     out << endl;
893     out << "Repeat Password: ";
894     out.flush();
895     QString password2 = in.readLine().trimmed();
896     out << endl;
897     enableStdInEcho();
898
899     if (password != password2) {
900         qWarning() << "Passwords don't match!";
901         return false;
902     }
903     if (password.isEmpty()) {
904         qWarning() << "Password is empty!";
905         return false;
906     }
907
908     if (_configured && _storage->addUser(username, password).isValid()) {
909         out << "Added user " << username << " successfully!" << endl;
910         return true;
911     }
912     else {
913         qWarning() << "Unable to add user:" << qPrintable(username);
914         return false;
915     }
916 }
917
918
919 bool Core::changeUserPass(const QString &username)
920 {
921     QTextStream out(stdout);
922     QTextStream in(stdin);
923     UserId userId = _storage->getUserId(username);
924     if (!userId.isValid()) {
925         out << "User " << username << " does not exist." << endl;
926         return false;
927     }
928
929     out << "Change password for user: " << username << endl;
930
931     disableStdInEcho();
932     out << "New Password: ";
933     out.flush();
934     QString password = in.readLine().trimmed();
935     out << endl;
936     out << "Repeat Password: ";
937     out.flush();
938     QString password2 = in.readLine().trimmed();
939     out << endl;
940     enableStdInEcho();
941
942     if (password != password2) {
943         qWarning() << "Passwords don't match!";
944         return false;
945     }
946     if (password.isEmpty()) {
947         qWarning() << "Password is empty!";
948         return false;
949     }
950
951     if (_configured && _storage->updateUser(userId, password)) {
952         out << "Password changed successfully!" << endl;
953         return true;
954     }
955     else {
956         qWarning() << "Failed to change password!";
957         return false;
958     }
959 }
960
961
962 bool Core::changeUserPassword(UserId userId, const QString &password)
963 {
964     if (!isConfigured() || !userId.isValid())
965         return false;
966
967     return instance()->_storage->updateUser(userId, password);
968 }
969
970
971 AbstractSqlMigrationReader *Core::getMigrationReader(Storage *storage)
972 {
973     if (!storage)
974         return 0;
975
976     AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
977     if (!sqlStorage) {
978         qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
979         return 0;
980     }
981
982     return sqlStorage->createMigrationReader();
983 }
984
985
986 AbstractSqlMigrationWriter *Core::getMigrationWriter(Storage *storage)
987 {
988     if (!storage)
989         return 0;
990
991     AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
992     if (!sqlStorage) {
993         qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
994         return 0;
995     }
996
997     return sqlStorage->createMigrationWriter();
998 }
999
1000
1001 bool Core::saveBackendSettings(const QString &backend, const QVariantMap &settings)
1002 {
1003     QVariantMap dbsettings;
1004     dbsettings["Backend"] = backend;
1005     dbsettings["ConnectionProperties"] = settings;
1006     CoreSettings s = CoreSettings();
1007     s.setStorageSettings(dbsettings);
1008     return s.sync();
1009 }
1010
1011 void Core::saveAuthBackendSettings(const QString &backend, const QVariantMap &settings)
1012 {
1013     QVariantMap dbsettings;
1014     dbsettings["AuthBackend"] = backend;
1015     dbsettings["ConnectionProperties"] = settings;
1016     CoreSettings().setAuthSettings(dbsettings);
1017 }
1018
1019
1020 QVariantMap Core::promptForSettings(const Storage *storage)
1021 {
1022     QVariantMap settings;
1023
1024     QStringList keys = storage->setupKeys();
1025     if (keys.isEmpty())
1026         return settings;
1027
1028     QTextStream out(stdout);
1029     QTextStream in(stdin);
1030     out << "Default values are in brackets" << endl;
1031
1032     QVariantMap defaults = storage->setupDefaults();
1033     QString value;
1034     foreach(QString key, keys) {
1035         QVariant val;
1036         if (defaults.contains(key)) {
1037             val = defaults[key];
1038         }
1039         out << key;
1040         if (!val.toString().isEmpty()) {
1041             out << " (" << val.toString() << ")";
1042         }
1043         out << ": ";
1044         out.flush();
1045
1046         bool noEcho = QString("password").toLower().startsWith(key.toLower());
1047         if (noEcho) {
1048             disableStdInEcho();
1049         }
1050         value = in.readLine().trimmed();
1051         if (noEcho) {
1052             out << endl;
1053             enableStdInEcho();
1054         }
1055
1056         if (!value.isEmpty()) {
1057             switch (defaults[key].type()) {
1058             case QVariant::Int:
1059                 val = QVariant(value.toInt());
1060                 break;
1061             default:
1062                 val = QVariant(value);
1063             }
1064         }
1065         settings[key] = val;
1066     }
1067     return settings;
1068 }
1069
1070
1071 #ifdef Q_OS_WIN
1072 void Core::stdInEcho(bool on)
1073 {
1074     HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
1075     DWORD mode = 0;
1076     GetConsoleMode(hStdin, &mode);
1077     if (on)
1078         mode |= ENABLE_ECHO_INPUT;
1079     else
1080         mode &= ~ENABLE_ECHO_INPUT;
1081     SetConsoleMode(hStdin, mode);
1082 }
1083
1084
1085 #else
1086 void Core::stdInEcho(bool on)
1087 {
1088     termios t;
1089     tcgetattr(STDIN_FILENO, &t);
1090     if (on)
1091         t.c_lflag |= ECHO;
1092     else
1093         t.c_lflag &= ~ECHO;
1094     tcsetattr(STDIN_FILENO, TCSANOW, &t);
1095 }
1096
1097
1098 #endif /* Q_OS_WIN */