fix migration from old settings configuration files
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Mon, 7 Nov 2022 17:33:39 +0000 (18:33 +0100)
committerClaudio Cambra <claudio.cambra@gmail.com>
Thu, 15 Dec 2022 15:58:00 +0000 (16:58 +0100)
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/gui/accountmanager.cpp
src/gui/folderman.cpp

index 9d8c93914f4798477f20052a734e78fd0cff7353..58b786088fff2ff37213e4e4272dce38f4f1179a 100644 (file)
@@ -49,7 +49,7 @@ constexpr auto httpAuthPrefix = "http_";
 constexpr auto webflowAuthPrefix = "webflow_";
 
 constexpr auto legacyRelativeConfigLocationC = "/ownCloud/owncloud.cfg";
-constexpr auto legacyOcSettingsC = "ownCloud";
+constexpr auto legacyCfgFileNameC = "owncloud.cfg";
 
 // The maximum versions that this client can read
 constexpr auto maxAccountsVersion = 2;
@@ -142,43 +142,55 @@ bool AccountManager::restoreFromLegacySettings()
     qCInfo(lcAccountManager) << "Migrate: restoreFromLegacySettings, checking settings group"
                              << Theme::instance()->appName();
 
-    // try to open the correctly themed settings
+         // try to open the correctly themed settings
     auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());
 
-    // if the settings file could not be opened, the childKeys list is empty
-    // then try to load settings from a very old place
+         // if the settings file could not be opened, the childKeys list is empty
+         // then try to load settings from a very old place
     if (settings->childKeys().isEmpty()) {
         // Now try to open the original ownCloud settings to see if they exist.
-        auto oCCfgFile = QDir::fromNativeSeparators(settings->fileName());
+        const auto fullLegacyCfgFile = QDir::fromNativeSeparators(settings->fileName());
         // replace the last two segments with ownCloud/owncloud.cfg
-        oCCfgFile = oCCfgFile.left(oCCfgFile.lastIndexOf('/'));
-        oCCfgFile = oCCfgFile.left(oCCfgFile.lastIndexOf('/'));
-        oCCfgFile += QLatin1String(legacyRelativeConfigLocationC);
-
-        qCInfo(lcAccountManager) << "Migrate: checking old config " << oCCfgFile;
-
-        QFileInfo fi(oCCfgFile);
-        if (fi.isReadable()) {
-            std::unique_ptr<QSettings> oCSettings(new QSettings(oCCfgFile, QSettings::IniFormat));
-            oCSettings->beginGroup(QLatin1String(legacyOcSettingsC));
-
-            // Check the theme url to see if it is the same url that the oC config was for
-            auto overrideUrl = Theme::instance()->overrideServerUrl();
-            if (!overrideUrl.isEmpty()) {
-                if (overrideUrl.endsWith('/')) {
-                    overrideUrl.chop(1);
-                }
-                auto oCUrl = oCSettings->value(QLatin1String(urlC)).toString();
-                if (oCUrl.endsWith('/')) {
-                    oCUrl.chop(1);
-                }
-
-                // in case the urls are equal reset the settings object to read from
-                // the ownCloud settings object
-                qCInfo(lcAccountManager) << "Migrate oC config if " << oCUrl << " == " << overrideUrl << ":"
-                                         << (oCUrl == overrideUrl ? "Yes" : "No");
-                if (oCUrl == overrideUrl) {
-                    settings = std::move(oCSettings);
+        const auto legacyCfgFileParentFolder = fullLegacyCfgFile.left(fullLegacyCfgFile.lastIndexOf('/'));
+        const auto legacyCfgFileGrandParentFolder = legacyCfgFileParentFolder.left(legacyCfgFileParentFolder.lastIndexOf('/'));
+
+        for (const auto &configFile : {QString{legacyCfgFileParentFolder + "/" + legacyCfgFileNameC}, QString{legacyCfgFileGrandParentFolder + QLatin1String(legacyRelativeConfigLocationC)}}) {
+            if (QFileInfo::exists(configFile)) {
+                qCInfo(lcAccountManager) << "Migrate: checking old config " << configFile;
+
+                QFileInfo fi(configFile);
+                if (fi.isReadable()) {
+                    std::unique_ptr<QSettings> oCSettings(new QSettings(configFile, QSettings::IniFormat));
+                    if (oCSettings->status() != QSettings::Status::NoError) {
+                        qCInfo(lcAccountManager) << "Error reading legacy configuration file" << oCSettings->status();
+                    }
+
+                    // Check the theme url to see if it is the same url that the oC config was for
+                    auto overrideUrl = Theme::instance()->overrideServerUrl();
+                    qCInfo(lcAccountManager) << "Migrate: overrideUrl" << overrideUrl;
+                    if (!overrideUrl.isEmpty()) {
+                        if (overrideUrl.endsWith('/')) {
+                            overrideUrl.chop(1);
+                        }
+                        auto oCUrl = oCSettings->value(QLatin1String(urlC)).toString();
+                        if (oCUrl.endsWith('/')) {
+                            oCUrl.chop(1);
+                        }
+
+                        // in case the urls are equal reset the settings object to read from
+                        // the ownCloud settings object
+                        qCInfo(lcAccountManager) << "Migrate oC config if " << oCUrl << " == " << overrideUrl << ":"
+                                                 << (oCUrl == overrideUrl ? "Yes" : "No");
+                        if (oCUrl == overrideUrl) {
+                            qCInfo(lcAccountManager) << "Copy settings" << oCSettings->allKeys().join(", ");
+                            settings = std::move(oCSettings);
+                        }
+                    } else {
+                        qCInfo(lcAccountManager) << "Copy settings" << oCSettings->allKeys().join(", ");
+                        settings = std::move(oCSettings);
+                    }
+
+                    break;
                 }
             }
         }
@@ -186,9 +198,15 @@ bool AccountManager::restoreFromLegacySettings()
 
     // Try to load the single account.
     if (!settings->childKeys().isEmpty()) {
-        if (const auto acc = loadAccountHelper(*settings)) {
-            addAccount(acc);
-            return true;
+        settings->beginGroup(accountsC);
+        const auto childGroups = settings->childGroups();
+        for (const auto &accountId : childGroups) {
+            settings->beginGroup(accountId);
+            if (const auto acc = loadAccountHelper(*settings)) {
+                addAccount(acc);
+
+                return true;
+            }
         }
     }
     return false;
index a5a13a51dc3de4cea2be8e384c7e78dda73ac39e..42cdcdfa254b1ea9400eae3ca386c5fee4553f07 100644 (file)
 #include <QNetworkProxy>
 
 namespace {
-#ifndef VERSION_C
-#define VERSION_C
-constexpr auto versionC= "version";
-#endif
+constexpr auto accountsC = "Accounts";
+constexpr auto foldersC = "Folders";
+constexpr auto versionC = "version";
+constexpr auto maxFoldersVersion = 1;
 }
-static const int maxFoldersVersion = 1;
 
 namespace OCC {
 
@@ -347,7 +346,7 @@ int FolderMan::setupFoldersMigration()
 {
     ConfigFile cfg;
     QDir storageDir(cfg.configPath());
-    _folderConfigPath = cfg.configPath() + QLatin1String("folders");
+    _folderConfigPath = cfg.configPath();
 
     qCInfo(lcFolderMan) << "Setup folders from " << _folderConfigPath << "(migration)";
 
@@ -509,57 +508,90 @@ Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
     // Check if the filename is equal to the group setting. If not, use the group
     // name as an alias.
     QStringList groups = settings.childGroups();
+    if (groups.isEmpty()) {
+        qCWarning(lcFolderMan) << "empty file:" << cfgFile.filePath();
+        return folder;
+    }
 
-    if (!groups.contains(escapedAlias) && groups.count() > 0) {
-        escapedAlias = groups.first();
+    if (!accountState) {
+        qCCritical(lcFolderMan) << "can't create folder without an account";
+        return nullptr;
     }
 
-    settings.beginGroup(escapedAlias); // read the group with the same name as the file which is the folder alias
+    settings.beginGroup(accountsC);
+    const auto rootChildGroups = settings.childGroups();
+    for (const auto &accountId : rootChildGroups) {
+        qCDebug(lcFolderMan) << "try to migrate accountId:" << accountId;
+        settings.beginGroup(accountId);
+        settings.beginGroup(foldersC);
 
-    QString path = settings.value(QLatin1String("localPath")).toString();
-    QString backend = settings.value(QLatin1String("backend")).toString();
-    QString targetPath = settings.value(QLatin1String("targetPath")).toString();
-    bool paused = settings.value(QLatin1String("paused"), false).toBool();
-    // QString connection = settings.value( QLatin1String("connection") ).toString();
-    QString alias = unescapeAlias(escapedAlias);
+        if (settings.childGroups().isEmpty()) {
+            continue;
+        }
 
-    if (backend.isEmpty() || backend != QLatin1String("owncloud")) {
-        qCWarning(lcFolderMan) << "obsolete configuration of type" << backend;
-        return nullptr;
-    }
+        const auto childGroups = settings.childGroups();
+        for (const auto &alias : childGroups) {
+            settings.beginGroup(alias);
+            qCDebug(lcFolderMan) << "try to migrate folder alias:" << alias;
 
-    // cut off the leading slash, oCUrl always has a trailing.
-    if (targetPath.startsWith(QLatin1Char('/'))) {
-        targetPath.remove(0, 1);
-    }
+            const auto path = settings.value(QLatin1String("localPath")).toString();
+            const auto targetPath = settings.value(QLatin1String("targetPath")).toString();
+            const auto journalPath = settings.value(QLatin1String("journalPath")).toString();
+            const auto paused = settings.value(QLatin1String("paused"), false).toBool();
+            const auto ignoreHiddenFiles = settings.value(QLatin1String("ignoreHiddenFiles"), false).toBool();
 
-    if (!accountState) {
-        qCCritical(lcFolderMan) << "can't create folder without an account";
-        return nullptr;
-    }
+            if (path.isEmpty()) {
+                qCDebug(lcFolderMan) << "localPath is empty";
+                settings.endGroup();
+                continue;
+            }
 
-    FolderDefinition folderDefinition;
-    folderDefinition.alias = alias;
-    folderDefinition.localPath = path;
-    folderDefinition.targetPath = targetPath;
-    folderDefinition.paused = paused;
-    folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles();
+            if (targetPath.isEmpty()) {
+                qCDebug(lcFolderMan) << "targetPath is empty";
+                settings.endGroup();
+                continue;
+            }
 
-    folder = addFolderInternal(folderDefinition, accountState, std::make_unique<VfsOff>());
-    if (folder) {
-        QStringList blackList = settings.value(QLatin1String("blackList")).toStringList();
-        if (!blackList.empty()) {
-            //migrate settings
-            folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
-            settings.remove(QLatin1String("blackList"));
-            // FIXME: If you remove this codepath, you need to provide another way to do
-            // this via theme.h or the normal FolderMan::setupFolders
+            if (journalPath.isEmpty()) {
+                qCDebug(lcFolderMan) << "journalPath is empty";
+                settings.endGroup();
+                continue;
+            }
+
+            FolderDefinition folderDefinition;
+            folderDefinition.alias = alias;
+            folderDefinition.localPath = path;
+            folderDefinition.targetPath = targetPath;
+            folderDefinition.journalPath = journalPath;
+            folderDefinition.paused = paused;
+            folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles;
+
+            folder = addFolderInternal(folderDefinition, accountState, std::make_unique<VfsOff>());
+            if (folder) {
+                const auto blackList = settings.value(QLatin1String("blackList")).toStringList();
+                if (!blackList.empty()) {
+                    //migrate settings
+                    folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
+                    settings.remove(QLatin1String("blackList"));
+                    // FIXME: If you remove this codepath, you need to provide another way to do
+                    // this via theme.h or the normal FolderMan::setupFolders
+                }
+
+                folder->saveToSettings();
+            }
+            qCInfo(lcFolderMan) << "Migrated!" << folder;
+            settings.sync();
+
+            if (folder) {
+                return folder;
+            }
+
+            settings.endGroup();
         }
 
-        folder->saveToSettings();
+        settings.endGroup();
+        settings.endGroup();
     }
-    qCInfo(lcFolderMan) << "Migrated!" << folder;
-    settings.sync();
     return folder;
 }