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

Bugfix migration from legacy clients. #7807

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
45 changes: 8 additions & 37 deletions src/gui/accountmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ constexpr auto unbrandedRelativeConfigLocationC = "/Nextcloud/nextcloud.cfg";
constexpr auto unbrandedCfgFileNameC = "nextcloud.cfg";

// The maximum versions that this client can read
constexpr auto maxAccountsVersion = 2;
constexpr auto maxAccountVersion = 1;
constexpr auto maxAccountsVersion = 13;
constexpr auto maxAccountVersion = 13;

constexpr auto serverHasValidSubscriptionC = "serverHasValidSubscription";
}
Expand Down Expand Up @@ -149,6 +149,9 @@ void AccountManager::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStr
const auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
const auto accountsVersion = settings->value(QLatin1String(versionC)).toInt();

qCInfo(lcAccountManager) << "Checking for accounts versions.";
qCInfo(lcAccountManager) << "Config accounts version:" << accountsVersion;
qCInfo(lcAccountManager) << "Max accounts Version is set to:" << maxAccountsVersion;
if (accountsVersion <= maxAccountsVersion) {
const auto settingsChildGroups = settings->childGroups();
for (const auto &accountId : settingsChildGroups) {
Expand All @@ -157,6 +160,7 @@ void AccountManager::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStr

if (accountVersion > maxAccountVersion) {
ignoreKeys->append(settings->group());
qCInfo(lcAccountManager) << "Ignoring account" << accountId << "because of version" << accountVersion;
}
settings->endGroup();
}
Expand Down Expand Up @@ -235,41 +239,8 @@ bool AccountManager::restoreFromLegacySettings()
}
}

// Check the theme url to see if it is the same url that the oC config was for
const auto overrideUrl = Theme::instance()->overrideServerUrl();
const auto cleanOverrideUrl = overrideUrl.endsWith('/') ? overrideUrl.chopped(1) : overrideUrl;
qCInfo(lcAccountManager) << "Migrate: overrideUrl" << cleanOverrideUrl;

if (!cleanOverrideUrl.isEmpty()) {
oCSettings->beginGroup(QLatin1String(accountsC));
const auto accountsChildGroups = oCSettings->childGroups();
for (const auto &accountId : accountsChildGroups) {
oCSettings->beginGroup(accountId);
const auto oCUrl = oCSettings->value(QLatin1String(urlC)).toString();
const auto cleanOCUrl = oCUrl.endsWith('/') ? oCUrl.chopped(1) : oCUrl;

// in case the urls are equal reset the settings object to read from
// the ownCloud settings object
qCInfo(lcAccountManager) << "Migrate oC config if " << cleanOCUrl << " == " << cleanOverrideUrl << ":"
<< (cleanOCUrl == cleanOverrideUrl ? "Yes" : "No");
if (cleanOCUrl == cleanOverrideUrl) {
qCInfo(lcAccountManager) << "Copy settings" << oCSettings->allKeys().join(", ");
oCSettings->endGroup(); // current accountID group
oCSettings->endGroup(); // accounts group
settings = std::move(oCSettings);
break;
}

oCSettings->endGroup();
}

if (oCSettings) {
oCSettings->endGroup();
}
} else {
qCInfo(lcAccountManager) << "Copy settings" << oCSettings->allKeys().join(", ");
settings = std::move(oCSettings);
}
qCInfo(lcAccountManager) << "Copy settings" << oCSettings->allKeys().join(", ");
settings = std::move(oCSettings);

ConfigFile::setDiscoveredLegacyConfigPath(configFileInfo.canonicalPath());
break;
Expand Down
32 changes: 20 additions & 12 deletions src/gui/application.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/gui/application.cpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/gui/application.cpp

File src/gui/application.cpp does not conform to Custom style guidelines. (lines 272)
* Copyright (C) by Duncan Mac-Vicar P. <[email protected]>
* Copyright (C) by Klaas Freitag <[email protected]>
* Copyright (C) by Daniel Molkentin <[email protected]>
Expand Down Expand Up @@ -143,6 +143,11 @@
const auto versionChanged = previousVersion != currentVersion;
const auto downgrading = previousVersion > currentVersion;

if (versionChanged) {
qCInfo(lcApplication) << "Version changed. Removing updater settings from config.";
configFile.cleanUpdaterConfiguration();
}

if (!versionChanged && !(!deleteKeys.isEmpty() || (!ignoreKeys.isEmpty() && versionChanged))) {
return true;
}
Expand Down Expand Up @@ -194,13 +199,16 @@
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
return false;
}
}

if (!deleteKeys.isEmpty()) {
auto settings = ConfigFile::settingsWithGroup("foo");
settings->endGroup();

// Wipe confusing keys from the future, ignore the others
for (const auto &badKey : std::as_const(deleteKeys)) {
settings->remove(badKey);
qCInfo(lcApplication) << "Migration: removed" << badKey << "key from settings.";
}
}

Expand Down Expand Up @@ -236,40 +244,40 @@
setWindowIcon(_theme->applicationIcon());

if (!ConfigFile().exists()) {
// Migrate from version <= 2.4
setApplicationName(_theme->appNameGUI());
// We need to use the deprecated QDesktopServices::storageLocation because of its Qt4
// behavior of adding "data" to the path
QString oldDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/data/" + organizationName() + "/" + applicationName();
if (oldDir.endsWith('/')) oldDir.chop(1); // macOS 10.11.x does not like trailing slash for rename/move.
QString legacyDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/" + APPLICATION_CONFIG_NAME;

if (legacyDir.endsWith('/')) {
legacyDir.chop(1); // macOS 10.11.x does not like trailing slash for rename/move.
}
setApplicationName(_theme->appName());
if (QFileInfo(oldDir).isDir()) {
if (QFileInfo(legacyDir).isDir()) {
auto confDir = ConfigFile().configPath();

// macOS 10.11.x does not like trailing slash for rename/move.
if (confDir.endsWith('/')) {
confDir.chop(1);
}

qCInfo(lcApplication) << "Migrating old config from" << oldDir << "to" << confDir;
qCInfo(lcApplication) << "Migrating old config from" << legacyDir << "to" << confDir;

if (!QFile::rename(oldDir, confDir)) {
qCWarning(lcApplication) << "Failed to move the old config directory to its new location (" << oldDir << "to" << confDir << ")";
if (!QFile::rename(legacyDir, confDir)) {
qCWarning(lcApplication) << "Failed to move the old config directory to its new location (" << legacyDir << "to" << confDir << ")";

// Try to move the files one by one
if (QFileInfo(confDir).isDir() || QDir().mkdir(confDir)) {
const QStringList filesList = QDir(oldDir).entryList(QDir::Files);
const QStringList filesList = QDir(legacyDir).entryList(QDir::Files);
qCInfo(lcApplication) << "Will move the individual files" << filesList;
for (const auto &name : filesList) {
if (!QFile::rename(oldDir + "/" + name, confDir + "/" + name)) {
if (!QFile::rename(legacyDir + "/" + name, confDir + "/" + name)) {
qCWarning(lcApplication) << "Fallback move of " << name << "also failed";
}
}
}
} else {
#ifndef Q_OS_WIN
// Create a symbolic link so a downgrade of the client would still find the config.
QFile::link(confDir, oldDir);
QFile::link(confDir, legacyDir);
#endif
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/gui/folder.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/gui/folder.h

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/gui/folder.h

File src/gui/folder.h does not conform to Custom style guidelines. (lines 90)
* Copyright (C) by Duncan Mac-Vicar P. <[email protected]>
* Copyright (C) by Daniel Molkentin <[email protected]>
* Copyright (C) by Klaas Freitag <[email protected]>
Expand All @@ -17,7 +17,7 @@
#ifndef MIRALL_FOLDER_H
#define MIRALL_FOLDER_H

#include "syncresult.h"

Check failure on line 20 in src/gui/folder.h

View workflow job for this annotation

GitHub Actions / build

src/gui/folder.h:20:10 [clang-diagnostic-error]

'syncresult.h' file not found
#include "progressdispatcher.h"
#include "common/syncjournaldb.h"
#include "networkjobs.h"
Expand Down Expand Up @@ -85,8 +85,9 @@
* (version remains readable by 2.5.1)
* Version 3: introduction of new windows vfs mode in 2.6.0
* Version 5: available in oC client 4.0.0 and 4.2.0
* Version 13: available in oC client 5.2.0 and 5.3.1
*/
static int maxSettingsVersion() { return 5; }
static int maxSettingsVersion() { return 13; }

/// Ensure / as separator and trailing /.
static QString prepareLocalPath(const QString &path);
Expand Down
146 changes: 79 additions & 67 deletions src/gui/folderman.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/gui/folderman.cpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/gui/folderman.cpp

File src/gui/folderman.cpp does not conform to Custom style guidelines. (lines 533, 576)
* Copyright (C) by Klaas Freitag <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -42,6 +42,7 @@
namespace {
constexpr auto settingsAccountsC = "Accounts";
constexpr auto settingsFoldersC = "Folders";
constexpr auto settingsFoldersWithPlaceholdersC = "FoldersWithPlaceholders";
constexpr auto settingsVersionC = "version";
constexpr auto maxFoldersVersion = 1;

Expand Down Expand Up @@ -388,16 +389,19 @@
auto processSubgroup = [&](const QString &name) {
settings->beginGroup(name);
const auto foldersVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
qCInfo(lcFolderMan) << "FolderDefinition::maxSettingsVersion:" << FolderDefinition::maxSettingsVersion();
if (foldersVersion <= maxFoldersVersion) {
for (const auto &folderAlias : settings->childGroups()) {
settings->beginGroup(folderAlias);
const auto folderVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
if (folderVersion > FolderDefinition::maxSettingsVersion()) {
qCInfo(lcFolderMan) << "Ignoring folder:" << folderAlias << "version:" << folderVersion;
ignoreKeys->append(settings->group());
}
settings->endGroup();
}
} else {
qCInfo(lcFolderMan) << "Ignoring group:" << name << "version:" << foldersVersion;
deleteKeys->append(settings->group());
}
settings->endGroup();
Expand Down Expand Up @@ -523,89 +527,97 @@
return;
}

settings.beginGroup(settingsAccountsC);
qCDebug(lcFolderMan) << "try to migrate accountId:" << accountState->account()->id();
settings.beginGroup(accountState->account()->id());
settings.beginGroup(settingsFoldersC);

if (settings.childGroups().isEmpty()) {
qCDebug(lcFolderMan) << "there are no legacy folders for accountId:" << accountState->account()->id();
return;
}

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;
auto migrateFoldersGroup = [&](const QString &folderGroupName) {
const auto childGroups = settings.childGroups();
if (childGroups.isEmpty()) {
qCDebug(lcFolderMan) << "There are no" << folderGroupName << "to migrate from account" << accountState->account()->id();
return;
}
for (const auto &alias : childGroups) {
settings.beginGroup(alias);
qCDebug(lcFolderMan) << "try to migrate" << folderGroupName << "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 (journalPath.isEmpty()) {
qCDebug(lcFolderMan) << "journalPath is empty";
settings.endGroup();
continue;
}
if (targetPath.isEmpty()) {
qCDebug(lcFolderMan) << "targetPath 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;

if (const auto folder = addFolderInternal(folderDefinition, accountState, std::make_unique<VfsOff>())) {
auto ok = true;
auto legacyBlacklist = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
&ok);
if (!ok) {
qCInfo(lcFolderMan) << "There was a problem retrieving the database selective sync for " << folder;
if (journalPath.isEmpty()) {
qCDebug(lcFolderMan) << "journalPath is empty";
settings.endGroup();
continue;
}

legacyBlacklist << settings.value(QLatin1String("blackList")).toStringList();
if (!legacyBlacklist.isEmpty()) {
qCInfo(lcFolderMan) << "Legacy selective sync list found:" << legacyBlacklist;
for (const auto &legacyFolder : legacyBlacklist) {
folder->migrateBlackListPath(legacyFolder);
qCDebug(lcFolderMan) << folderGroupName << "located at" << path;

FolderDefinition folderDefinition;
folderDefinition.alias = alias;
folderDefinition.localPath = path;
folderDefinition.targetPath = targetPath;
folderDefinition.journalPath = journalPath;
folderDefinition.paused = paused;
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles;

if (const auto folder = addFolderInternal(folderDefinition, accountState, std::make_unique<VfsOff>())) {
auto ok = true;
auto legacyBlacklist = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
&ok);
if (!ok) {
qCInfo(lcFolderMan) << "There was a problem retrieving the database selective sync for " << folder;
}

legacyBlacklist << settings.value(QLatin1String("blackList")).toStringList();
if (!legacyBlacklist.isEmpty()) {
qCInfo(lcFolderMan) << "Legacy selective sync list found:" << legacyBlacklist;
for (const auto &legacyFolder : legacyBlacklist) {
folder->migrateBlackListPath(legacyFolder);
}
settings.remove(QLatin1String("blackList"));
}
settings.remove(QLatin1String("blackList"));
}

folder->saveToSettings();
folder->saveToSettings();

qCInfo(lcFolderMan) << "Migrated!" << folder->path();
settings.sync();
qCInfo(lcFolderMan) << "Migrated!" << folder->path();
settings.sync();

if (!folder) {
continue;
}
if (!folder) {
continue;
}

scheduleFolder(folder);
emit folderSyncStateChange(folder);
scheduleFolder(folder);
emit folderSyncStateChange(folder);
}
settings.endGroup(); // folder alias
}
};

settings.endGroup();
}
settings.beginGroup(settingsAccountsC);
qCDebug(lcFolderMan) << "try to migrate accountId:" << accountState->account()->id();
settings.beginGroup(accountState->account()->id());

settings.beginGroup(settingsFoldersWithPlaceholdersC);
migrateFoldersGroup(settingsFoldersWithPlaceholdersC);
settings.endGroup();

settings.beginGroup(settingsFoldersC);
migrateFoldersGroup(settingsFoldersC);
settings.endGroup();

settings.endGroup();
settings.endGroup();
return;
}

Expand Down
11 changes: 11 additions & 0 deletions src/libsync/configfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* for more details.
*/

#include "config.h"

Check failure on line 15 in src/libsync/configfile.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/configfile.cpp:15:10 [clang-diagnostic-error]

'config.h' file not found

#include "configfile.h"
#include "theme.h"
Expand Down Expand Up @@ -453,6 +453,17 @@
return fi.absoluteFilePath();
}

void OCC::ConfigFile::cleanUpdaterConfiguration()
{
QSettings settings(configFile(), QSettings::IniFormat);
settings.beginGroup("Updater");
settings.remove("autoUpdateAttempted");
settings.remove("updateTargetVersion");
settings.remove("updateTargetVersionString");
settings.remove("updateAvailable");
settings.sync();
}

QString ConfigFile::backup(const QString &fileName) const
{
const QString baseFilePath = configPath() + fileName;
Expand Down
2 changes: 2 additions & 0 deletions src/libsync/configfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#ifndef CONFIGFILE_H
#define CONFIGFILE_H

#include "owncloudlib.h"

Check failure on line 18 in src/libsync/configfile.h

View workflow job for this annotation

GitHub Actions / build

src/libsync/configfile.h:18:10 [clang-diagnostic-error]

'owncloudlib.h' file not found
#include <memory>
#include <QSharedPointer>
#include <QSettings>
Expand Down Expand Up @@ -48,6 +48,8 @@
[[nodiscard]] QString excludeFile(Scope scope) const;
static QString excludeFileFromSystem(); // doesn't access config dir

void cleanUpdaterConfiguration();

/**
* Creates a backup of any given fileName in the config folder
*
Expand Down
Loading