1 /***************************************************************************
2 * Copyright (C) 2005-2016 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include <QCoreApplication>
24 #include "coreauthhandler.h"
25 #include "coresession.h"
26 #include "coresettings.h"
28 #include "internalpeer.h"
30 #include "postgresqlstorage.h"
32 #include "sqlauthenticator.h"
33 #include "sqlitestorage.h"
46 # include <sys/types.h>
47 # include <sys/stat.h>
48 #endif /* HAVE_UMASK */
50 // ==============================
52 // ==============================
53 const int Core::AddClientEventId = QEvent::registerEventType();
55 class AddClientEvent : public QEvent
58 AddClientEvent(RemotePeer *p, UserId uid) : QEvent(QEvent::Type(Core::AddClientEventId)), peer(p), userId(uid) {}
64 // ==============================
66 // ==============================
67 Core *Core::instanceptr = 0;
69 Core *Core::instance()
71 if (instanceptr) return instanceptr;
72 instanceptr = new Core();
91 umask(S_IRWXG | S_IRWXO);
93 _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
95 Quassel::loadTranslation(QLocale::system());
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
100 QSettings newSettings("quassel-irc.org", "quasselcore");
104 QSettings::Format format = QSettings::IniFormat;
106 QSettings::Format format = QSettings::NativeFormat;
108 QString newFilePath = Quassel::configDirPath() + "quasselcore"
109 + ((format == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"));
110 QSettings newSettings(newFilePath, format);
111 #endif /* Q_OS_MAC */
113 if (newSettings.value("Config/Version").toUInt() == 0) {
115 QString org = "quassel-irc.org";
117 QString org = "Quassel Project";
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();
127 #ifndef Q_OS_MAC /* we don't need to move the db and cert for mac */
129 QString quasselDir = qgetenv("APPDATA") + "/quassel/";
130 #elif defined Q_OS_MAC
131 QString quasselDir = QDir::homePath() + "/Library/Application Support/Quassel/";
133 QString quasselDir = QDir::homePath() + "/.quassel/";
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");
143 qWarning() << "* Your database has been moved to" << Quassel::configDirPath() + "quassel-storage.sqlite";
145 qWarning() << "!!! Moving your database has failed. Please move it manually into" << Quassel::configDirPath();
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");
154 qWarning() << "* Your certificate has been moved to" << Quassel::configDirPath() + "quasselCert.pem";
156 qWarning() << "!!! Moving your certificate has failed. Please move it manually into" << Quassel::configDirPath();
158 #endif /* !Q_OS_MAC */
159 qWarning() << "*** Migration completed.\n\n";
164 // check settings version
165 // so far, we only have 1
167 if (s.version() != 1) {
168 qCritical() << "Invalid core settings version, terminating!";
172 registerStorageBackends();
173 registerAuthenticatorBackends();
175 connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
176 _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes
184 QVariantMap dbsettings = cs.storageSettings().toMap();
185 _configured = initStorage(dbsettings.value("Backend").toString(), dbsettings.value("ConnectionProperties").toMap());
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());
191 if (Quassel::isOptionSet("select-backend")) {
192 selectBackend(Quassel::optionValue("select-backend"));
196 // TODO: add --select-authenticator command line option and code.
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"
204 exit(1); // TODO make this less brutal (especially for mono client -> popup)
207 qWarning() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
209 if (!cs.isWritable()) {
210 qWarning() << "Cannot write quasselcore configuration; probably a permission problem.";
216 if (Quassel::isOptionSet("add-user")) {
217 exit(createUser() ? EXIT_SUCCESS : EXIT_FAILURE);
221 if (Quassel::isOptionSet("change-userpass")) {
222 exit(changeUserPass(Quassel::optionValue("change-userpass")) ?
223 EXIT_SUCCESS : EXIT_FAILURE);
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
230 if (Quassel::isOptionSet("oidentd"))
231 _oidentdConfigGenerator = new OidentdConfigGenerator(this);
237 // FIXME do we need more cleanup for handlers?
238 foreach(CoreAuthHandler *handler, _connectingClients) {
239 handler->deleteLater(); // disconnect non authed clients
241 qDeleteAll(_sessions);
242 qDeleteAll(_storageBackends);
246 /*** Session Restore ***/
248 void Core::saveState()
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);
261 void Core::restoreState()
263 if (!instance()->_configured) {
264 // qWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!"));
267 if (instance()->_sessions.count()) {
268 qWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!"));
272 /* We don't check, since we are at the first version since switching to Git
273 uint statever = s.coreState().toMap()["CoreStateVersion"].toUInt();
275 qWarning() << qPrintable(tr("Core state too old, ignoring..."));
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);
293 QString Core::setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupData)
295 return instance()->setupCore(adminUser, adminPassword, backend, setupData, authBackend, authSetupData);
299 QString Core::setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupData)
302 return tr("Core is already configured! Not configuring again...");
304 if (adminUser.isEmpty() || adminPassword.isEmpty()) {
305 return tr("Admin user or password not set.");
307 if (!(_configured = initStorage(backend, setupData, true))) {
308 return tr("Could not setup storage!");
311 if (!saveBackendSettings(backend, setupData)) {
312 return tr("Could not save backend settings, probably a permission problem.");
314 saveAuthBackendSettings(authBackend, authSetupData);
316 quInfo() << qPrintable(tr("Creating admin user..."));
317 _storage->addUser(adminUser, adminPassword);
318 startListening(); // TODO check when we need this
323 QString Core::setupCoreForInternalUsage()
325 Q_ASSERT(!_storageBackends.isEmpty());
327 qsrand(QDateTime::currentDateTime().toTime_t());
329 for (int i = 0; i < 10; i++) {
331 pass += qrand() % 10;
334 // mono client currently needs sqlite
335 return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap(), "StorageAuth", QVariantMap());
339 /*** Storage Handling ***/
340 void Core::registerStorageBackends()
342 // Register storage backends here!
343 registerStorageBackend(new SqliteStorage(this));
344 registerStorageBackend(new PostgreSqlStorage(this));
348 bool Core::registerStorageBackend(Storage *backend)
350 if (backend->isAvailable()) {
351 _storageBackends[backend->displayName()] = backend;
355 backend->deleteLater();
360 void Core::unregisterStorageBackends()
362 foreach(Storage *s, _storageBackends.values()) {
365 _storageBackends.clear();
369 void Core::unregisterStorageBackend(Storage *backend)
371 _storageBackends.remove(backend->displayName());
372 backend->deleteLater();
375 // Authentication handling, now independent from storage.
376 // Register and unregister authenticators.
378 void Core::registerAuthenticatorBackends()
380 // Register new authentication backends here!
381 //registerAuthenticatorBackend(new LdapAuthenticator(this));
382 registerAuthenticatorBackend(new SqlAuthenticator(this));
386 bool Core::registerAuthenticatorBackend(Authenticator *authenticator)
388 if (authenticator->isAvailable())
390 _authenticatorBackends[authenticator->displayName()] = authenticator;
393 authenticator->deleteLater();
398 void Core::unregisterAuthenticatorBackends()
400 foreach(Authenticator* a, _authenticatorBackends.values())
404 _authenticatorBackends.clear();
407 void Core::unregisterAuthenticatorBackend(Authenticator *backend)
409 _authenticatorBackends.remove(backend->displayName());
410 backend->deleteLater();
414 // "Type" => "sqlite"
415 bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool setup)
419 if (backend.isEmpty()) {
423 Storage *storage = 0;
424 if (_storageBackends.contains(backend)) {
425 storage = _storageBackends[backend];
428 qCritical() << "Selected storage backend is not available:" << backend;
432 Storage::State storageState = storage->init(settings);
433 switch (storageState) {
434 case Storage::NeedsSetup:
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;
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 &)));
453 // XXX: TODO: Apparently, this is legacy?
454 bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings, bool setup)
458 if (backend.isEmpty()) {
462 Authenticator *authenticator = 0;
463 if (_authenticatorBackends.contains(backend)) {
464 authenticator = _authenticatorBackends[backend];
467 qCritical() << "Selected auth backend is not available:" << backend;
471 Authenticator::State authState = authenticator->init(settings);
473 case Authenticator::NeedsSetup:
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;
482 case Authenticator::IsReady:
483 // delete all other backends
484 _authenticatorBackends.remove(backend);
485 unregisterAuthenticatorBackends();
487 _authenticator = authenticator;
491 void Core::syncStorage()
498 /*** Storage Access ***/
499 bool Core::createNetwork(UserId user, NetworkInfo &info)
501 NetworkId networkId = instance()->_storage->createNetwork(user, info);
502 if (!networkId.isValid())
505 info.networkId = networkId;
510 /*** Network Management ***/
512 bool Core::sslSupported()
515 SslServer *sslServer = qobject_cast<SslServer *>(&instance()->_server);
516 return sslServer && sslServer->isCertValid();
523 bool Core::reloadCerts()
526 SslServer *sslServerv4 = qobject_cast<SslServer *>(&instance()->_server);
527 bool retv4 = sslServerv4->reloadCerts();
529 SslServer *sslServerv6 = qobject_cast<SslServer *>(&instance()->_v6server);
530 bool retv6 = sslServerv6->reloadCerts();
532 return retv4 && retv6;
534 // SSL not supported, don't mark configuration reload as failed
540 bool Core::startListening()
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"))
546 bool success = false;
547 uint port = Quassel::optionValue("port").toUInt();
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
554 if (!addr.setAddress(listen_term)) {
555 qCritical() << qPrintable(
556 tr("Invalid listen address %1")
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)
573 quWarning() << qPrintable(
574 tr("Could not open IPv6 interface %1:%2: %3")
575 .arg(addr.toString())
577 .arg(_v6server.errorString()));
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)
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())
596 .arg(_server.errorString()));
600 qCritical() << qPrintable(
601 tr("Invalid listen address %1, unknown network protocol")
610 quError() << qPrintable(tr("Could not open any network interfaces to listen on!"));
616 void Core::stopListening(const QString &reason)
618 bool wasListening = false;
619 if (_server.isListening()) {
623 if (_v6server.isListening()) {
628 if (reason.isEmpty())
629 quInfo() << "No longer listening for GUI clients.";
631 quInfo() << qPrintable(reason);
636 void Core::incomingConnection()
638 QTcpServer *server = qobject_cast<QTcpServer *>(sender());
640 while (server->hasPendingConnections()) {
641 QTcpSocket *socket = server->nextPendingConnection();
643 CoreAuthHandler *handler = new CoreAuthHandler(socket, this);
644 _connectingClients.insert(handler);
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)));
650 quInfo() << qPrintable(tr("Client connected from")) << qPrintable(socket->peerAddress().toString());
653 stopListening(tr("Closing server for basic setup."));
659 // Potentially called during the initialization phase (before handing the connection off to the session)
660 void Core::clientDisconnected()
662 CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
665 quInfo() << qPrintable(tr("Non-authed client disconnected:")) << qPrintable(handler->socket()->peerAddress().toString());
666 _connectingClients.remove(handler);
667 handler->deleteLater();
669 // make server listen again if still not configured
674 // TODO remove unneeded sessions - if necessary/possible...
675 // Suggestion: kill sessions if they are not connected to any network and client.
679 void Core::setupClientSession(RemotePeer *peer, UserId uid)
681 CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
684 // From now on everything is handled by the client session
685 disconnect(handler, 0, this, 0);
686 _connectingClients.remove(handler);
687 handler->deleteLater();
689 // Find or create session for validated user
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));
698 void Core::customEvent(QEvent *event)
700 if (event->type() == AddClientEventId) {
701 AddClientEvent *addClientEvent = static_cast<AddClientEvent *>(event);
702 addClientHelper(addClientEvent->peer, addClientEvent->userId);
708 void Core::addClientHelper(RemotePeer *peer, UserId uid)
710 // Find or create session for validated user
711 SessionThread *session = sessionForUser(uid);
712 session->addClient(peer);
716 void Core::setupInternalClientSession(InternalPeer *clientPeer)
720 setupCoreForInternalUsage();
725 uid = _storage->internalUser();
728 qWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!";
732 InternalPeer *corePeer = new InternalPeer(this);
733 corePeer->setPeer(clientPeer);
734 clientPeer->setPeer(corePeer);
736 // Find or create session for validated user
737 SessionThread *sessionThread = sessionForUser(uid);
738 sessionThread->addClient(corePeer);
742 SessionThread *Core::sessionForUser(UserId uid, bool restore)
744 if (_sessions.contains(uid))
745 return _sessions[uid];
747 SessionThread *session = new SessionThread(uid, restore, this);
748 _sessions[uid] = session;
754 void Core::socketError(QAbstractSocket::SocketError err, const QString &errorString)
756 qWarning() << QString("Socket error %1: %2").arg(err).arg(errorString);
760 QVariantList Core::backendInfo()
762 QVariantList backends;
763 foreach(const Storage *backend, instance()->_storageBackends.values()) {
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);
775 QVariantList Core::authenticatorInfo()
777 QVariantList backends;
778 foreach(const Authenticator *backend, instance()->_authenticatorBackends.values()) {
780 v["DisplayName"] = backend->displayName();
781 v["Description"] = backend->description();
782 v["SetupKeys"] = backend->setupKeys();
783 v["SetupDefaults"] = backend->setupDefaults();
789 // migration / backend selection
790 bool Core::selectBackend(const QString &backend)
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(", "));
800 Storage *storage = _storageBackends[backend];
801 QVariantMap settings = promptForSettings(storage);
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."));
809 qWarning() << "Switched backend to:" << qPrintable(backend);
810 qWarning() << "Backend already initialized. Skipping Migration";
812 case Storage::NotAvailable:
813 qCritical() << "Backend is not available:" << qPrintable(backend);
815 case Storage::NeedsSetup:
816 if (!storage->setup(settings)) {
817 qWarning() << qPrintable(QString("Core::selectBackend(): unable to setup backend: %1").arg(backend));
821 if (storage->init(settings) != Storage::IsReady) {
822 qWarning() << qPrintable(QString("Core::migrateBackend(): unable to initialize backend: %1").arg(backend));
826 if (!saveBackendSettings(backend, settings)) {
827 qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem."));
829 qWarning() << "Switched backend to:" << qPrintable(backend);
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()));
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."));
851 qWarning() << qPrintable(QString("Core::migrateDb(): unable to migrate storage backend! (No migration writer for %1)").arg(backend));
854 // inform the user why we cannot merge
856 qWarning() << "No currently active backend. Skipping migration.";
859 qWarning() << "Currently active backend does not support migration:" << qPrintable(_storage->displayName());
862 qWarning() << "New backend does not support migration:" << qPrintable(backend);
865 // so we were unable to merge, but let's create a user \o/
872 bool Core::createUser()
874 QTextStream out(stdout);
875 QTextStream in(stdin);
876 out << "Add a new user:" << endl;
879 QString username = in.readLine().trimmed();
884 QString password = in.readLine().trimmed();
886 out << "Repeat Password: ";
888 QString password2 = in.readLine().trimmed();
892 if (password != password2) {
893 qWarning() << "Passwords don't match!";
896 if (password.isEmpty()) {
897 qWarning() << "Password is empty!";
901 if (_configured && _storage->addUser(username, password).isValid()) {
902 out << "Added user " << username << " successfully!" << endl;
906 qWarning() << "Unable to add user:" << qPrintable(username);
912 bool Core::changeUserPass(const QString &username)
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;
922 out << "Change password for user: " << username << endl;
925 out << "New Password: ";
927 QString password = in.readLine().trimmed();
929 out << "Repeat Password: ";
931 QString password2 = in.readLine().trimmed();
935 if (password != password2) {
936 qWarning() << "Passwords don't match!";
939 if (password.isEmpty()) {
940 qWarning() << "Password is empty!";
944 if (_configured && _storage->updateUser(userId, password)) {
945 out << "Password changed successfully!" << endl;
949 qWarning() << "Failed to change password!";
955 bool Core::changeUserPassword(UserId userId, const QString &password)
957 if (!isConfigured() || !userId.isValid())
960 return instance()->_storage->updateUser(userId, password);
964 AbstractSqlMigrationReader *Core::getMigrationReader(Storage *storage)
969 AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
971 qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
975 return sqlStorage->createMigrationReader();
979 AbstractSqlMigrationWriter *Core::getMigrationWriter(Storage *storage)
984 AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
986 qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
990 return sqlStorage->createMigrationWriter();
994 bool Core::saveBackendSettings(const QString &backend, const QVariantMap &settings)
996 QVariantMap dbsettings;
997 dbsettings["Backend"] = backend;
998 dbsettings["ConnectionProperties"] = settings;
999 CoreSettings s = CoreSettings();
1000 s.setStorageSettings(dbsettings);
1004 void Core::saveAuthBackendSettings(const QString &backend, const QVariantMap &settings)
1006 QVariantMap dbsettings;
1007 dbsettings["AuthBackend"] = backend;
1008 dbsettings["ConnectionProperties"] = settings;
1009 CoreSettings().setAuthSettings(dbsettings);
1013 QVariantMap Core::promptForSettings(const Storage *storage)
1015 QVariantMap settings;
1017 QStringList keys = storage->setupKeys();
1021 QTextStream out(stdout);
1022 QTextStream in(stdin);
1023 out << "Default values are in brackets" << endl;
1025 QVariantMap defaults = storage->setupDefaults();
1027 foreach(QString key, keys) {
1029 if (defaults.contains(key)) {
1030 val = defaults[key];
1033 if (!val.toString().isEmpty()) {
1034 out << " (" << val.toString() << ")";
1039 bool noEcho = QString("password").toLower().startsWith(key.toLower());
1043 value = in.readLine().trimmed();
1049 if (!value.isEmpty()) {
1050 switch (defaults[key].type()) {
1052 val = QVariant(value.toInt());
1055 val = QVariant(value);
1058 settings[key] = val;
1065 void Core::stdInEcho(bool on)
1067 HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
1069 GetConsoleMode(hStdin, &mode);
1071 mode |= ENABLE_ECHO_INPUT;
1073 mode &= ~ENABLE_ECHO_INPUT;
1074 SetConsoleMode(hStdin, mode);
1079 void Core::stdInEcho(bool on)
1082 tcgetattr(STDIN_FILENO, &t);
1087 tcsetattr(STDIN_FILENO, TCSANOW, &t);
1091 #endif /* Q_OS_WIN */