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 "configfile.h"
#include "sslerrordialog.h"
#include "proxyauthhandler.h"
#include "common/asserts.h"
#include <theme.h>
#include <creds/credentialsfactory.h>
#include <creds/abstractcredentials.h>
#include <cookiejar.h>
#include "creds/credentialsfactory.h"
#include "creds/abstractcredentials.h"
#include "libsync/clientsideencryption.h"
#include "libsync/configfile.h"
#include "libsync/cookiejar.h"
#include "libsync/theme.h"
#include <QSettings>
#include <QDir>
#include <QNetworkAccessManager>
#include <QMessageBox>
#include "clientsideencryption.h"
namespace {
constexpr auto urlC = "url";
@ -49,7 +51,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;
@ -148,47 +150,81 @@ bool AccountManager::restoreFromLegacySettings()
// 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());
// replace the last two segments with ownCloud/owncloud.cfg
oCCfgFile = oCCfgFile.left(oCCfgFile.lastIndexOf('/'));
oCCfgFile = oCCfgFile.left(oCCfgFile.lastIndexOf('/'));
oCCfgFile += QLatin1String(legacyRelativeConfigLocationC);
// Legacy settings used QDesktopServices to get the location for the config folder in 2.4 and before
const auto legacy2_4CfgSettingsLocation = QString(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/data"));
const auto legacy2_4CfgFileParentFolder = legacy2_4CfgSettingsLocation.left(legacy2_4CfgSettingsLocation.lastIndexOf('/'));
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);
if (fi.isReadable()) {
std::unique_ptr<QSettings> oCSettings(new QSettings(oCCfgFile, QSettings::IniFormat));
oCSettings->beginGroup(QLatin1String(legacyOcSettingsC));
// Now try the locations we use today
const auto fullLegacyCfgFile = QDir::fromNativeSeparators(settings->fileName());
const auto legacyCfgFileParentFolder = fullLegacyCfgFile.left(fullLegacyCfgFile.lastIndexOf('/'));
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
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);
const auto legacyCfgFileNamePath = QString(QStringLiteral("/") + legacyCfgFileNameC);
const auto legacyCfgFileRelativePath = QString(legacyRelativeConfigLocationC);
const auto legacyLocations = QVector<QString>{legacy2_4CfgFileParentFolder + legacyCfgFileRelativePath,
legacy2_5CfgFileParentFolder + legacyCfgFileRelativePath,
legacyCfgFileParentFolder + legacyCfgFileNamePath,
legacyCfgFileGrandParentFolder + legacyCfgFileRelativePath};
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
// the ownCloud settings object
qCInfo(lcAccountManager) << "Migrate oC config if " << oCUrl << " == " << overrideUrl << ":"
<< (oCUrl == overrideUrl ? "Yes" : "No");
if (oCUrl == overrideUrl) {
// 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;
} else {
qCInfo(lcAccountManager) << "Migrate: could not read old config " << configFile;
}
}
}
// 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;

View file

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

View file

@ -39,12 +39,11 @@
#include <QNetworkProxy>
namespace {
#ifndef VERSION_C
#define VERSION_C
constexpr auto versionC= "version";
#endif
constexpr auto settingsAccountsC = "Accounts";
constexpr auto settingsFoldersC = "Folders";
constexpr auto settingsVersionC = "version";
constexpr auto maxFoldersVersion = 1;
}
static const int maxFoldersVersion = 1;
namespace OCC {
@ -323,7 +322,7 @@ void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account,
f->switchToVirtualFiles();
}
// 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()) {
qCInfo(lcFolderMan) << "Migrate: From usePlaceholders to PinState::OnlineOnly";
f->setRootPinState(PinState::OnlineOnly);
@ -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)";
@ -378,11 +377,11 @@ void FolderMan::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringLi
auto processSubgroup = [&](const QString &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) {
foreach (const auto &folderAlias, settings->childGroups()) {
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()) {
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
// name as an alias.
QStringList groups = settings.childGroups();
if (!groups.contains(escapedAlias) && groups.count() > 0) {
escapedAlias = groups.first();
}
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);
const auto groups = settings.childGroups();
if (groups.isEmpty()) {
qCWarning(lcFolderMan) << "empty file:" << cfgFile.filePath();
return folder;
}
if (!accountState) {
@ -538,28 +518,80 @@ Folder *FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
return nullptr;
}
FolderDefinition folderDefinition;
folderDefinition.alias = alias;
folderDefinition.localPath = path;
folderDefinition.targetPath = targetPath;
folderDefinition.paused = paused;
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles();
settings.beginGroup(settingsAccountsC);
const auto rootChildGroups = settings.childGroups();
for (const auto &accountId : rootChildGroups) {
qCDebug(lcFolderMan) << "try to migrate accountId:" << accountId;
settings.beginGroup(accountId);
settings.beginGroup(settingsFoldersC);
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 (settings.childGroups().isEmpty()) {
continue;
}
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;
}