+ qWarning() << "Core::socketError()" << socket << err << socket->errorString();
+}
+
+// migration / backend selection
+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(", "));
+ return false;
+ }
+
+ Storage *storage = _storageBackends[backend];
+ QVariantMap settings = promptForSettings(storage);
+
+ Storage::State storageState = storage->init(settings);
+ switch(storageState) {
+ case Storage::IsReady:
+ saveBackendSettings(backend, settings);
+ qWarning() << "Switched backend to:" << qPrintable(backend);
+ qWarning() << "Backend already initialized. Skipping Migration";
+ return true;
+ case Storage::NotAvailable:
+ qCritical() << "Backend is not available:" << qPrintable(backend);
+ return false;
+ case Storage::NeedsSetup:
+ if(!storage->setup(settings)) {
+ qWarning() << qPrintable(QString("Core::selectBackend(): unable to setup backend: %1").arg(backend));
+ return false;
+ }
+
+ if(storage->init(settings) != Storage::IsReady) {
+ qWarning() << qPrintable(QString("Core::migrateBackend(): unable to initialize backend: %1").arg(backend));
+ return false;
+ }
+
+ saveBackendSettings(backend, settings);
+ qWarning() << "Switched backend to:" << qPrintable(backend);
+ break;
+ }
+
+ // let's see if we have a current storage object we can migrate from
+ AbstractSqlMigrationReader *reader = getMigrationReader(_storage);
+ AbstractSqlMigrationWriter *writer = getMigrationWriter(storage);
+ 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;
+ if(reader->migrateTo(writer)) {
+ qDebug() << "Migration finished!";
+ saveBackendSettings(backend, settings);
+ return true;
+ }
+ 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.";
+ } else if(!reader) {
+ qWarning() << "Currently active backend does not support migration:" << qPrintable(_storage->displayName());
+ }
+ if(writer) {
+ qWarning() << "New backend does not support migration:" << qPrintable(backend);
+ }
+
+ // so we were unable to merge, but let's create a user \o/
+ _storage = storage;
+ createUser();
+ return true;
+}
+
+void Core::createUser() {
+ QTextStream out(stdout);
+ QTextStream in(stdin);
+ out << "Add a new user:" << endl;
+ out << "Username: ";
+ out.flush();
+ QString username = in.readLine().trimmed();
+
+ disableStdInEcho();
+ out << "Password: ";
+ out.flush();
+ QString password = in.readLine().trimmed();
+ out << endl;
+ out << "Repeat Password: ";
+ out.flush();
+ QString password2 = in.readLine().trimmed();
+ out << endl;
+ enableStdInEcho();
+
+ if(password != password2) {
+ qWarning() << "Passwords don't match!";
+ return;
+ }
+ if(password.isEmpty()) {
+ qWarning() << "Password is empty!";
+ return;
+ }
+
+ if(_storage->addUser(username, password).isValid()) {
+ out << "Added user " << username << " successfully!" << endl;
+ } else {
+ qWarning() << "Unable to add user:" << qPrintable(username);
+ }
+}
+
+void Core::changeUserPass(const QString &username) {
+ QTextStream out(stdout);
+ QTextStream in(stdin);
+ UserId userId = _storage->getUserId(username);
+ if(!userId.isValid()) {
+ out << "User " << username << " does not exist." << endl;
+ return;
+ }
+
+ out << "Change password for user: " << username << endl;
+
+ disableStdInEcho();
+ out << "New Password: ";
+ out.flush();
+ QString password = in.readLine().trimmed();
+ out << endl;
+ out << "Repeat Password: ";
+ out.flush();
+ QString password2 = in.readLine().trimmed();
+ out << endl;
+ enableStdInEcho();
+
+ if(password != password2) {
+ qWarning() << "Passwords don't match!";
+ return;
+ }
+ if(password.isEmpty()) {
+ qWarning() << "Password is empty!";
+ return;
+ }
+
+ if(_storage->updateUser(userId, password)) {
+ out << "Password changed successfuly!" << endl;
+ } else {
+ qWarning() << "Failed to change password!";
+ }
+}
+
+AbstractSqlMigrationReader *Core::getMigrationReader(Storage *storage) {
+ if(!storage)
+ return 0;
+
+ AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
+ if(!sqlStorage) {
+ qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
+ return 0;
+ }
+
+ return sqlStorage->createMigrationReader();
+}
+
+AbstractSqlMigrationWriter *Core::getMigrationWriter(Storage *storage) {
+ if(!storage)
+ return 0;
+
+ AbstractSqlStorage *sqlStorage = qobject_cast<AbstractSqlStorage *>(storage);
+ if(!sqlStorage) {
+ qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!";
+ return 0;
+ }
+
+ return sqlStorage->createMigrationWriter();
+}
+
+void Core::saveBackendSettings(const QString &backend, const QVariantMap &settings) {
+ QVariantMap dbsettings;
+ dbsettings["Backend"] = backend;
+ dbsettings["ConnectionProperties"] = settings;
+ CoreSettings().setStorageSettings(dbsettings);
+}
+
+QVariantMap Core::promptForSettings(const Storage *storage) {
+ QVariantMap settings;
+
+ QStringList keys = storage->setupKeys();
+ if(keys.isEmpty())
+ return settings;
+
+ QTextStream out(stdout);
+ QTextStream in(stdin);
+ out << "Default values are in brackets" << endl;
+
+ QVariantMap defaults = storage->setupDefaults();
+ 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();
+
+ bool noEcho = QString("password").toLower().startsWith(key.toLower());
+ if(noEcho) {
+ disableStdInEcho();
+ }
+ value = in.readLine().trimmed();
+ if(noEcho) {
+ out << endl;
+ enableStdInEcho();
+ }
+
+ if(!value.isEmpty()) {
+ switch(defaults[key].type()) {
+ case QVariant::Int:
+ val = QVariant(value.toInt());
+ break;
+ default:
+ val = QVariant(value);
+ }
+ }
+ settings[key] = val;
+ }
+ return settings;
+}
+
+
+#ifdef Q_OS_WIN32
+void Core::stdInEcho(bool on) {
+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD mode = 0;
+ GetConsoleMode(hStdin, &mode);
+ if(on)
+ mode |= ENABLE_ECHO_INPUT;
+ else
+ mode &= ~ENABLE_ECHO_INPUT;
+ SetConsoleMode(hStdin, mode);
+}
+#else
+void Core::stdInEcho(bool on) {
+ termios t;
+ tcgetattr(STDIN_FILENO, &t);
+ if(on)
+ t.c_lflag |= ECHO;
+ else
+ t.c_lflag &= ~ECHO;
+ tcsetattr(STDIN_FILENO, TCSANOW, &t);