From: Manuel Nickschas Date: Tue, 1 Nov 2016 16:34:16 +0000 (+0100) Subject: Rework the handling of storage/auth backends and config X-Git-Tag: travis-deploy-test~269 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=cfbd4daee17dbb3c4052d938bf33edd08711d728 Rework the handling of storage/auth backends and config The legacy way of transmitting backend settings to the client for initial core configuration was pretty weird, especially regarding the setupKeys/setupDefaults mess that required guesswork on the client side as of the property types. With the introduction of authenticators, an opportunity presented itself to improve this. Upon fixing this, I realized that lots of pretty crazy legacy code was involved, and partially even duplicated for authenticator handling. I could not keep myself from cleaning that up. In the process, the affected parts of the code were modernized too, leveraging Modern C++ and the STL where possible. The setupKeys/setupDefaults properties of the backend info structs are now deprecated and replaced by a setupData list of field IDs, translatable display names, and default values/types. To keep legacy clients working, the old properties are still transmitted until the next protocol break. --- diff --git a/src/core/abstractsqlstorage.cpp b/src/core/abstractsqlstorage.cpp index c100d4ef..a0cf64c9 100644 --- a/src/core/abstractsqlstorage.cpp +++ b/src/core/abstractsqlstorage.cpp @@ -144,7 +144,7 @@ Storage::State AbstractSqlStorage::init(const QVariantMap &settings) } } - quInfo() << qPrintable(displayName()) << "Storage Backend is ready. Quassel Schema Version:" << installedSchemaVersion(); + quInfo() << qPrintable(displayName()) << "storage backend is ready. Schema version:" << installedSchemaVersion(); return IsReady; } diff --git a/src/core/abstractsqlstorage.h b/src/core/abstractsqlstorage.h index 6c469dfd..8a58be9a 100644 --- a/src/core/abstractsqlstorage.h +++ b/src/core/abstractsqlstorage.h @@ -18,11 +18,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef ABSTRACTSQLSTORAGE_H -#define ABSTRACTSQLSTORAGE_H +#pragma once #include "storage.h" +#include + #include #include #include @@ -38,8 +39,8 @@ public: AbstractSqlStorage(QObject *parent = 0); virtual ~AbstractSqlStorage(); - virtual inline AbstractSqlMigrationReader *createMigrationReader() { return 0; } - virtual inline AbstractSqlMigrationWriter *createMigrationWriter() { return 0; } + virtual std::unique_ptr createMigrationReader() { return {}; } + virtual std::unique_ptr createMigrationWriter() { return {}; } public slots: virtual State init(const QVariantMap &settings = QVariantMap()); @@ -357,6 +358,3 @@ public: virtual inline bool postProcess() { return true; } friend class AbstractSqlMigrationReader; }; - - -#endif diff --git a/src/core/authenticator.cpp b/src/core/authenticator.cpp index 21b6e488..16c83ce3 100644 --- a/src/core/authenticator.cpp +++ b/src/core/authenticator.cpp @@ -18,10 +18,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +// Make moc happy #include "authenticator.h" - -Authenticator::Authenticator(QObject *parent) - : QObject(parent) -{ -} - +#include "moc_authenticator.cpp" diff --git a/src/core/authenticator.h b/src/core/authenticator.h index 3bec6f6f..55930d9a 100644 --- a/src/core/authenticator.h +++ b/src/core/authenticator.h @@ -32,8 +32,8 @@ class Authenticator : public QObject { Q_OBJECT public: - Authenticator(QObject *parent = 0); - virtual ~Authenticator() {}; + using QObject::QObject; + ~Authenticator() override = default; enum State { IsReady, // ready to go @@ -64,16 +64,18 @@ public slots: /** \return A string that can be displayed by the client to describe the authenticator */ virtual QString description() const = 0; - //! Returns a list of properties required to use the authenticator backend - virtual QStringList setupKeys() const = 0; + //! Returns data required to configure the authenticator backend + /** + * A list of flattened triples for each field: {key, translated field name, default value} + * The default value's type determines the kind of input widget to be shown + * (int -> QSpinBox; QString -> QLineEdit) + * \return A list of triples defining the data to be shown in the configuration dialog + */ + virtual QVariantList setupData() const = 0; //! Checks if the authenticator allows manual password changes from inside quassel. virtual bool canChangePassword() const = 0; - //! Returns a map where the keys are are properties to use the authenticator backend - /* the values are QVariants with default values */ - virtual QVariantMap setupDefaults() const = 0; - //! Setup the authenticator provider. /** This prepares the authenticator provider (e.g. create tables, etc.) for use within Quassel. * \param settings Hostname, port, username, password, ... @@ -93,7 +95,4 @@ public slots: * \return A valid UserId if the password matches the username; 0 else */ virtual UserId validateUser(const QString &user, const QString &password) = 0; - -private: - }; diff --git a/src/core/core.cpp b/src/core/core.cpp index 4aab673e..1a418f1d 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -18,6 +18,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include + #include #include "core.h" @@ -88,9 +90,6 @@ void Core::destroy() Core::Core() - : QObject(), - _storage(0), - _authenticator(0) { #ifdef HAVE_UMASK umask(S_IRWXG | S_IRWXO); @@ -123,11 +122,11 @@ Core::Core() # endif QSettings oldSettings(org, "Quassel Core"); if (oldSettings.allKeys().count()) { - qWarning() << "\n\n*** IMPORTANT: Config and data file locations have changed. Attempting to auto-migrate your core settings..."; + quWarning() << "\n\n*** IMPORTANT: Config and data file locations have changed. Attempting to auto-migrate your core settings..."; foreach(QString key, oldSettings.allKeys()) newSettings.setValue(key, oldSettings.value(key)); newSettings.setValue("Config/Version", 1); - qWarning() << "* Your core settings have been migrated to" << newSettings.fileName(); + quWarning() << "* Your core settings have been migrated to" << newSettings.fileName(); #ifndef Q_OS_MAC /* we don't need to move the db and cert for mac */ #ifdef Q_OS_WIN @@ -145,9 +144,9 @@ Core::Core() if (oldDb.exists()) { bool success = oldDb.rename(Quassel::configDirPath() + "quassel-storage.sqlite"); if (success) - qWarning() << "* Your database has been moved to" << Quassel::configDirPath() + "quassel-storage.sqlite"; + quWarning() << "* Your database has been moved to" << Quassel::configDirPath() + "quassel-storage.sqlite"; else - qWarning() << "!!! Moving your database has failed. Please move it manually into" << Quassel::configDirPath(); + quWarning() << "!!! Moving your database has failed. Please move it manually into" << Quassel::configDirPath(); } } // move certificate @@ -156,12 +155,12 @@ Core::Core() QFile cert(quasselDir + "quasselCert.pem"); bool success = cert.rename(Quassel::configDirPath() + "quasselCert.pem"); if (success) - qWarning() << "* Your certificate has been moved to" << Quassel::configDirPath() + "quasselCert.pem"; + quWarning() << "* Your certificate has been moved to" << Quassel::configDirPath() + "quasselCert.pem"; else - qWarning() << "!!! Moving your certificate has failed. Please move it manually into" << Quassel::configDirPath(); + quWarning() << "!!! Moving your certificate has failed. Please move it manually into" << Quassel::configDirPath(); } #endif /* !Q_OS_MAC */ - qWarning() << "*** Migration completed.\n\n"; + quWarning() << "*** Migration completed.\n\n"; } } // MIGRATION end @@ -174,6 +173,7 @@ Core::Core() exit(EXIT_FAILURE); } + // Set up storage and authentication backends registerStorageBackends(); registerAuthenticators(); @@ -190,8 +190,10 @@ void Core::init() _configured = initStorage(dbsettings.value("Backend").toString(), dbsettings.value("ConnectionProperties").toMap()); // Not entirely sure what is 'legacy' about the above, but it seems to be the way things work! - QVariantMap authSettings = cs.authSettings().toMap(); - initAuthenticator(authSettings.value("Authenticator").toString(), authSettings.value("AuthProperties").toMap()); + if (_configured) { + QVariantMap authSettings = cs.authSettings().toMap(); + initAuthenticator(authSettings.value("Authenticator", "Database").toString(), authSettings.value("AuthProperties").toMap()); + } if (Quassel::isOptionSet("select-backend") || Quassel::isOptionSet("select-authenticator")) { if (Quassel::isOptionSet("select-backend")) { @@ -200,19 +202,18 @@ void Core::init() if (Quassel::isOptionSet("select-authenticator")) { selectAuthenticator(Quassel::optionValue("select-authenticator")); } - exit(0); + exit(EXIT_SUCCESS); } if (!_configured) { - if (!_storageBackends.count()) { - qWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting...")); - qWarning() << qPrintable(tr("Currently, Quassel supports SQLite3 and PostgreSQL. You need to build your\n" + if (_registeredStorageBackends.size() == 0) { + quWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting...")); + quWarning() << qPrintable(tr("Currently, Quassel supports SQLite3 and PostgreSQL. You need to build your\n" "Qt library with the sqlite or postgres plugin enabled in order for quasselcore\n" "to work.")); - exit(1); // TODO make this less brutal (especially for mono client -> popup) + exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup) } - - qWarning() << "Core is currently not configured! Please connect with a Quassel Client for basic setup."; + quWarning() << "Core is currently not configured! Please connect with a Quassel Client for basic setup."; if (!cs.isWritable()) { qWarning() << "Cannot write quasselcore configuration; probably a permission problem."; @@ -247,8 +248,6 @@ Core::~Core() handler->deleteLater(); // disconnect non authed clients } qDeleteAll(_sessions); - qDeleteAll(_storageBackends); - qDeleteAll(_authenticators); } @@ -270,18 +269,18 @@ void Core::saveState() void Core::restoreState() { if (!instance()->_configured) { - // qWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!")); + // quWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!")); return; } if (instance()->_sessions.count()) { - qWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!")); + quWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!")); return; } CoreSettings s; /* We don't check, since we are at the first version since switching to Git uint statever = s.coreState().toMap()["CoreStateVersion"].toUInt(); if(statever < 1) { - qWarning() << qPrintable(tr("Core state too old, ignoring...")); + quWarning() << qPrintable(tr("Core state too old, ignoring...")); return; } */ @@ -337,7 +336,7 @@ QString Core::setupCore(const QString &adminUser, const QString &adminPassword, QString Core::setupCoreForInternalUsage() { - Q_ASSERT(!_storageBackends.isEmpty()); + Q_ASSERT(!_registeredStorageBackends.empty()); qsrand(QDateTime::currentDateTime().toTime_t()); int pass = 0; @@ -352,98 +351,47 @@ QString Core::setupCoreForInternalUsage() /*** Storage Handling ***/ -void Core::registerStorageBackends() -{ - // Register storage backends here! - registerStorageBackend(new SqliteStorage(this)); - registerStorageBackend(new PostgreSqlStorage(this)); -} - -bool Core::registerStorageBackend(Storage *backend) +template +void Core::registerStorageBackend() { - if (backend->isAvailable()) { - _storageBackends[backend->displayName()] = backend; - return true; - } - else { + auto backend = makeDeferredShared(this); + if (backend->isAvailable()) + _registeredStorageBackends.emplace_back(std::move(backend)); + else backend->deleteLater(); - return false; - } -} - - -void Core::unregisterStorageBackends() -{ - foreach(Storage *s, _storageBackends.values()) { - s->deleteLater(); - } - _storageBackends.clear(); -} - - -void Core::unregisterStorageBackend(Storage *backend) -{ - _storageBackends.remove(backend->displayName()); - backend->deleteLater(); -} - -// Authentication handling, now independent from storage. -// Register and unregister authenticators. - -void Core::registerAuthenticators() -{ - // Register new authentication backends here! - registerAuthenticator(new SqlAuthenticator(this)); -#ifdef HAVE_LDAP - registerAuthenticator(new LdapAuthenticator(this)); -#endif -} - - -bool Core::registerAuthenticator(Authenticator *authenticator) -{ - if (authenticator->isAvailable()) { - _authenticators[authenticator->backendId()] = authenticator; - return true; - } - else { - authenticator->deleteLater(); - return false; - } } -void Core::unregisterAuthenticators() +void Core::registerStorageBackends() { - foreach(Authenticator* a, _authenticators.values()) { - a->deleteLater(); + if (_registeredStorageBackends.empty()) { + registerStorageBackend(); + registerStorageBackend(); } - _authenticators.clear(); } -void Core::unregisterAuthenticator(Authenticator *backend) +DeferredSharedPtr Core::storageBackend(const QString &backendId) const { - _authenticators.remove(backend->backendId()); - backend->deleteLater(); + auto it = std::find_if(_registeredStorageBackends.begin(), _registeredStorageBackends.end(), + [backendId](const DeferredSharedPtr &backend) { + return backend->displayName() == backendId; + }); + return it != _registeredStorageBackends.end() ? *it : nullptr; } // old db settings: // "Type" => "sqlite" bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool setup) { - _storage = 0; - if (backend.isEmpty()) { + quWarning() << "No storage backend selected!"; return false; } - Storage *storage = 0; - if (_storageBackends.contains(backend)) { - storage = _storageBackends[backend]; - } - else { + auto storage = storageBackend(backend); + if (!storage) { qCritical() << "Selected storage backend is not available:" << backend; return false; } @@ -461,39 +409,91 @@ bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool exit(EXIT_FAILURE); case Storage::IsReady: // delete all other backends - _storageBackends.remove(backend); - unregisterStorageBackends(); - connect(storage, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &))); + _registeredStorageBackends.clear(); + connect(storage.get(), SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), + this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &))); + break; } - _storage = storage; + _storage = std::move(storage); return true; } + +void Core::syncStorage() +{ + if (_storage) + _storage->sync(); +} + + +/*** Storage Access ***/ +bool Core::createNetwork(UserId user, NetworkInfo &info) +{ + NetworkId networkId = instance()->_storage->createNetwork(user, info); + if (!networkId.isValid()) + return false; + + info.networkId = networkId; + return true; +} + + +/*** Authenticators ***/ + +// Authentication handling, now independent from storage. +template +void Core::registerAuthenticator() +{ + auto authenticator = makeDeferredShared(this); + if (authenticator->isAvailable()) + _registeredAuthenticators.emplace_back(std::move(authenticator)); + else + authenticator->deleteLater(); +} + + +void Core::registerAuthenticators() +{ + if (_registeredAuthenticators.empty()) { + registerAuthenticator(); +#ifdef HAVE_LDAP + registerAuthenticator(); +#endif + } +} + + +DeferredSharedPtr Core::authenticator(const QString &backendId) const +{ + auto it = std::find_if(_registeredAuthenticators.begin(), _registeredAuthenticators.end(), + [backendId](const DeferredSharedPtr &authenticator) { + return authenticator->backendId() == backendId; + }); + return it != _registeredAuthenticators.end() ? *it : nullptr; +} + + // FIXME: Apparently, this is the legacy way of initting storage backends? // If there's a not-legacy way, it should be used here bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings, bool setup) { - _authenticator = 0; - if (backend.isEmpty()) { + quWarning() << "No authenticator selected!"; return false; } - Authenticator *authenticator = 0; - if (_authenticators.contains(backend)) { - authenticator = _authenticators[backend]; - } - else { + auto auth = authenticator(backend); + if (!auth) { qCritical() << "Selected auth backend is not available:" << backend; return false; } - Authenticator::State authState = authenticator->init(settings); + Authenticator::State authState = auth->init(settings); switch (authState) { case Authenticator::NeedsSetup: if (!setup) return false; // trigger setup process - if (authenticator->setup(settings)) + if (auth->setup(settings)) return initAuthenticator(backend, settings, false); // if initialization wasn't successful, we quit to keep from coming up unconfigured case Authenticator::NotAvailable: @@ -501,29 +501,10 @@ bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings exit(EXIT_FAILURE); case Authenticator::IsReady: // delete all other backends - _authenticators.remove(backend); - unregisterAuthenticators(); + _registeredAuthenticators.clear(); + break; } - _authenticator = authenticator; - return true; -} - - -void Core::syncStorage() -{ - if (_storage) - _storage->sync(); -} - - -/*** Storage Access ***/ -bool Core::createNetwork(UserId user, NetworkInfo &info) -{ - NetworkId networkId = instance()->_storage->createNetwork(user, info); - if (!networkId.isValid()) - return false; - - info.networkId = networkId; + _authenticator = std::move(auth); return true; } @@ -746,7 +727,7 @@ void Core::setupInternalClientSession(InternalPeer *clientPeer) uid = _storage->internalUser(); } else { - qWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!"; + quWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!"; return; } @@ -774,39 +755,55 @@ SessionThread *Core::sessionForUser(UserId uid, bool restore) void Core::socketError(QAbstractSocket::SocketError err, const QString &errorString) { - qWarning() << QString("Socket error %1: %2").arg(err).arg(errorString); + quWarning() << QString("Socket error %1: %2").arg(err).arg(errorString); } QVariantList Core::backendInfo() { - QVariantList backends; - foreach(const Storage *backend, instance()->_storageBackends.values()) { + instance()->registerStorageBackends(); + + QVariantList backendInfos; + for (auto &&backend : instance()->_registeredStorageBackends) { QVariantMap v; + v["BackendId"] = backend->backendId(); v["DisplayName"] = backend->displayName(); v["Description"] = backend->description(); - v["SetupKeys"] = backend->setupKeys(); - v["SetupDefaults"] = backend->setupDefaults(); - v["IsDefault"] = isStorageBackendDefault(backend); - backends.append(v); + v["SetupData"] = backend->setupData(); // ignored by legacy clients + + // TODO Protocol Break: Remove legacy (cf. authenticatorInfo()) + const auto &setupData = backend->setupData(); + QStringList setupKeys; + QVariantMap setupDefaults; + for (int i = 0; i + 2 < setupData.size(); i += 3) { + setupKeys << setupData[i].toString(); + setupDefaults[setupData[i].toString()] = setupData[i + 2]; + } + v["SetupKeys"] = setupKeys; + v["SetupDefaults"] = setupDefaults; + // TODO Protocol Break: Remove + v["IsDefault"] = (backend->backendId() == "SQLite"); // newer clients will just use the first in the list + + backendInfos << v; } - return backends; + return backendInfos; } QVariantList Core::authenticatorInfo() { - QVariantList backends; - foreach(const Authenticator *backend, instance()->_authenticators.values()) { + instance()->registerAuthenticators(); + + QVariantList authInfos; + for(auto &&backend : instance()->_registeredAuthenticators) { QVariantMap v; - v["BackendId"] = backend->backendId(); + v["BackendId"] = backend->backendId(); v["DisplayName"] = backend->displayName(); v["Description"] = backend->description(); - v["SetupKeys"] = backend->setupKeys(); - v["SetupDefaults"] = backend->setupDefaults(); - backends.append(v); + v["SetupData"] = backend->setupData(); + authInfos << v; } - return backends; + return authInfos; } // migration / backend selection @@ -814,14 +811,19 @@ bool Core::selectBackend(const QString &backend) { // reregister all storage backends registerStorageBackends(); - if (!_storageBackends.contains(backend)) { - qWarning() << qPrintable(QString("Core::selectBackend(): unsupported backend: %1").arg(backend)); - qWarning() << " supported backends are:" << qPrintable(QStringList(_storageBackends.keys()).join(", ")); + auto storage = storageBackend(backend); + if (!storage) { + QStringList backends; + std::transform(_registeredStorageBackends.begin(), _registeredStorageBackends.end(), + std::back_inserter(backends), [](const DeferredSharedPtr& backend) { + return backend->displayName(); + }); + quWarning() << qPrintable(tr("Unsupported storage backend: %1").arg(backend)); + quWarning() << qPrintable(tr("Supported backends are:")) << qPrintable(backends.join(", ")); return false; } - Storage *storage = _storageBackends[backend]; - QVariantMap settings = promptForSettings(storage); + QVariantMap settings = promptForSettings(storage.get()); Storage::State storageState = storage->init(settings); switch (storageState) { @@ -829,64 +831,63 @@ bool Core::selectBackend(const QString &backend) if (!saveBackendSettings(backend, settings)) { qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem.")); } - qWarning() << "Switched backend to:" << qPrintable(backend); - qWarning() << "Backend already initialized. Skipping Migration"; + quWarning() << qPrintable(tr("Switched storage backend to: %1").arg(backend)); + quWarning() << qPrintable(tr("Backend already initialized. Skipping Migration...")); return true; case Storage::NotAvailable: - qCritical() << "Backend is not available:" << qPrintable(backend); + qCritical() << qPrintable(tr("Storage backend is not available: %1").arg(backend)); return false; case Storage::NeedsSetup: if (!storage->setup(settings)) { - qWarning() << qPrintable(QString("Core::selectBackend(): unable to setup backend: %1").arg(backend)); + quWarning() << qPrintable(tr("Unable to setup storage backend: %1").arg(backend)); return false; } if (storage->init(settings) != Storage::IsReady) { - qWarning() << qPrintable(QString("Core::migrateBackend(): unable to initialize backend: %1").arg(backend)); + quWarning() << qPrintable(tr("Unable to initialize storage backend: %1").arg(backend)); return false; } if (!saveBackendSettings(backend, settings)) { qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem.")); } - qWarning() << "Switched backend to:" << qPrintable(backend); + quWarning() << qPrintable(tr("Switched storage backend to: %1").arg(backend)); break; } // let's see if we have a current storage object we can migrate from - AbstractSqlMigrationReader *reader = getMigrationReader(_storage); - AbstractSqlMigrationWriter *writer = getMigrationWriter(storage); + auto reader = getMigrationReader(_storage.get()); + auto writer = getMigrationWriter(storage.get()); if (reader && writer) { - qDebug() << qPrintable(QString("Migrating Storage backend %1 to %2...").arg(_storage->displayName(), storage->displayName())); - delete _storage; - _storage = 0; - delete storage; - storage = 0; + qDebug() << qPrintable(tr("Migrating storage backend %1 to %2...").arg(_storage->displayName(), storage->displayName())); + _storage.reset(); + storage.reset(); if (reader->migrateTo(writer)) { qDebug() << "Migration finished!"; + qDebug() << qPrintable(tr("Migration finished!")); if (!saveBackendSettings(backend, settings)) { qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem.")); return false; } return true; } + quWarning() << qPrintable(tr("Unable to migrate storage backend! (No migration writer for %1)").arg(backend)); return false; - qWarning() << qPrintable(QString("Core::migrateDb(): unable to migrate storage backend! (No migration writer for %1)").arg(backend)); } // inform the user why we cannot merge if (!_storage) { - qWarning() << "No currently active backend. Skipping migration."; + quWarning() << qPrintable(tr("No currently active storage backend. Skipping migration...")); } else if (!reader) { - qWarning() << "Currently active backend does not support migration:" << qPrintable(_storage->displayName()); + quWarning() << qPrintable(tr("Currently active storage backend does not support migration: %1").arg(_storage->displayName())); } if (writer) { - qWarning() << "New backend does not support migration:" << qPrintable(backend); + quWarning() << qPrintable(tr("New storage backend does not support migration: %1").arg(backend)); } // so we were unable to merge, but let's create a user \o/ - _storage = storage; + _storage = std::move(storage); createUser(); return true; } @@ -897,41 +898,45 @@ bool Core::selectAuthenticator(const QString &backend) { // Register all authentication backends. registerAuthenticators(); - if (!_authenticators.contains(backend)) { - qWarning() << qPrintable(QString("Core::selectAuthenticator(): unsupported backend: %1").arg(backend)); - qWarning() << " supported backends are:" << qPrintable(QStringList(_authenticators.keys()).join(", ")); + auto auther = authenticator(backend); + if (!auther) { + QStringList authenticators; + std::transform(_registeredAuthenticators.begin(), _registeredAuthenticators.end(), + std::back_inserter(authenticators), [](const DeferredSharedPtr& authenticator) { + return authenticator->displayName(); + }); + quWarning() << qPrintable(tr("Unsupported authenticator: %1").arg(backend)); + quWarning() << qPrintable(tr("Supported authenticators are:")) << qPrintable(authenticators.join(", ")); return false; } - Authenticator *authenticator = _authenticators[backend]; - QVariantMap settings = promptForSettings(authenticator); + QVariantMap settings = promptForSettings(auther.get()); - Authenticator::State state = authenticator->init(settings); + Authenticator::State state = auther->init(settings); switch (state) { case Authenticator::IsReady: saveAuthenticatorSettings(backend, settings); - qWarning() << "Switched auth backend to:" << qPrintable(backend); -// qWarning() << "Auth backend already initialized. Skipping Migration"; + quWarning() << qPrintable(tr("Switched authenticator to: %1").arg(backend)); return true; case Authenticator::NotAvailable: - qCritical() << "Auth backend is not available:" << qPrintable(backend); + qCritical() << qPrintable(tr("Authenticator is not available: %1").arg(backend)); return false; case Authenticator::NeedsSetup: - if (!authenticator->setup(settings)) { - qWarning() << qPrintable(QString("Core::selectAuthenticator(): unable to setup authenticator: %1").arg(backend)); + if (!auther->setup(settings)) { + quWarning() << qPrintable(tr("Unable to setup authenticator: %1").arg(backend)); return false; } - if (authenticator->init(settings) != Authenticator::IsReady) { - qWarning() << qPrintable(QString("Core::migrateBackend(): unable to initialize authenticator: %1").arg(backend)); + if (auther->init(settings) != Authenticator::IsReady) { + quWarning() << qPrintable(tr("Unable to initialize authenticator: %1").arg(backend)); return false; } saveAuthenticatorSettings(backend, settings); - qWarning() << "Switched auth backend to:" << qPrintable(backend); + quWarning() << qPrintable(tr("Switched authenticator to: %1").arg(backend)); } - _authenticator = authenticator; + _authenticator = std::move(auther); return true; } @@ -957,11 +962,11 @@ bool Core::createUser() enableStdInEcho(); if (password != password2) { - qWarning() << "Passwords don't match!"; + quWarning() << "Passwords don't match!"; return false; } if (password.isEmpty()) { - qWarning() << "Password is empty!"; + quWarning() << "Password is empty!"; return false; } @@ -970,7 +975,7 @@ bool Core::createUser() return true; } else { - qWarning() << "Unable to add user:" << qPrintable(username); + quWarning() << "Unable to add user:" << qPrintable(username); return false; } } @@ -1005,11 +1010,11 @@ bool Core::changeUserPass(const QString &username) enableStdInEcho(); if (password != password2) { - qWarning() << "Passwords don't match!"; + quWarning() << "Passwords don't match!"; return false; } if (password.isEmpty()) { - qWarning() << "Password is empty!"; + quWarning() << "Password is empty!"; return false; } @@ -1018,7 +1023,7 @@ bool Core::changeUserPass(const QString &username) return true; } else { - qWarning() << "Failed to change password!"; + quWarning() << "Failed to change password!"; return false; } } @@ -1054,30 +1059,30 @@ bool Core::canChangeUserPassword(UserId userId) } -AbstractSqlMigrationReader *Core::getMigrationReader(Storage *storage) +std::unique_ptr Core::getMigrationReader(Storage *storage) { if (!storage) - return 0; + return nullptr; AbstractSqlStorage *sqlStorage = qobject_cast(storage); if (!sqlStorage) { qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!"; - return 0; + return nullptr; } return sqlStorage->createMigrationReader(); } -AbstractSqlMigrationWriter *Core::getMigrationWriter(Storage *storage) +std::unique_ptr Core::getMigrationWriter(Storage *storage) { if (!storage) - return 0; + return nullptr; AbstractSqlStorage *sqlStorage = qobject_cast(storage); if (!sqlStorage) { qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!"; - return 0; + return nullptr; } return sqlStorage->createMigrationWriter(); @@ -1105,71 +1110,48 @@ void Core::saveAuthenticatorSettings(const QString &backend, const QVariantMap & // Generic version of promptForSettings that doesn't care what *type* of // backend it runs over. -QVariantMap Core::promptForSettings(QStringList keys, QVariantMap defaults) +template +QVariantMap Core::promptForSettings(const Backend *backend) { QVariantMap settings; + const QVariantList& setupData = backend->setupData(); - if (keys.isEmpty()) + if (setupData.isEmpty()) return settings; QTextStream out(stdout); QTextStream in(stdin); out << "Default values are in brackets" << endl; - QString value; - foreach(QString key, keys) { - QVariant val; - if (defaults.contains(key)) { - val = defaults[key]; - } - out << key; - if (!val.toString().isEmpty()) { - out << " (" << val.toString() << ")"; - } - out << ": "; - out.flush(); + for (int i = 0; i + 2 < setupData.size(); i += 3) { + QString key = setupData[i].toString(); + out << setupData[i+1].toString() << " [" << setupData[i+2].toString() << "]: " << flush; - bool noEcho = QString("password").toLower().startsWith(key.toLower()); + bool noEcho = key.toLower().contains("password"); if (noEcho) { disableStdInEcho(); } - value = in.readLine().trimmed(); + QString input = in.readLine().trimmed(); if (noEcho) { out << endl; enableStdInEcho(); } - if (!value.isEmpty()) { - switch (defaults[key].type()) { + QVariant value{setupData[i+2]}; + if (!input.isEmpty()) { + switch (value.type()) { case QVariant::Int: - val = QVariant(value.toInt()); + value = input.toInt(); break; default: - val = QVariant(value); + value = input; } } - settings[key] = val; + settings[key] = value; } return settings; } -// Since an auth and storage backend work basically the same way, -// use polymorphism here on this routine. -QVariantMap Core::promptForSettings(const Storage *storage) -{ - QStringList keys = storage->setupKeys(); - QVariantMap defaults = storage->setupDefaults(); - return Core::promptForSettings(keys, defaults); -} - - -QVariantMap Core::promptForSettings(const Authenticator *authenticator) -{ - QStringList keys = authenticator->setupKeys(); - QVariantMap defaults = authenticator->setupDefaults(); - return Core::promptForSettings(keys, defaults); -} - #ifdef Q_OS_WIN void Core::stdInEcho(bool on) @@ -1184,7 +1166,6 @@ void Core::stdInEcho(bool on) SetConsoleMode(hStdin, mode); } - #else void Core::stdInEcho(bool on) { @@ -1197,5 +1178,4 @@ void Core::stdInEcho(bool on) tcsetattr(STDIN_FILENO, TCSANOW, &t); } - #endif /* Q_OS_WIN */ diff --git a/src/core/core.h b/src/core/core.h index 66fc50b0..333d0670 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -18,8 +18,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef CORE_H -#define CORE_H +#pragma once + +#include +#include #include #include @@ -36,6 +38,7 @@ #include "authenticator.h" #include "bufferinfo.h" +#include "deferredptr.h" #include "message.h" #include "oidentdconfiggenerator.h" #include "sessionthread.h" @@ -543,19 +546,6 @@ public: static QVariantList backendInfo(); static QVariantList authenticatorInfo(); - /** - * Checks if a storage backend is the default storage backend. This - * hardcodes this information into the core (not the client). - * - * \param backend The backend to check. - * - * @return True if storage backend is default, false otherwise. - */ - static inline bool isStorageBackendDefault(const Storage *backend) - { - return (backend->displayName() == "SQLite") ? true : false; - } - static QString setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authenticator, const QVariantMap &authSetupMap); static inline QTimer &syncTimer() { return instance()->_storageSyncTimer; } @@ -607,34 +597,34 @@ private: //void processCoreSetup(QTcpSocket *socket, QVariantMap &msg); QString setupCoreForInternalUsage(); - void registerStorageBackends(); - bool registerStorageBackend(Storage *); - void unregisterStorageBackends(); - void unregisterStorageBackend(Storage *); + bool createUser(); + template + void registerStorageBackend(); + + template + void registerAuthenticator(); + + void registerStorageBackends(); void registerAuthenticators(); - bool registerAuthenticator(Authenticator *); - void unregisterAuthenticators(); - void unregisterAuthenticator(Authenticator *); + + DeferredSharedPtr storageBackend(const QString& backendId) const; + DeferredSharedPtr authenticator(const QString& authenticatorId) const; bool selectBackend(const QString &backend); bool selectAuthenticator(const QString &backend); - bool createUser(); bool saveBackendSettings(const QString &backend, const QVariantMap &settings); void saveAuthenticatorSettings(const QString &backend, const QVariantMap &settings); - QVariantMap promptForSettings(const Storage *storage); - QVariantMap promptForSettings(const Authenticator *authenticator); - QVariantMap promptForSettings(QStringList keys, QVariantMap defaults); + template + QVariantMap promptForSettings(const Backend *backend); private: QSet _connectingClients; QHash _sessions; - - // Have both a storage backend and an authenticator backend. - Storage *_storage; - Authenticator *_authenticator; + DeferredSharedPtr _storage; ///< Active storage backend + DeferredSharedPtr _authenticator; ///< Active authenticator QTimer _storageSyncTimer; #ifdef HAVE_SSL @@ -643,21 +633,18 @@ private: QTcpServer _server, _v6server; #endif - OidentdConfigGenerator *_oidentdConfigGenerator; + OidentdConfigGenerator *_oidentdConfigGenerator {nullptr}; - QHash _storageBackends; - QHash _authenticators; + std::vector> _registeredStorageBackends; + std::vector> _registeredAuthenticators; QDateTime _startTime; bool _configured; - static AbstractSqlMigrationReader *getMigrationReader(Storage *storage); - static AbstractSqlMigrationWriter *getMigrationWriter(Storage *storage); + static std::unique_ptr getMigrationReader(Storage *storage); + static std::unique_ptr getMigrationWriter(Storage *storage); static void stdInEcho(bool on); static inline void enableStdInEcho() { stdInEcho(true); } static inline void disableStdInEcho() { stdInEcho(false); } }; - - -#endif diff --git a/src/core/ldapauthenticator.cpp b/src/core/ldapauthenticator.cpp index 227df19d..34768faa 100644 --- a/src/core/ldapauthenticator.cpp +++ b/src/core/ldapauthenticator.cpp @@ -83,28 +83,20 @@ QString LdapAuthenticator::description() const return tr("Authenticate users using an LDAP server."); } -QStringList LdapAuthenticator::setupKeys() const -{ - // The parameters needed for LDAP. - QStringList keys; - keys << "Hostname" - << "Port" - << "Bind DN" - << "Bind Password" - << "Base DN" - << "Filter" - << "UID Attribute"; - return keys; -} - -QVariantMap LdapAuthenticator::setupDefaults() const +QVariantList LdapAuthenticator::setupData() const { - QVariantMap map; - map["Hostname"] = QVariant(QString("ldap://localhost")); - map["Port"] = QVariant(DEFAULT_LDAP_PORT); - map["UID Attribute"] = QVariant(QString("uid")); - return map; + // The parameters needed for LDAP. + QVariantList data; + data << "Hostname" << tr("Hostname") << QString{"ldap://localhost"} + << "Port" << tr("Port") << DEFAULT_LDAP_PORT + << "BindDN" << tr("Bind DN") << QString{} + << "BindPassword" << tr("Bind Password") << QString{} + << "BaseDN" << tr("Base DN") << QString{} + << "Filter" << tr("Filter") << QString{} + << "UidAttribute" << tr("UID Attribute") << QString{"uid"} + ; + return data; } @@ -112,11 +104,11 @@ void LdapAuthenticator::setAuthProperties(const QVariantMap &properties) { _hostName = properties["Hostname"].toString(); _port = properties["Port"].toInt(); - _baseDN = properties["Base DN"].toString(); + _bindDN = properties["BindDN"].toString(); + _bindPassword = properties["BindPassword"].toString(); + _baseDN = properties["BaseDN"].toString(); _filter = properties["Filter"].toString(); - _bindDN = properties["Bind DN"].toString(); - _bindPassword = properties["Bind Password"].toString(); - _uidAttribute = properties["UID Attribute"].toString(); + _uidAttribute = properties["UidAttribute"].toString(); } // TODO: this code is sufficiently general that in the future, perhaps an abstract @@ -160,11 +152,11 @@ Authenticator::State LdapAuthenticator::init(const QVariantMap &settings) bool status = ldapConnect(); if (!status) { - quInfo() << qPrintable(backendId()) << "Authenticator cannot connect."; + quInfo() << qPrintable(backendId()) << "authenticator cannot connect."; return NotAvailable; } - quInfo() << qPrintable(backendId()) << "Authenticator is ready."; + quInfo() << qPrintable(backendId()) << "authenticator is ready."; return IsReady; } diff --git a/src/core/ldapauthenticator.h b/src/core/ldapauthenticator.h index 8415496f..4e586129 100644 --- a/src/core/ldapauthenticator.h +++ b/src/core/ldapauthenticator.h @@ -43,7 +43,7 @@ //#endif // Default LDAP server port. -#define DEFAULT_LDAP_PORT 389 +constexpr int DEFAULT_LDAP_PORT = 389; class LdapAuthenticator : public Authenticator { @@ -51,34 +51,33 @@ class LdapAuthenticator : public Authenticator public: LdapAuthenticator(QObject *parent = 0); - virtual ~LdapAuthenticator(); + ~LdapAuthenticator() override; public slots: /* General */ - bool isAvailable() const; - QString backendId() const; - QString displayName() const; - QString description() const; - virtual QStringList setupKeys() const; - virtual QVariantMap setupDefaults() const; + bool isAvailable() const override; + QString backendId() const override; + QString displayName() const override; + QString description() const override; + QVariantList setupData() const override; - virtual inline bool canChangePassword() const { return false; } + bool canChangePassword() const override { return false; } - bool setup(const QVariantMap &settings = QVariantMap()); - State init(const QVariantMap &settings = QVariantMap()); - UserId validateUser(const QString &user, const QString &password); + bool setup(const QVariantMap &settings = {}) override; + State init(const QVariantMap &settings = {}) override; + UserId validateUser(const QString &user, const QString &password) override; protected: - virtual void setAuthProperties(const QVariantMap &properties); + void setAuthProperties(const QVariantMap &properties); bool ldapConnect(); void ldapDisconnect(); bool ldapAuth(const QString &username, const QString &password); // Protected methods for retrieving info about the LDAP connection. - inline virtual QString hostName() { return _hostName; } - inline virtual int port() { return _port; } - inline virtual QString bindDN() { return _bindDN; } - inline virtual QString baseDN() { return _baseDN; } + QString hostName() const { return _hostName; } + int port() const { return _port; } + QString bindDN() const { return _bindDN; } + QString baseDN() const { return _baseDN; } private: QString _hostName; @@ -90,6 +89,5 @@ private: QString _uidAttribute; // The actual connection object. - LDAP *_connection; - + LDAP *_connection {nullptr}; }; diff --git a/src/core/postgresqlstorage.cpp b/src/core/postgresqlstorage.cpp index ce543ae0..ac6dec93 100644 --- a/src/core/postgresqlstorage.cpp +++ b/src/core/postgresqlstorage.cpp @@ -38,9 +38,9 @@ PostgreSqlStorage::~PostgreSqlStorage() } -AbstractSqlMigrationWriter *PostgreSqlStorage::createMigrationWriter() +std::unique_ptr PostgreSqlStorage::createMigrationWriter() { - PostgreSqlMigrationWriter *writer = new PostgreSqlMigrationWriter(); + auto writer = new PostgreSqlMigrationWriter(); QVariantMap properties; properties["Username"] = _userName; properties["Password"] = _password; @@ -48,51 +48,50 @@ AbstractSqlMigrationWriter *PostgreSqlStorage::createMigrationWriter() properties["Port"] = _port; properties["Database"] = _databaseName; writer->setConnectionProperties(properties); - return writer; + return std::unique_ptr{writer}; } bool PostgreSqlStorage::isAvailable() const { - qDebug() << QSqlDatabase::drivers(); - if (!QSqlDatabase::isDriverAvailable("QPSQL")) return false; + if (!QSqlDatabase::isDriverAvailable("QPSQL")) { + quWarning() << qPrintable(tr("PostgreSQL driver plugin not available for Qt. Installed drivers:")) + << qPrintable(QSqlDatabase::drivers().join(", ")); + return false; + } return true; } -QString PostgreSqlStorage::displayName() const +QString PostgreSqlStorage::backendId() const { return QString("PostgreSQL"); } -QString PostgreSqlStorage::description() const +QString PostgreSqlStorage::displayName() const { - // FIXME: proper description - return tr("PostgreSQL Turbo Bomber HD!"); + return backendId(); // Note: Pre-0.13 clients use the displayName property for backend idenfication } -QStringList PostgreSqlStorage::setupKeys() const +QString PostgreSqlStorage::description() const { - QStringList keys; - keys << "Username" - << "Password" - << "Hostname" - << "Port" - << "Database"; - return keys; + // FIXME: proper description + return tr("PostgreSQL Turbo Bomber HD!"); } -QVariantMap PostgreSqlStorage::setupDefaults() const +QVariantList PostgreSqlStorage::setupData() const { - QVariantMap map; - map["Username"] = QVariant(QString("quassel")); - map["Hostname"] = QVariant(QString("localhost")); - map["Port"] = QVariant(5432); - map["Database"] = QVariant(QString("quassel")); - return map; + QVariantList data; + data << "Username" << tr("Username") << QString("quassel") + << "Password" << tr("Password") << QString() + << "Hostname" << tr("Hostname") << QString("localhost") + << "Port" << tr("Port") << 5432 + << "Database" << tr("Database") << QString("quassel") + ; + return data; } diff --git a/src/core/postgresqlstorage.h b/src/core/postgresqlstorage.h index 530e7aad..c11a819e 100644 --- a/src/core/postgresqlstorage.h +++ b/src/core/postgresqlstorage.h @@ -34,15 +34,15 @@ public: PostgreSqlStorage(QObject *parent = 0); virtual ~PostgreSqlStorage(); - virtual AbstractSqlMigrationWriter *createMigrationWriter(); + virtual std::unique_ptr createMigrationWriter(); public slots: /* General */ virtual bool isAvailable() const; + virtual QString backendId() const; virtual QString displayName() const; virtual QString description() const; - virtual QStringList setupKeys() const; - virtual QVariantMap setupDefaults() const; + virtual QVariantList setupData() const; // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc diff --git a/src/core/sqlauthenticator.cpp b/src/core/sqlauthenticator.cpp index 3be817b9..b6984d2b 100644 --- a/src/core/sqlauthenticator.cpp +++ b/src/core/sqlauthenticator.cpp @@ -61,8 +61,8 @@ QString SqlAuthenticator::displayName() const QString SqlAuthenticator::description() const { - return tr("Do not auth against any remote authentication service, but instead save a hashed and salted password " - "in the selected database."); + return tr("Do not authenticate against any remote service, but instead save a hashed and salted password " + "in the database selected in the next step."); } @@ -86,6 +86,6 @@ Authenticator::State SqlAuthenticator::init(const QVariantMap &settings) // TODO: FIXME: this should check if the storage provider is ready, but I don't // know if there's an exposed way to do that at the moment. - quInfo() << qPrintable(backendId()) << "Authenticator is ready."; + quInfo() << qPrintable(backendId()) << "authenticator is ready."; return IsReady; } diff --git a/src/core/sqlauthenticator.h b/src/core/sqlauthenticator.h index b5a010a9..41b366f6 100644 --- a/src/core/sqlauthenticator.h +++ b/src/core/sqlauthenticator.h @@ -36,8 +36,7 @@ public slots: QString backendId() const; QString displayName() const; QString description() const; - virtual inline QStringList setupKeys() const { return QStringList(); } - virtual inline QVariantMap setupDefaults() const { return QVariantMap(); } + virtual inline QVariantList setupData() const { return {}; } virtual inline bool canChangePassword() const { return true; } @@ -47,5 +46,4 @@ public slots: /* User handling */ //virtual UserId getUserId(const QString &username); - }; diff --git a/src/core/sqlitestorage.cpp b/src/core/sqlitestorage.cpp index 6a8965b5..c14d7cb7 100644 --- a/src/core/sqlitestorage.cpp +++ b/src/core/sqlitestorage.cpp @@ -46,12 +46,19 @@ bool SqliteStorage::isAvailable() const } +QString SqliteStorage::backendId() const +{ + return QString("SQLite"); +} + + QString SqliteStorage::displayName() const { + // Note: Pre-0.13 clients use the displayName property for backend idenfication // We identify the backend to use for the monolithic core by its displayname. // so only change this string if you _really_ have to and make sure the core // setup for the mono client still works ;) - return QString("SQLite"); + return backendId(); } diff --git a/src/core/sqlitestorage.h b/src/core/sqlitestorage.h index 8287f384..54f1cb4e 100644 --- a/src/core/sqlitestorage.h +++ b/src/core/sqlitestorage.h @@ -35,15 +35,15 @@ public: SqliteStorage(QObject *parent = 0); virtual ~SqliteStorage(); - virtual AbstractSqlMigrationReader *createMigrationReader(); + virtual std::unique_ptr createMigrationReader(); public slots: /* General */ bool isAvailable() const; + QString backendId() const; QString displayName() const; - virtual inline QStringList setupKeys() const { return QStringList(); } - virtual inline QVariantMap setupDefaults() const { return QVariantMap(); } + virtual inline QVariantList setupData() const { return {}; } QString description() const; // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc @@ -161,9 +161,9 @@ private: }; -inline AbstractSqlMigrationReader *SqliteStorage::createMigrationReader() +inline std::unique_ptr SqliteStorage::createMigrationReader() { - return new SqliteMigrationReader(); + return std::unique_ptr{new SqliteMigrationReader()}; } diff --git a/src/core/storage.h b/src/core/storage.h index 29a8df87..fec8d861 100644 --- a/src/core/storage.h +++ b/src/core/storage.h @@ -46,7 +46,7 @@ public: Sha1, Sha2_512, Latest=Sha2_512 - + }; public slots: @@ -59,6 +59,10 @@ public slots: */ virtual bool isAvailable() const = 0; + //! Returns the identifier of the authenticator backend + /** \return A string that can be used by the client to identify the authenticator backend */ + virtual QString backendId() const = 0; + //! Returns the display name of the storage backend /** \return A string that can be used by the client to name the storage backend */ virtual QString displayName() const = 0; @@ -67,12 +71,14 @@ public slots: /** \return A string that can be displayed by the client to describe the storage backend */ virtual QString description() const = 0; - //! Returns a list of properties required to use the storage backend - virtual QStringList setupKeys() const = 0; - - //! Returns a map where the keys are are properties to use the storage backend - /* the values are QVariants with default values */ - virtual QVariantMap setupDefaults() const = 0; + //! Returns data required to configure the authenticator backend + /** + * A list of flattened triples for each field: {key, translated field name, default value} + * The default value's type determines the kind of input widget to be shown + * (int -> QSpinBox; QString -> QLineEdit) + * \return A list of triples defining the data to be shown in the configuration dialog + */ + virtual QVariantList setupData() const = 0; //! Setup the storage provider. /** This prepares the storage provider (e.g. create tables, etc.) for use within Quassel. diff --git a/src/qtui/coreconfigwizard.cpp b/src/qtui/coreconfigwizard.cpp index 401fd616..18133f07 100644 --- a/src/qtui/coreconfigwizard.cpp +++ b/src/qtui/coreconfigwizard.cpp @@ -29,23 +29,95 @@ #include "client.h" -CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList &backends, const QList &authenticators, QWidget *parent) +namespace { + +template +void createFieldWidgets(QGroupBox *fieldBox, const std::vector &fieldInfos) +{ + // Create a config UI based on the field types sent from the backend + // We make some assumptions here (like integer range and password field names) that may not + // hold true for future authenticator types - but the only way around it for now would be to + // provide specialized config widgets for those (which may be a good idea anyway, e.g. if we + // think about client-side translations...) + + QFormLayout *formLayout = new QFormLayout; + for (auto &&fieldInfo : fieldInfos) { + QWidget *widget {nullptr}; + switch (std::get<2>(fieldInfo).type()) { + case QVariant::Int: + widget = new QSpinBox(fieldBox); + // Here we assume that int fields are always in 16 bit range, like ports + static_cast(widget)->setMinimum(0); + static_cast(widget)->setMaximum(65535); + static_cast(widget)->setValue(std::get<2>(fieldInfo).toInt()); + break; + case QVariant::String: + widget = new QLineEdit(std::get<2>(fieldInfo).toString(), fieldBox); + // Here we assume that fields named something with "password" are actual password inputs + if (std::get<0>(fieldInfo).toLower().contains("password")) + static_cast(widget)->setEchoMode(QLineEdit::Password); + break; + default: + qWarning() << "Unsupported type for backend property" << std::get<0>(fieldInfo); + } + if (widget) { + widget->setObjectName(std::get<0>(fieldInfo)); + formLayout->addRow(std::get<1>(fieldInfo) + ":", widget); + } + } + fieldBox->setLayout(formLayout); +} + + +template +QVariantMap propertiesFromFieldWidgets(QGroupBox *fieldBox, const std::vector &fieldInfos) +{ + QVariantMap properties; + if (!fieldBox) + return properties; + + for (auto &&fieldInfo : fieldInfos) { + QString key = std::get<0>(fieldInfo); + QVariant value; + switch (std::get<2>(fieldInfo).type()) { + case QVariant::Int: { + QSpinBox *spinBox = fieldBox->findChild(key); + if (spinBox) + value = spinBox->value(); + else + qWarning() << "Could not find child widget for field" << key; + break; + } + case QVariant::String: { + QLineEdit *lineEdit = fieldBox->findChild(key); + if (lineEdit) + value = lineEdit->text(); + else + qWarning() << "Could not find child widget for field" << key; + break; + } + default: + qWarning() << "Unsupported type for backend property" << key; + } + properties[key] = std::move(value); + } + return properties; +} + +} // anon + + +CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QVariantList &backendInfos, const QVariantList &authInfos, QWidget *parent) : QWizard(parent), - _connection(connection) + _connection{connection} { setModal(true); setAttribute(Qt::WA_DeleteOnClose); - foreach(const QVariant &v, backends) - _backends[v.toMap()["DisplayName"].toString()] = v; - - foreach(const QVariant &v, authenticators) - _authenticators[v.toMap()["BackendId"].toString()] = v; - setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this)); setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this)); - setPage(AuthenticationSelectionPage, new CoreConfigWizardPages::AuthenticationSelectionPage(_authenticators, this)); - setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this)); + setPage(AuthenticationSelectionPage, new CoreConfigWizardPages::AuthenticationSelectionPage(authInfos, this)); + setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(backendInfos, this)); syncPage = new CoreConfigWizardPages::SyncPage(this); connect(syncPage, SIGNAL(setupCore(const QString &, const QVariantMap &, const QString &, const QVariantMap &)), SLOT(prepareCoreSetup(const QString &, const QVariantMap &, const QString &, const QVariantMap &))); @@ -84,18 +156,6 @@ CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList CoreConfigWizard::backends() const -{ - return _backends; -} - - -QHash CoreConfigWizard::authenticators() const -{ - return _authenticators; -} - - void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authenticator, const QVariantMap &authProperties) { // Prevent the user from changing any settings he already specified... @@ -117,7 +177,7 @@ void CoreConfigWizard::coreSetupSuccess() { syncPage->setStatus(tr("Your core has been successfully configured. Logging you in...")); syncPage->setError(false); - syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error); + syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Success); coreConnection()->loginToCore(field("adminUser.user").toString(), field("adminUser.password").toString(), field("adminUser.rememberPasswd").toBool()); } @@ -186,10 +246,6 @@ AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent) registerField("adminUser.password*", ui.password); registerField("adminUser.password2*", ui.password2); registerField("adminUser.rememberPasswd", ui.rememberPasswd); - - //ui.user->setText("foo"); - //ui.password->setText("foo"); - //ui.password2->setText("foo"); } @@ -213,10 +269,8 @@ bool AdminUserPage::isComplete() const /*** Authentication Selection Page ***/ -AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash &backends, QWidget *parent) - : QWizardPage(parent), - _connectionBox(0), - _backends(backends) +AuthenticationSelectionPage::AuthenticationSelectionPage(const QVariantList &authInfos, QWidget *parent) + : QWizardPage(parent) { ui.setupUi(this); @@ -225,11 +279,24 @@ AuthenticationSelectionPage::AuthenticationSelectionPage(const QHashaddItem(_backends[key].toMap()["DisplayName"].toString(), key); + for (auto &&authInfo : authInfos) { + auto props = authInfo.toMap(); + // Extract field infos to avoid having to reparse the list + std::vector fields; + const auto &list = props["SetupData"].toList(); + for (int i = 0; i + 2 < list.size(); i += 3) { + fields.emplace_back(std::make_tuple(list[i].toString(), list[i+1].toString(), list[i+2])); + } + props.remove("SetupData"); + + _authProperties.emplace_back(props); + _authFields.emplace_back(std::move(fields)); + + // Create entry in authenticator selector + ui.backendList->addItem(props["DisplayName"].toString(), props["BackendId"].toString()); } - on_backendList_currentIndexChanged(); + ui.backendList->setCurrentIndex(0); } @@ -239,125 +306,95 @@ int AuthenticationSelectionPage::nextId() const } -QString AuthenticationSelectionPage::selectedBackend() const +QString AuthenticationSelectionPage::displayName() const { return ui.backendList->currentText(); } -QVariantMap AuthenticationSelectionPage::authProperties() const +QString AuthenticationSelectionPage::authenticator() const { - QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); +#if QT_VERSION >= 0x050200 + return ui.backendList->currentData().toString(); +#else + return ui.backendList->itemData(ui.backendList->currentIndex()).toString(); +#endif +} - QVariantMap properties; - QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList(); - if (!setupKeys.isEmpty()) { - QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap(); - foreach(QString key, setupKeys) { - QWidget *widget = _connectionBox->findChild(key); - QVariant def; - if (defaults.contains(key)) { - def = defaults[key]; - } - switch (def.type()) { - case QVariant::Int: - { - QSpinBox *spinbox = qobject_cast(widget); - Q_ASSERT(spinbox); - def = QVariant(spinbox->value()); - } - break; - default: - { - QLineEdit *lineEdit = qobject_cast(widget); - Q_ASSERT(lineEdit); - def = QVariant(lineEdit->text()); - } - } - properties[key] = def; - } - } - return properties; + +QVariantMap AuthenticationSelectionPage::authProperties() const +{ + return propertiesFromFieldWidgets(_fieldBox, _authFields[ui.backendList->currentIndex()]); } -void AuthenticationSelectionPage::on_backendList_currentIndexChanged() +void AuthenticationSelectionPage::on_backendList_currentIndexChanged(int index) { - QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); - ui.description->setText(_backends[backend].toMap()["Description"].toString()); + ui.description->setText(_authProperties[index]["Description"].toString()); - if (_connectionBox) { - layout()->removeWidget(_connectionBox); - _connectionBox->deleteLater(); - _connectionBox = 0; + if (_fieldBox) { + layout()->removeWidget(_fieldBox); + _fieldBox->deleteLater(); + _fieldBox = nullptr; } - - QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList(); - if (!setupKeys.isEmpty()) { - QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap(); - QGroupBox *propertyBox = new QGroupBox(this); - propertyBox->setTitle(tr("Connection Properties")); - QFormLayout *formlayout = new QFormLayout; - - foreach(QString key, setupKeys) { - QWidget *widget = 0; - QVariant def; - if (defaults.contains(key)) { - def = defaults[key]; - } - switch (def.type()) { - case QVariant::Int: - { - QSpinBox *spinbox = new QSpinBox(propertyBox); - spinbox->setMaximum(64000); - spinbox->setValue(def.toInt()); - widget = spinbox; - } - break; - default: - { - QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox); - if (key.toLower().contains("password")) { - lineEdit->setEchoMode(QLineEdit::Password); - } - widget = lineEdit; - } - } - widget->setObjectName(key); - formlayout->addRow(key + ":", widget); - } - propertyBox->setLayout(formlayout); - static_cast(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox); - _connectionBox = propertyBox; + if (!_authFields[index].empty()) { + _fieldBox = new QGroupBox(this); + _fieldBox->setTitle(tr("Authentication Settings")); + createFieldWidgets(_fieldBox, _authFields[index]); + static_cast(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, _fieldBox); } } /*** Storage Selection Page ***/ -StorageSelectionPage::StorageSelectionPage(const QHash &backends, QWidget *parent) - : QWizardPage(parent), - _connectionBox(0), - _backends(backends) +StorageSelectionPage::StorageSelectionPage(const QVariantList &backendInfos, QWidget *parent) + : QWizardPage(parent) { ui.setupUi(this); setTitle(tr("Select Storage Backend")); - setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in.")); + setSubTitle(tr("Please select a storage backend for Quassel Core.")); setCommitPage(true); registerField("storage.backend", ui.backendList); - int defaultIndex = 0; - foreach(QString key, _backends.keys()) { - ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key); - if (_backends[key].toMap()["IsDefault"].toBool()) { - defaultIndex = ui.backendList->count() - 1; + int defaultIndex {0}; // Legacy cores send backend infos in arbitrary order + + for (auto &&backendInfo : backendInfos) { + auto props = backendInfo.toMap(); + // Extract field infos to avoid having to reparse the list + std::vector fields; + + // Legacy cores (prior to 0.13) didn't send SetupData for storage backends; deal with this + if (!props.contains("SetupData")) { + const auto &defaultValues = props["SetupDefaults"].toMap(); + for (auto &&key : props["SetupKeys"].toStringList()) { + fields.emplace_back(std::make_tuple(key, key, defaultValues.value(key, QString{}))); + } + if (props.value("IsDefault", false).toBool()) { + defaultIndex = ui.backendList->count(); + } } + else { + const auto &list = props["SetupData"].toList(); + for (int i = 0; i + 2 < list.size(); i += 3) { + fields.emplace_back(std::make_tuple(list[i].toString(), list[i+1].toString(), list[i+2])); + } + props.remove("SetupData"); + } + props.remove("SetupKeys"); + props.remove("SetupDefaults"); + // Legacy cores (prior to 0.13) don't send the BackendId property + if (!props.contains("BackendId")) + props["BackendId"] = props["DisplayName"]; + _backendProperties.emplace_back(props); + _backendFields.emplace_back(std::move(fields)); + + // Create entry in backend selector + ui.backendList->addItem(props["DisplayName"].toString(), props["BackendId"].toString()); } ui.backendList->setCurrentIndex(defaultIndex); - - on_backendList_currentIndexChanged(); } @@ -367,121 +404,42 @@ int StorageSelectionPage::nextId() const } -QString StorageSelectionPage::selectedBackend() const +QString StorageSelectionPage::displayName() const { return ui.backendList->currentText(); } -QVariantMap StorageSelectionPage::connectionProperties() const +QString StorageSelectionPage::backend() const { - QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); +#if QT_VERSION >= 0x050200 + return ui.backendList->currentData().toString(); +#else + return ui.backendList->itemData(ui.backendList->currentIndex()).toString(); +#endif +} - QVariantMap properties; - QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList(); - if (!setupKeys.isEmpty()) { - QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap(); - foreach(QString key, setupKeys) { - QWidget *widget = _connectionBox->findChild(key); - QVariant def; - if (defaults.contains(key)) { - def = defaults[key]; - } - switch (def.type()) { - case QVariant::Int: - { - QSpinBox *spinbox = qobject_cast(widget); - Q_ASSERT(spinbox); - def = QVariant(spinbox->value()); - } - break; - default: - { - QLineEdit *lineEdit = qobject_cast(widget); - Q_ASSERT(lineEdit); - def = QVariant(lineEdit->text()); - } - } - properties[key] = def; - } - } - qDebug() << properties; - -// QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap(); -// if(!properties.isEmpty() && _connectionBox) { -// QVariantMap::iterator propertyIter = properties.begin(); -// while(propertyIter != properties.constEnd()) { -// QWidget *widget = _connectionBox->findChild(propertyIter.key()); -// switch(propertyIter.value().type()) { -// case QVariant::Int: -// { -// QSpinBox *spinbox = qobject_cast(widget); -// Q_ASSERT(spinbox); -// propertyIter.value() = QVariant(spinbox->value()); -// } -// break; -// default: -// { -// QLineEdit *lineEdit = qobject_cast(widget); -// Q_ASSERT(lineEdit); -// propertyIter.value() = QVariant(lineEdit->text()); -// } -// } -// propertyIter++; -// } -// } - return properties; + +QVariantMap StorageSelectionPage::backendProperties() const +{ + return propertiesFromFieldWidgets(_fieldBox, _backendFields[ui.backendList->currentIndex()]); } -void StorageSelectionPage::on_backendList_currentIndexChanged() +void StorageSelectionPage::on_backendList_currentIndexChanged(int index) { - QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); - ui.description->setText(_backends[backend].toMap()["Description"].toString()); + ui.description->setText(_backendProperties[index]["Description"].toString()); - if (_connectionBox) { - layout()->removeWidget(_connectionBox); - _connectionBox->deleteLater(); - _connectionBox = 0; + if (_fieldBox) { + layout()->removeWidget(_fieldBox); + _fieldBox->deleteLater(); + _fieldBox = nullptr; } - - QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList(); - if (!setupKeys.isEmpty()) { - QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap(); - QGroupBox *propertyBox = new QGroupBox(this); - propertyBox->setTitle(tr("Connection Properties")); - QFormLayout *formlayout = new QFormLayout; - - foreach(QString key, setupKeys) { - QWidget *widget = 0; - QVariant def; - if (defaults.contains(key)) { - def = defaults[key]; - } - switch (def.type()) { - case QVariant::Int: - { - QSpinBox *spinbox = new QSpinBox(propertyBox); - spinbox->setMaximum(64000); - spinbox->setValue(def.toInt()); - widget = spinbox; - } - break; - default: - { - QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox); - if (key.toLower().contains("password")) { - lineEdit->setEchoMode(QLineEdit::Password); - } - widget = lineEdit; - } - } - widget->setObjectName(key); - formlayout->addRow(key + ":", widget); - } - propertyBox->setLayout(formlayout); - static_cast(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox); - _connectionBox = propertyBox; + if (!_backendFields[index].empty()) { + _fieldBox = new QGroupBox(this); + _fieldBox->setTitle(tr("Storage Settings")); + createFieldWidgets(_fieldBox, _backendFields[index]); + static_cast(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, _fieldBox); } } @@ -492,45 +450,45 @@ SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent) { ui.setupUi(this); setTitle(tr("Storing Your Settings")); - setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically.")); + setSubTitle(tr("Your settings are now being stored in the core, and you will be logged in automatically.")); } void SyncPage::initializePage() { - complete = false; - hasError = false; + _complete = false; + _hasError = false; + emit completeChanged(); // Fill in sync info about the storage layer. StorageSelectionPage *storagePage = qobject_cast(wizard()->page(CoreConfigWizard::StorageSelectionPage)); - QString backend = storagePage->selectedBackend(); - QVariantMap properties = storagePage->connectionProperties(); - Q_ASSERT(!backend.isEmpty()); - ui.backend->setText(backend); + QString backend = storagePage->backend(); + QVariantMap backendProperties = storagePage->backendProperties(); + ui.backend->setText(storagePage->displayName()); // Fill in sync info about the authentication layer. AuthenticationSelectionPage *authPage = qobject_cast(wizard()->page(CoreConfigWizard::AuthenticationSelectionPage)); - QString authenticator = authPage->selectedBackend(); + QString authenticator = authPage->authenticator(); QVariantMap authProperties = authPage->authProperties(); - Q_ASSERT(!authenticator.isEmpty()); - ui.authenticator->setText(authenticator); + ui.authenticator->setText(authPage->displayName()); ui.user->setText(wizard()->field("adminUser.user").toString()); - emit setupCore(backend, properties, authenticator, authProperties); + emit setupCore(backend, backendProperties, authenticator, authProperties); } int SyncPage::nextId() const { - if (!hasError) return -1; + if (!_hasError) + return -1; return CoreConfigWizard::SyncRelayPage; } bool SyncPage::isComplete() const { - return complete; + return _complete || _hasError; } @@ -542,13 +500,15 @@ void SyncPage::setStatus(const QString &status) void SyncPage::setError(bool e) { - hasError = e; + _hasError = e; + setFinalPage(!e); + emit completeChanged(); } void SyncPage::setComplete(bool c) { - complete = c; + _complete = c; completeChanged(); } @@ -566,18 +526,6 @@ void SyncRelayPage::setMode(Mode m) mode = m; } - -/* -void SyncRelayPage::initializePage() { - return; - if(mode == Success) { - wizard()->accept(); - } else { - emit startOver(); - } -} -*/ - int SyncRelayPage::nextId() const { emit startOver(); diff --git a/src/qtui/coreconfigwizard.h b/src/qtui/coreconfigwizard.h index bcc765fe..5b09a872 100644 --- a/src/qtui/coreconfigwizard.h +++ b/src/qtui/coreconfigwizard.h @@ -18,10 +18,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef _CORECONFIGWIZARD_H_ -#define _CORECONFIGWIZARD_H_ +#pragma once + +#include +#include -#include #include #include @@ -54,9 +55,7 @@ public: ConclusionPage }; - CoreConfigWizard(CoreConnection *connection, const QList &backends, const QList &authenticators, QWidget *parent = 0); - QHash backends() const; - QHash authenticators() const; + CoreConfigWizard(CoreConnection *connection, const QVariantList &backendInfos, const QVariantList &authInfos, QWidget *parent = 0); inline CoreConnection *coreConnection() const { return _connection; } @@ -75,9 +74,6 @@ private slots: void startOver(); private: - QHash _backends; - QHash _authenticators; - CoreConfigWizardPages::SyncPage *syncPage; CoreConfigWizardPages::SyncRelayPage *syncRelayPage; @@ -114,37 +110,45 @@ private: class AuthenticationSelectionPage : public QWizardPage { Q_OBJECT + using FieldInfo = std::tuple; public: - AuthenticationSelectionPage(const QHash &backends, QWidget *parent = 0); + AuthenticationSelectionPage(const QVariantList &authInfos, QWidget *parent = 0); int nextId() const; - QString selectedBackend() const; + QString displayName() const; + QString authenticator() const; QVariantMap authProperties() const; private slots: - void on_backendList_currentIndexChanged(); + void on_backendList_currentIndexChanged(int index); + private: Ui::CoreConfigWizardAuthenticationSelectionPage ui; - QGroupBox *_connectionBox; - QHash _backends; + QGroupBox *_fieldBox {nullptr}; + std::vector _authProperties; + std::vector> _authFields; }; class StorageSelectionPage : public QWizardPage { Q_OBJECT + using FieldInfo = std::tuple; public: - StorageSelectionPage(const QHash &backends, QWidget *parent = 0); + StorageSelectionPage(const QVariantList &backendInfos, QWidget *parent = 0); int nextId() const; - QString selectedBackend() const; - QVariantMap connectionProperties() const; + QString displayName() const; + QString backend() const; + QVariantMap backendProperties() const; private slots: - void on_backendList_currentIndexChanged(); + void on_backendList_currentIndexChanged(int index); + private: Ui::CoreConfigWizardStorageSelectionPage ui; - QGroupBox *_connectionBox; - QHash _backends; + QGroupBox *_fieldBox {nullptr}; + std::vector _backendProperties; + std::vector> _backendFields; }; class SyncPage : public QWizardPage @@ -167,8 +171,8 @@ signals: private: Ui::CoreConfigWizardSyncPage ui; - bool complete; - bool hasError; + bool _complete {false}; + bool _hasError {false}; }; @@ -191,5 +195,3 @@ private: Mode mode; }; } - -#endif diff --git a/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui b/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui index 4d0ed790..d00c8f05 100644 --- a/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui +++ b/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui @@ -1,7 +1,8 @@ - + + CoreConfigWizardAuthenticationSelectionPage - - + + 0 0 @@ -9,38 +10,38 @@ 168 - + Form - + - + - - + + Authentication Backend: - - - + + + 0 0 - + QComboBox::InsertAtBottom - + Qt::Horizontal - + 40 20 @@ -51,20 +52,20 @@ - - + + Description - + - - - Foobar + + + Foobar - + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - + true @@ -73,11 +74,11 @@ - - + + Qt::Vertical - + 20 40 diff --git a/src/qtui/ui/coreconfigwizardstorageselectionpage.ui b/src/qtui/ui/coreconfigwizardstorageselectionpage.ui index 75f45e9d..6e718376 100644 --- a/src/qtui/ui/coreconfigwizardstorageselectionpage.ui +++ b/src/qtui/ui/coreconfigwizardstorageselectionpage.ui @@ -1,7 +1,8 @@ - + + CoreConfigWizardStorageSelectionPage - - + + 0 0 @@ -9,38 +10,38 @@ 168 - + Form - + - + - - + + Storage Backend: - - - + + + 0 0 - + QComboBox::InsertAtBottom - + Qt::Horizontal - + 40 20 @@ -51,20 +52,20 @@ - - + + Description - + - - - Foobar + + + Foobar - + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - + true @@ -73,11 +74,11 @@ - - + + Qt::Vertical - + 20 40 diff --git a/src/qtui/ui/coreconfigwizardsyncpage.ui b/src/qtui/ui/coreconfigwizardsyncpage.ui index 6345ef74..9894e97b 100644 --- a/src/qtui/ui/coreconfigwizardsyncpage.ui +++ b/src/qtui/ui/coreconfigwizardsyncpage.ui @@ -1,7 +1,8 @@ - + + CoreConfigWizardSyncPage - - + + 0 0 @@ -9,77 +10,77 @@ 300 - + Form - + - - + + Your Choices - + - + - - - - + + + + 75 true - + Admin User: - - - - foo + + + + foo - - - + + + 75 true - + Storage Backend: - - - - bar + + + + bar - - - + + + 75 true - + Authentication Backend: - - - - bar + + + + bar @@ -87,10 +88,10 @@ - + Qt::Horizontal - + 40 20 @@ -105,10 +106,10 @@ - + Qt::Vertical - + 20 40 @@ -117,14 +118,14 @@ - - + + Please wait while your settings are being transmitted to the core... - + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - + true