Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix migration from old settings configuration files #5141

Merged
merged 9 commits into from
Dec 15, 2022
112 changes: 74 additions & 38 deletions src/gui/accountmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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);

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);
// 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('/'));

// 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('/'));

// 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('/'));

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;
Expand Down
2 changes: 1 addition & 1 deletion src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
namespace {
#ifndef VERSION_C
#define VERSION_C
constexpr auto versionC= "version";
constexpr auto versionC = "version";
#endif
}
namespace OCC {
Expand Down
130 changes: 81 additions & 49 deletions src/gui/folderman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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);
Expand All @@ -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)";

Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -508,58 +507,91 @@ 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();
const auto 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(settingsAccountsC);
const auto rootChildGroups = settings.childGroups();
for (const auto &accountId : rootChildGroups) {
qCDebug(lcFolderMan) << "try to migrate accountId:" << accountId;
settings.beginGroup(accountId);
settings.beginGroup(settingsFoldersC);

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;
}

Expand Down