+void Core::restoreState()
+{
+ if (!_configured) {
+ qWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!"));
+ return;
+ }
+ if (_sessions.count()) {
+ qWarning() << 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..."));
+ return;
+ }
+ */
+
+ const QList<QVariant>& activeSessionsFallback = s.coreState().toMap()["ActiveSessions"].toList();
+ QVariantList activeSessions = instance()->_storage->getCoreState(activeSessionsFallback);
+
+ if (activeSessions.count() > 0) {
+ qInfo() << "Restoring previous core state...";
+ for (auto&& v : activeSessions) {
+ UserId user = v.value<UserId>();
+ sessionForUser(user, true);
+ }
+ }
+}
+
+/*** Core Setup ***/
+
+QString Core::setup(const QString& adminUser,
+ const QString& adminPassword,
+ const QString& backend,
+ const QVariantMap& setupData,
+ const QString& authenticator,
+ const QVariantMap& authSetupData)
+{
+ return instance()->setupCore(adminUser, adminPassword, backend, setupData, authenticator, authSetupData);
+}
+
+QString Core::setupCore(const QString& adminUser,
+ const QString& adminPassword,
+ const QString& backend,
+ const QVariantMap& setupData,
+ const QString& authenticator,
+ const QVariantMap& authSetupData)
+{
+ if (_configured)
+ return tr("Core is already configured! Not configuring again...");
+
+ if (adminUser.isEmpty() || adminPassword.isEmpty()) {
+ return tr("Admin user or password not set.");
+ }
+ try {
+ if (!(_configured = initStorage(backend, setupData, {}, false, true))) {
+ return tr("Could not setup storage!");
+ }
+
+ qInfo() << "Selected authenticator:" << authenticator;
+ if (!(_configured = initAuthenticator(authenticator, authSetupData, {}, false, true))) {
+ return tr("Could not setup authenticator!");
+ }
+ }
+ catch (ExitException e) {
+ // Event loop is running, so trigger an exit rather than throwing an exception
+ QCoreApplication::exit(e.exitCode);
+ return e.errorString.isEmpty() ? tr("Fatal failure while trying to setup, terminating") : e.errorString;
+ }
+
+ if (!saveBackendSettings(backend, setupData)) {
+ return tr("Could not save backend settings, probably a permission problem.");
+ }
+ saveAuthenticatorSettings(authenticator, authSetupData);
+
+ qInfo() << qPrintable(tr("Creating admin user..."));
+ _storage->addUser(adminUser, adminPassword);
+ cacheSysIdent();
+ startListening(); // TODO check when we need this
+ return QString();
+}
+
+QString Core::setupCoreForInternalUsage()
+{
+ Q_ASSERT(!_registeredStorageBackends.empty());
+
+ qsrand(QDateTime::currentDateTime().toMSecsSinceEpoch());
+ int pass = 0;
+ for (int i = 0; i < 10; i++) {
+ pass *= 10;
+ pass += qrand() % 10;
+ }
+
+ // mono client currently needs sqlite
+ return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap(), "Database", QVariantMap());
+}
+
+/*** Storage Handling ***/
+
+template<typename Storage>
+void Core::registerStorageBackend()
+{
+ auto backend = makeDeferredShared<Storage>(this);
+ if (backend->isAvailable())
+ _registeredStorageBackends.emplace_back(std::move(backend));
+ else
+ backend->deleteLater();
+}
+
+void Core::registerStorageBackends()
+{
+ if (_registeredStorageBackends.empty()) {
+ registerStorageBackend<SqliteStorage>();
+ registerStorageBackend<PostgreSqlStorage>();
+ }
+}
+
+DeferredSharedPtr<Storage> Core::storageBackend(const QString& backendId) const
+{
+ auto it = std::find_if(_registeredStorageBackends.begin(),
+ _registeredStorageBackends.end(),
+ [backendId](const DeferredSharedPtr<Storage>& backend) { return backend->displayName() == backendId; });
+ return it != _registeredStorageBackends.end() ? *it : nullptr;
+}
+
+bool Core::initStorage(
+ const QString& backend, const QVariantMap& settings, const QProcessEnvironment& environment, bool loadFromEnvironment, bool setup)
+{
+ if (backend.isEmpty()) {
+ qWarning() << "No storage backend selected!";
+ return false;
+ }
+
+ auto storage = storageBackend(backend);
+ if (!storage) {
+ qCritical() << "Selected storage backend is not available:" << backend;
+ return false;
+ }
+
+ connect(storage.get(), &Storage::dbUpgradeInProgress, this, &Core::dbUpgradeInProgress);
+
+ Storage::State storageState = storage->init(settings, environment, loadFromEnvironment);
+ switch (storageState) {
+ case Storage::NeedsSetup:
+ if (!setup)
+ return false; // trigger setup process
+ if (storage->setup(settings, environment, loadFromEnvironment))
+ return initStorage(backend, settings, environment, loadFromEnvironment, false);
+ return false;
+
+ case Storage::NotAvailable:
+ if (!setup) {
+ // If initialization wasn't successful, we quit to keep from coming up unconfigured
+ throw ExitException{EXIT_FAILURE, tr("Selected storage backend %1 is not available.").arg(backend)};
+ }
+ qCritical() << "Selected storage backend is not available:" << backend;
+ return false;
+
+ case Storage::IsReady:
+ // delete all other backends
+ _registeredStorageBackends.clear();
+ connect(storage.get(), &Storage::bufferInfoUpdated, this, &Core::bufferInfoUpdated);
+ break;
+ }
+ _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;