Przeglądaj źródła

Config: Add version flags to accounts and folders

Also, if there is too-new configuration, backup the file, show a
warning message asking the user whether it's ok to discard the
configuration from the future.

See #6504
Christian Kamm 7 lat temu
rodzic
commit
87ba4e6b9c

+ 27 - 1
src/gui/accountmanager.cpp

@@ -36,6 +36,10 @@ static const char caCertsKeyC[] = "CaCertificates";
 static const char accountsC[] = "Accounts";
 static const char versionC[] = "version";
 static const char serverVersionC[] = "serverVersion";
+
+// The maximum versions that this client can read
+static const int maxAccountsVersion = 2;
+static const int maxAccountVersion = 1;
 }
 
 
@@ -79,6 +83,27 @@ bool AccountManager::restore()
     return true;
 }
 
+QStringList AccountManager::backwardMigrationKeys()
+{
+    auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
+    QStringList badKeys;
+
+    const int accountsVersion = settings->value(QLatin1String(versionC)).toInt();
+    if (accountsVersion <= maxAccountsVersion) {
+        foreach (const auto &accountId, settings->childGroups()) {
+            settings->beginGroup(accountId);
+            const int accountVersion = settings->value(QLatin1String(versionC), 1).toInt();
+            if (accountVersion > maxAccountVersion) {
+                badKeys.append(settings->group());
+            }
+            settings->endGroup();
+        }
+    } else {
+        badKeys.append(settings->group());
+    }
+    return badKeys;
+}
+
 bool AccountManager::restoreFromLegacySettings()
 {
     qCInfo(lcAccountManager) << "Migrate: restoreFromLegacySettings, checking settings group"
@@ -139,7 +164,7 @@ bool AccountManager::restoreFromLegacySettings()
 void AccountManager::save(bool saveCredentials)
 {
     auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
-    settings->setValue(QLatin1String(versionC), 2);
+    settings->setValue(QLatin1String(versionC), maxAccountsVersion);
     for (const auto &acc : qAsConst(_accounts)) {
         settings->beginGroup(acc->account()->id());
         saveAccountHelper(acc->account().data(), *settings, saveCredentials);
@@ -177,6 +202,7 @@ void AccountManager::saveAccountState(AccountState *a)
 
 void AccountManager::saveAccountHelper(Account *acc, QSettings &settings, bool saveCredentials)
 {
+    settings.setValue(QLatin1String(versionC), maxAccountVersion);
     settings.setValue(QLatin1String(urlC), acc->_url.toString());
     settings.setValue(QLatin1String(serverVersionC), acc->_serverVersion);
     if (acc->_credentials) {

+ 6 - 0
src/gui/accountmanager.h

@@ -76,6 +76,12 @@ public:
      */
     static AccountPtr createAccount();
 
+    /**
+     * Returns the list of settings keys that can't be read because
+     * they are from the future.
+     */
+    static QStringList backwardMigrationKeys();
+
 private:
     // saving and loading Account to settings
     void saveAccountHelper(Account *account, QSettings &settings, bool saveCredentials = true);

+ 49 - 1
src/gui/application.cpp

@@ -102,6 +102,50 @@ namespace {
 
 // ----------------------------------------------------------------------------------
 
+bool Application::configBackwardMigration()
+{
+    auto accountKeys = AccountManager::backwardMigrationKeys();
+    auto folderKeys = FolderMan::backwardMigrationKeys();
+
+    bool containsFutureData = !accountKeys.isEmpty() || !folderKeys.isEmpty();
+
+    // Deal with unreadable accounts
+    if (!containsFutureData)
+        return true;
+
+    const auto backupFile = ConfigFile().backup();
+
+    QMessageBox box(
+        QMessageBox::Warning,
+        APPLICATION_SHORTNAME,
+        tr("Some settings were configured in newer versions of this client and "
+           "use features that are not available in this version.<br>"
+           "<br>"
+           "<b>Continuing will mean losing these settings.</b><br>"
+           "<br>"
+           "The current configuration file was already backed up to <i>%1</i>.")
+            .arg(backupFile));
+    box.addButton(tr("Quit"), QMessageBox::AcceptRole);
+    auto continueBtn = box.addButton(tr("Continue"), QMessageBox::DestructiveRole);
+
+    box.exec();
+    if (box.clickedButton() != continueBtn) {
+        QTimer::singleShot(0, qApp, SLOT(quit()));
+        return false;
+    }
+
+    auto settings = ConfigFile::settingsWithGroup("foo");
+    settings->endGroup();
+
+    // Wipe the keys from the future
+    for (const auto &badKey : accountKeys)
+        settings->remove(badKey);
+    for (const auto &badKey : folderKeys)
+        settings->remove(badKey);
+
+    return true;
+}
+
 Application::Application(int &argc, char **argv)
     : SharedTools::QtSingleApplication(Theme::instance()->appName(), argc, argv)
     , _gui(nullptr)
@@ -187,8 +231,12 @@ Application::Application(int &argc, char **argv)
     setupLogging();
     setupTranslations();
 
-    // The timeout is initialized with an environment variable, if not, override with the value from the config
+    if (!configBackwardMigration()) {
+        return;
+    }
+
     ConfigFile cfg;
+    // The timeout is initialized with an environment variable, if not, override with the value from the config
     if (!AbstractNetworkJob::httpTimeout)
         AbstractNetworkJob::httpTimeout = cfg.timeout();
 

+ 6 - 0
src/gui/application.h

@@ -103,6 +103,12 @@ protected slots:
 private:
     void setHelp();
 
+    /**
+     * Maybe a newer version of the client was used with this config file:
+     * if so, backup, confirm with user and remove the config that can't be read.
+     */
+    bool configBackwardMigration();
+
     QPointer<ownCloudGui> _gui;
 
     Theme *_theme;

+ 5 - 0
src/gui/folder.cpp

@@ -42,6 +42,8 @@
 #include <QMessageBox>
 #include <QPushButton>
 
+static const char versionC[] = "version";
+
 namespace OCC {
 
 Q_LOGGING_CATEGORY(lcFolder, "nextcloud.gui.folder", QtInfoMsg)
@@ -571,6 +573,8 @@ void Folder::saveToSettings() const
     }
 
     settings->beginGroup(settingsGroup);
+    // Note: Each of these groups might have a "version" tag, but that's
+    //       currently unused.
     FolderDefinition::save(*settings, _definition);
 
     settings->sync();
@@ -1127,6 +1131,7 @@ void FolderDefinition::save(QSettings &settings, const FolderDefinition &folder)
     settings.setValue(QLatin1String("paused"), folder.paused);
     settings.setValue(QLatin1String("ignoreHiddenFiles"), folder.ignoreHiddenFiles);
     settings.setValue(QLatin1String("usePlaceholders"), folder.useVirtualFiles);
+    settings.setValue(QLatin1String(versionC), maxSettingsVersion());
 
     // Happens only on Windows when the explorer integration is enabled.
     if (!folder.navigationPaneClsid.isNull())

+ 3 - 0
src/gui/folder.h

@@ -72,6 +72,9 @@ public:
     static bool load(QSettings &settings, const QString &alias,
         FolderDefinition *folder);
 
+    /// The highest version in the settings that load() can read
+    static int maxSettingsVersion() { return 1; }
+
     /// Ensure / as separator and trailing /.
     static QString prepareLocalPath(const QString &path);
 

+ 36 - 0
src/gui/folderman.cpp

@@ -39,6 +39,9 @@
 #include <QSet>
 #include <QNetworkProxy>
 
+static const char versionC[] = "version";
+static const int maxFoldersVersion = 1;
+
 namespace OCC {
 
 Q_LOGGING_CATEGORY(lcFolderMan, "nextcloud.gui.folder.manager", QtInfoMsg)
@@ -303,6 +306,39 @@ int FolderMan::setupFoldersMigration()
     return _folderMap.size();
 }
 
+QStringList FolderMan::backwardMigrationKeys()
+{
+    QStringList badKeys;
+    auto settings = ConfigFile::settingsWithGroup(QLatin1String("Accounts"));
+
+    auto processSubgroup = [&](const QString &name) {
+        settings->beginGroup(name);
+        const int foldersVersion = settings->value(QLatin1String(versionC), 1).toInt();
+        if (foldersVersion <= maxFoldersVersion) {
+            foreach (const auto &folderAlias, settings->childGroups()) {
+                settings->beginGroup(folderAlias);
+                const int folderVersion = settings->value(QLatin1String(versionC), 1).toInt();
+                if (folderVersion > FolderDefinition::maxSettingsVersion()) {
+                    badKeys.append(settings->group());
+                }
+                settings->endGroup();
+            }
+        } else {
+            badKeys.append(settings->group());
+        }
+        settings->endGroup();
+    };
+
+    for (const auto &accountId : settings->childGroups()) {
+        settings->beginGroup(accountId);
+        processSubgroup("Folders");
+        processSubgroup("Multifolders");
+        processSubgroup("FoldersWithPlaceholders");
+        settings->endGroup();
+    }
+    return badKeys;
+}
+
 bool FolderMan::ensureJournalGone(const QString &journalDbFile)
 {
     // remove the old journal file

+ 6 - 0
src/gui/folderman.h

@@ -68,6 +68,12 @@ public:
     int setupFolders();
     int setupFoldersMigration();
 
+    /**
+     * Returns a list of keys that can't be read because they are from
+     * future versions.
+     */
+    static QStringList backwardMigrationKeys();
+
     OCC::Folder::Map map();
 
     /** Adds a folder for an account, ensures the journal is gone and saves it in the settings.

+ 15 - 0
src/libsync/configfile.cpp

@@ -411,6 +411,21 @@ QString ConfigFile::excludeFileFromSystem()
     return fi.absoluteFilePath();
 }
 
+QString ConfigFile::backup() const
+{
+    QString baseFile = configFile();
+    QString backupFile = QString("%1.backup_%2").arg(baseFile, QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"));
+
+    // If this exact file already exists it's most likely that a backup was
+    // already done. (two backup calls directly after each other, potentially
+    // even with source alterations in between!)
+    if (!QFile::exists(backupFile)) {
+        QFile f(baseFile);
+        f.copy(backupFile);
+    }
+    return backupFile;
+}
+
 QString ConfigFile::configFile() const
 {
     return configPath() + Theme::instance()->configFileName();

+ 7 - 0
src/libsync/configfile.h

@@ -48,6 +48,13 @@ public:
     QString excludeFile(Scope scope) const;
     static QString excludeFileFromSystem(); // doesn't access config dir
 
+    /**
+     * Creates a backup of the file
+     *
+     * Returns the path of the new backup.
+     */
+    QString backup() const;
+
     bool exists();
 
     QString defaultConnection() const;