Merge pull request #5141 from nextcloud/bugfix/upgradeFromOldClients

fix migration from old settings configuration files
This commit is contained in:
Claudio Cambra 2022-12-15 17:40:30 +01:00 committed by GitHub
commit c03e6cca61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 154 additions and 86 deletions

View file

@ -13,19 +13,21 @@
*/ */
#include "accountmanager.h" #include "accountmanager.h"
#include "configfile.h"
#include "sslerrordialog.h" #include "sslerrordialog.h"
#include "proxyauthhandler.h" #include "proxyauthhandler.h"
#include "common/asserts.h" #include "common/asserts.h"
#include <theme.h> #include "creds/credentialsfactory.h"
#include <creds/credentialsfactory.h> #include "creds/abstractcredentials.h"
#include <creds/abstractcredentials.h> #include "libsync/clientsideencryption.h"
#include <cookiejar.h> #include "libsync/configfile.h"
#include "libsync/cookiejar.h"
#include "libsync/theme.h"
#include <QSettings> #include <QSettings>
#include <QDir> #include <QDir>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QMessageBox> #include <QMessageBox>
#include "clientsideencryption.h"
namespace { namespace {
constexpr auto urlC = "url"; constexpr auto urlC = "url";
@ -49,7 +51,7 @@ constexpr auto httpAuthPrefix = "http_";
constexpr auto webflowAuthPrefix = "webflow_"; constexpr auto webflowAuthPrefix = "webflow_";
constexpr auto legacyRelativeConfigLocationC = "/ownCloud/owncloud.cfg"; constexpr auto legacyRelativeConfigLocationC = "/ownCloud/owncloud.cfg";
constexpr auto legacyOcSettingsC = "ownCloud"; constexpr auto legacyCfgFileNameC = "owncloud.cfg";
// The maximum versions that this client can read // The maximum versions that this client can read
constexpr auto maxAccountsVersion = 2; constexpr auto maxAccountsVersion = 2;
@ -148,47 +150,81 @@ bool AccountManager::restoreFromLegacySettings()
// if the settings file could not be opened, the childKeys list is empty // if the settings file could not be opened, the childKeys list is empty
// then try to load settings from a very old place // then try to load settings from a very old place
if (settings->childKeys().isEmpty()) { if (settings->childKeys().isEmpty()) {
// Now try to open the original ownCloud settings to see if they exist. // Legacy settings used QDesktopServices to get the location for the config folder in 2.4 and before
auto oCCfgFile = QDir::fromNativeSeparators(settings->fileName()); const auto legacy2_4CfgSettingsLocation = QString(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/data"));
// replace the last two segments with ownCloud/owncloud.cfg const auto legacy2_4CfgFileParentFolder = legacy2_4CfgSettingsLocation.left(legacy2_4CfgSettingsLocation.lastIndexOf('/'));
oCCfgFile = oCCfgFile.left(oCCfgFile.lastIndexOf('/'));
oCCfgFile = oCCfgFile.left(oCCfgFile.lastIndexOf('/'));
oCCfgFile += QLatin1String(legacyRelativeConfigLocationC);
qCInfo(lcAccountManager) << "Migrate: checking old config " << oCCfgFile; // 2.5+ (rest of 2.x series)
const auto legacy2_5CfgSettingsLocation = QStandardPaths::writableLocation(Utility::isWindows() ? QStandardPaths::AppDataLocation : QStandardPaths::AppConfigLocation);
const auto legacy2_5CfgFileParentFolder = legacy2_5CfgSettingsLocation.left(legacy2_5CfgSettingsLocation.lastIndexOf('/'));
QFileInfo fi(oCCfgFile); // Now try the locations we use today
if (fi.isReadable()) { const auto fullLegacyCfgFile = QDir::fromNativeSeparators(settings->fileName());
std::unique_ptr<QSettings> oCSettings(new QSettings(oCCfgFile, QSettings::IniFormat)); const auto legacyCfgFileParentFolder = fullLegacyCfgFile.left(fullLegacyCfgFile.lastIndexOf('/'));
oCSettings->beginGroup(QLatin1String(legacyOcSettingsC)); const auto legacyCfgFileGrandParentFolder = legacyCfgFileParentFolder.left(legacyCfgFileParentFolder.lastIndexOf('/'));
// Check the theme url to see if it is the same url that the oC config was for const auto legacyCfgFileNamePath = QString(QStringLiteral("/") + legacyCfgFileNameC);
auto overrideUrl = Theme::instance()->overrideServerUrl(); const auto legacyCfgFileRelativePath = QString(legacyRelativeConfigLocationC);
if (!overrideUrl.isEmpty()) {
if (overrideUrl.endsWith('/')) { const auto legacyLocations = QVector<QString>{legacy2_4CfgFileParentFolder + legacyCfgFileRelativePath,
overrideUrl.chop(1); legacy2_5CfgFileParentFolder + legacyCfgFileRelativePath,
} legacyCfgFileParentFolder + legacyCfgFileNamePath,
auto oCUrl = oCSettings->value(QLatin1String(urlC)).toString(); legacyCfgFileGrandParentFolder + legacyCfgFileRelativePath};
if (oCUrl.endsWith('/')) {
oCUrl.chop(1); for (const auto &configFile : legacyLocations) {
if (const QFileInfo configFileInfo(configFile);
configFileInfo.exists() && configFileInfo.isReadable()) {
qCInfo(lcAccountManager) << "Migrate: checking old config " << configFile;
auto oCSettings = std::make_unique<QSettings>(configFile, QSettings::IniFormat);
if (oCSettings->status() != QSettings::Status::NoError) {
qCInfo(lcAccountManager) << "Error reading legacy configuration file" << oCSettings->status();
} }
// in case the urls are equal reset the settings object to read from // Check the theme url to see if it is the same url that the oC config was for
// the ownCloud settings object auto overrideUrl = Theme::instance()->overrideServerUrl();
qCInfo(lcAccountManager) << "Migrate oC config if " << oCUrl << " == " << overrideUrl << ":" qCInfo(lcAccountManager) << "Migrate: overrideUrl" << overrideUrl;
<< (oCUrl == overrideUrl ? "Yes" : "No"); if (!overrideUrl.isEmpty()) {
if (oCUrl == overrideUrl) { 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); settings = std::move(oCSettings);
} }
break;
} else {
qCInfo(lcAccountManager) << "Migrate: could not read old config " << configFile;
} }
} }
} }
// Try to load the single account. // Try to load the single account.
if (!settings->childKeys().isEmpty()) { if (!settings->childKeys().isEmpty()) {
if (const auto acc = loadAccountHelper(*settings)) { settings->beginGroup(accountsC);
addAccount(acc); const auto childGroups = settings->childGroups();
return true; for (const auto &accountId : childGroups) {
settings->beginGroup(accountId);
if (const auto acc = loadAccountHelper(*settings)) {
addAccount(acc);
return true;
}
} }
} }
return false; return false;

View file

@ -49,7 +49,7 @@
namespace { namespace {
#ifndef VERSION_C #ifndef VERSION_C
#define VERSION_C #define VERSION_C
constexpr auto versionC= "version"; constexpr auto versionC = "version";
#endif #endif
} }
namespace OCC { namespace OCC {

View file

@ -39,12 +39,11 @@
#include <QNetworkProxy> #include <QNetworkProxy>
namespace { namespace {
#ifndef VERSION_C constexpr auto settingsAccountsC = "Accounts";
#define VERSION_C constexpr auto settingsFoldersC = "Folders";
constexpr auto versionC= "version"; constexpr auto settingsVersionC = "version";
#endif constexpr auto maxFoldersVersion = 1;
} }
static const int maxFoldersVersion = 1;
namespace OCC { namespace OCC {
@ -323,7 +322,7 @@ void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account,
f->switchToVirtualFiles(); f->switchToVirtualFiles();
} }
// Migrate the old "usePlaceholders" setting to the root folder pin state // Migrate the old "usePlaceholders" setting to the root folder pin state
if (settings.value(QLatin1String(versionC), 1).toInt() == 1 if (settings.value(QLatin1String(settingsVersionC), 1).toInt() == 1
&& settings.value(QLatin1String("usePlaceholders"), false).toBool()) { && settings.value(QLatin1String("usePlaceholders"), false).toBool()) {
qCInfo(lcFolderMan) << "Migrate: From usePlaceholders to PinState::OnlineOnly"; qCInfo(lcFolderMan) << "Migrate: From usePlaceholders to PinState::OnlineOnly";
f->setRootPinState(PinState::OnlineOnly); f->setRootPinState(PinState::OnlineOnly);
@ -347,7 +346,7 @@ int FolderMan::setupFoldersMigration()
{ {
ConfigFile cfg; ConfigFile cfg;
QDir storageDir(cfg.configPath()); QDir storageDir(cfg.configPath());
_folderConfigPath = cfg.configPath() + QLatin1String("folders"); _folderConfigPath = cfg.configPath();
qCInfo(lcFolderMan) << "Setup folders from " << _folderConfigPath << "(migration)"; qCInfo(lcFolderMan) << "Setup folders from " << _folderConfigPath << "(migration)";
@ -378,11 +377,11 @@ void FolderMan::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringLi
auto processSubgroup = [&](const QString &name) { auto processSubgroup = [&](const QString &name) {
settings->beginGroup(name); settings->beginGroup(name);
const int foldersVersion = settings->value(QLatin1String(versionC), 1).toInt(); const int foldersVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
if (foldersVersion <= maxFoldersVersion) { if (foldersVersion <= maxFoldersVersion) {
foreach (const auto &folderAlias, settings->childGroups()) { foreach (const auto &folderAlias, settings->childGroups()) {
settings->beginGroup(folderAlias); settings->beginGroup(folderAlias);
const int folderVersion = settings->value(QLatin1String(versionC), 1).toInt(); const int folderVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
if (folderVersion > FolderDefinition::maxSettingsVersion()) { if (folderVersion > FolderDefinition::maxSettingsVersion()) {
ignoreKeys->append(settings->group()); ignoreKeys->append(settings->group());
} }
@ -508,29 +507,10 @@ Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
// Check if the filename is equal to the group setting. If not, use the group // Check if the filename is equal to the group setting. If not, use the group
// name as an alias. // name as an alias.
QStringList groups = settings.childGroups(); const auto groups = settings.childGroups();
if (groups.isEmpty()) {
if (!groups.contains(escapedAlias) && groups.count() > 0) { qCWarning(lcFolderMan) << "empty file:" << cfgFile.filePath();
escapedAlias = groups.first(); return folder;
}
settings.beginGroup(escapedAlias); // read the group with the same name as the file which is the folder alias
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 (backend.isEmpty() || backend != QLatin1String("owncloud")) {
qCWarning(lcFolderMan) << "obsolete configuration of type" << backend;
return nullptr;
}
// cut off the leading slash, oCUrl always has a trailing.
if (targetPath.startsWith(QLatin1Char('/'))) {
targetPath.remove(0, 1);
} }
if (!accountState) { if (!accountState) {
@ -538,28 +518,80 @@ Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
return nullptr; return nullptr;
} }
FolderDefinition folderDefinition; settings.beginGroup(settingsAccountsC);
folderDefinition.alias = alias; const auto rootChildGroups = settings.childGroups();
folderDefinition.localPath = path; for (const auto &accountId : rootChildGroups) {
folderDefinition.targetPath = targetPath; qCDebug(lcFolderMan) << "try to migrate accountId:" << accountId;
folderDefinition.paused = paused; settings.beginGroup(accountId);
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles(); settings.beginGroup(settingsFoldersC);
folder = addFolderInternal(folderDefinition, accountState, std::make_unique<VfsOff>()); if (settings.childGroups().isEmpty()) {
if (folder) { continue;
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
} }
folder->saveToSettings(); const auto childGroups = settings.childGroups();
for (const auto &alias : childGroups) {
settings.beginGroup(alias);
qCDebug(lcFolderMan) << "try to migrate folder alias:" << alias;
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 (path.isEmpty()) {
qCDebug(lcFolderMan) << "localPath is empty";
settings.endGroup();
continue;
}
if (targetPath.isEmpty()) {
qCDebug(lcFolderMan) << "targetPath is empty";
settings.endGroup();
continue;
}
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();
}
settings.endGroup();
settings.endGroup();
} }
qCInfo(lcFolderMan) << "Migrated!" << folder;
settings.sync();
return folder; return folder;
} }