Skip to content
Draft
156 changes: 56 additions & 100 deletions src/gui/accountmanager.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2015 ownCloud GmbH
Expand All @@ -20,6 +20,7 @@
#if !DISABLE_ACCOUNT_MIGRATION
#include "legacyaccountselectiondialog.h"
#endif
#include "settings/migration.h"

#include <QSettings>
#include <QDir>
Expand Down Expand Up @@ -86,7 +87,6 @@
constexpr auto generalC = "General";
}


namespace OCC {

Q_LOGGING_CATEGORY(lcAccountManager, "nextcloud.gui.account.manager", QtInfoMsg)
Expand Down Expand Up @@ -196,110 +196,64 @@
{
qCInfo(lcAccountManager) << "Migrate: restoreFromLegacySettings, checking settings group"
<< Theme::instance()->appName();

// try to open the correctly themed settings
auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());

auto wasLegacyImportDialogDisplayed = false;
const auto displayLegacyImportDialog = Theme::instance()->displayLegacyImportDialog();
QStringList selectedAccountIds;

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

auto legacyLocations = QVector<QString>{legacy2_4CfgFileParentFolder + legacyCfgFileRelativePath,
legacy2_5CfgFileParentFolder + legacyCfgFileRelativePath,
legacyCfgFileParentFolder + legacyCfgFileNamePath,
legacyCfgFileGrandParentFolder + legacyCfgFileRelativePath};

if (Theme::instance()->isBranded()) {
const auto unbrandedCfgFileNamePath = QString(QStringLiteral("/") + unbrandedCfgFileNameC);
const auto unbrandedCfgFileRelativePath = QString(unbrandedRelativeConfigLocationC);
legacyLocations.append({legacyCfgFileParentFolder + unbrandedCfgFileNamePath, legacyCfgFileGrandParentFolder + unbrandedCfgFileRelativePath});
}

for (const auto &configFile : std::as_const(legacyLocations)) {
auto oCSettings = std::make_unique<QSettings>(configFile, QSettings::IniFormat);
if (oCSettings->status() != QSettings::Status::NoError) {
qCInfo(lcAccountManager) << "Error reading legacy configuration file" << oCSettings->status();
break;
}

oCSettings->beginGroup(QLatin1String(accountsC));
const auto childGroups = oCSettings->childGroups();
const auto accountsListSize = childGroups.size();
oCSettings->endGroup(); //accountsC
if (const QFileInfo configFileInfo(configFile);
configFileInfo.exists() && configFileInfo.isReadable()) {

qCInfo(lcAccountManager) << "Migrate: checking old config " << configFile;
if (!forceLegacyImport() && accountsListSize > 0 && displayLegacyImportDialog) {
wasLegacyImportDialogDisplayed = true;
if (accountsListSize == 1) {
const auto importQuestion =
tr("An account was detected from a legacy desktop client.\n"
"Should the account be imported?");
QMessageBox importMessageBox(QMessageBox::Question, tr("Legacy import"), importQuestion);
importMessageBox.addButton(tr("Import"), QMessageBox::AcceptRole);
const auto skipButton = importMessageBox.addButton(tr("Skip"), QMessageBox::DestructiveRole);
importMessageBox.exec();
if (importMessageBox.clickedButton() == skipButton) {
return false;
}
selectedAccountIds = childGroups;
} else {
QVector<LegacyAccountSelectionDialog::AccountItem> accountsToDisplay;
oCSettings->beginGroup(QLatin1String(accountsC));
for (const auto &accId : childGroups) {
oCSettings->beginGroup(accId);
const auto displayName = oCSettings->value(QLatin1String(displayNameC)).toString();
const auto urlStr = oCSettings->value(QLatin1String(urlC)).toString();
oCSettings->endGroup(); //accId
const auto label = QString("%1 - %2").arg(displayName, urlStr);
accountsToDisplay.push_back({accId, label});
}
oCSettings->endGroup(); //accountsC

LegacyAccountSelectionDialog accountSelectionDialog(accountsToDisplay);
if (accountSelectionDialog.exec() != QDialog::Accepted) {
return false;
}
selectedAccountIds = accountSelectionDialog.selectedAccountIds();
if (selectedAccountIds.isEmpty()) {
return false;
}
}
} else {
selectedAccountIds = childGroups;
Migration migration;
if (const auto legacyData = migration.legacyData(); !legacyData.isNull()) {

const auto displayLegacyImportDialog = Theme::instance()->displayLegacyImportDialog();

auto oCSettings = std::move(legacyData);

oCSettings->beginGroup(QLatin1String(accountsC));
const auto childGroups = oCSettings->childGroups();
const auto accountsListSize = childGroups.size();
oCSettings->endGroup(); // accountsC

qCInfo(lcAccountManager) << "Migrate: checking old config";
if (!forceLegacyImport() && displayLegacyImportDialog && accountsListSize > 0) {
wasLegacyImportDialogDisplayed = true;
if (childGroups.size() == 1) {
const auto importQuestion =
tr("An account was detected from a legacy desktop client.\n"
"Should the account be imported?");
QMessageBox importMessageBox(QMessageBox::Question, tr("Legacy import"), importQuestion);
importMessageBox.addButton(tr("Import"), QMessageBox::AcceptRole);
const auto skipButton = importMessageBox.addButton(tr("Skip"), QMessageBox::DestructiveRole);
importMessageBox.exec();
if (importMessageBox.clickedButton() == skipButton) {
return false;
}

const auto legacyVersion = oCSettings->value(ConfigFile::clientVersionC, {}).toString();
ConfigFile().setClientPreviousVersionString(legacyVersion);
qCInfo(lcAccountManager) << "Migrating from" << legacyVersion;
qCInfo(lcAccountManager) << "Copy settings" << oCSettings->allKeys().join(", ");
settings = std::move(oCSettings);
ConfigFile::setDiscoveredLegacyConfigPath(configFileInfo.canonicalPath());
break;
selectedAccountIds = childGroups;
} else {
qCInfo(lcAccountManager) << "Migrate: could not read old config " << configFile;
QVector<LegacyAccountSelectionDialog::AccountItem> accountsToDisplay;
oCSettings->beginGroup(QLatin1String(accountsC));
for (const auto &accId : childGroups) {
oCSettings->beginGroup(accId);
const auto displayName = oCSettings->value(QLatin1String(displayNameC)).toString();
const auto urlStr = oCSettings->value(QLatin1String(urlC)).toString();
oCSettings->endGroup(); // accId
const auto label = QString("%1 - %2").arg(displayName, urlStr);
accountsToDisplay.push_back({accId, label});
}
oCSettings->endGroup(); // accountsC

LegacyAccountSelectionDialog accountSelectionDialog(accountsToDisplay);
if (accountSelectionDialog.exec() != QDialog::Accepted) {
return false;
}
selectedAccountIds = accountSelectionDialog.selectedAccountIds();
if (selectedAccountIds.isEmpty()) {
return false;
}
}
} else {
selectedAccountIds = childGroups;
}

settings.reset(oCSettings.get());
}

ConfigFile configFile;
Expand Down Expand Up @@ -345,7 +299,7 @@
configFile.setDownloadLimit(settings->value(ConfigFile::downloadLimitC, configFile.downloadLimit()).toInt());

// Try to load the single account.
configFile.setMigrationPhase(ConfigFile::MigrationPhase::SetupUsers);
migration.setPhase(Migration::Phase::SetupUsers);
if (!settings->childKeys().isEmpty()) {
settings->beginGroup(accountsC);
const auto childGroups = selectedAccountIds.isEmpty() ? settings->childGroups() : selectedAccountIds;
Expand Down Expand Up @@ -545,8 +499,9 @@

// Override user settings with global settings if user is set to use global settings
ConfigFile configFile;
Migration migration;
auto accountProxySetting = settings.value(networkProxySettingC).toInt();
if (accountProxySetting == 0 && configFile.isMigrationInProgress()) {
if (accountProxySetting == 0 && migration.isInProgress()) {
accountProxyType = static_cast<QNetworkProxy::ProxyType>(configFile.proxyType());
accountProxyHost = configFile.proxyHostName();
accountProxyPort = configFile.proxyPort();
Expand Down Expand Up @@ -680,8 +635,9 @@
acc->setDownloadLimit(settings.value(networkDownloadLimitC).toInt());

ConfigFile configFile;
Migration migration;
const auto proxyPasswordKey = QString(acc->userIdAtHostWithPort() + networkProxyPasswordKeychainKeySuffixC);
const auto appName = configFile.isUnbrandedToBrandedMigrationInProgress() ? ConfigFile::unbrandedAppName
const auto appName = migration.isUnbrandedToBrandedMigration() ? ConfigFile::unbrandedAppName
: Theme::instance()->appName();
const auto job = new QKeychain::ReadPasswordJob(appName, this);
job->setKey(proxyPasswordKey);
Expand Down
11 changes: 7 additions & 4 deletions src/gui/accountstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "ocsuserstatusconnector.h"
#include "pushnotifications.h"
#include "networkjobs.h"
#include "settings/migration.h"

#include <QSettings>
#include <QTimer>
Expand Down Expand Up @@ -297,9 +298,10 @@
if (!account()->credentials()->wasFetched()) {
_waitingForNewCredentials = true;
ConfigFile configFile;
const auto shouldTryUnbrandedToBrandedMigration = configFile.shouldTryUnbrandedToBrandedMigration();
Migration migration;

Check warning on line 301 in src/gui/accountstate.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/accountstate.cpp:301:19 [cppcoreguidelines-init-variables]

variable 'migration' is not initialized
const auto shouldTryUnbrandedToBrandedMigration = migration.shouldTryUnbrandedToBrandedMigration();
qCDebug(lcAccountState) << "shouldTryUnbrandedToBrandedMigration?" << shouldTryUnbrandedToBrandedMigration;
qCDebug(lcAccountState) << "migrationPhase?" << configFile.migrationPhase();
qCDebug(lcAccountState) << "migration Phase?" << migration.phase();
const auto appName = shouldTryUnbrandedToBrandedMigration ? configFile.unbrandedAppName : "";
account()->credentials()->fetchFromKeychain(appName);
return;
Expand Down Expand Up @@ -498,8 +500,9 @@
<< "attempting to connect";
_waitingForNewCredentials = false;
ConfigFile configFile;
if (configFile.isMigrationInProgress()) {
configFile.setMigrationPhase(ConfigFile::MigrationPhase::Done);
Migration migration;

Check warning on line 503 in src/gui/accountstate.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/accountstate.cpp:503:15 [cppcoreguidelines-init-variables]

variable 'migration' is not initialized
if (migration.isInProgress()) {
migration.setPhase(Migration::Phase::Done);
}
checkConnectivity();
}
Expand Down
43 changes: 15 additions & 28 deletions src/gui/application.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
Expand Down Expand Up @@ -39,6 +39,7 @@
#include "common/vfs.h"

#include "config.h"
#include "settings/migration.h"

#if defined(Q_OS_WIN)
#include <windows.h>
Expand Down Expand Up @@ -121,20 +122,21 @@
bool Application::configVersionMigration()
{
ConfigFile configFile;
const auto shouldTryToMigrate = configFile.shouldTryToMigrate();
Migration migration;
const auto shouldTryToMigrate = migration.shouldTryToMigrate();
if (!shouldTryToMigrate) {
qCInfo(lcApplication) << "This is not an upgrade/downgrade/migration. Proceed to read current application config file.";
configFile.setMigrationPhase(ConfigFile::MigrationPhase::Done);
migration.setPhase(Migration::Phase::Done);
return false;
}

configFile.setMigrationPhase(ConfigFile::MigrationPhase::SetupConfigFile);
migration.setPhase(Migration::Phase::SetupConfigFile);
QStringList deleteKeys, ignoreKeys;
AccountManager::backwardMigrationSettingsKeys(&deleteKeys, &ignoreKeys);
FolderMan::backwardMigrationSettingsKeys(&deleteKeys, &ignoreKeys);

qCDebug(lcApplication) << "Migration is in progress:" << configFile.isMigrationInProgress();
const auto versionChanged = configFile.hasVersionChanged();
qCDebug(lcApplication) << "Migration is in progress:" << migration.isInProgress();
const auto versionChanged = migration.versionChanged();
if (versionChanged) {
qCInfo(lcApplication) << "Version changed. Removing updater settings from config.";
configFile.cleanUpdaterConfiguration();
Expand All @@ -152,25 +154,9 @@
// default is now off to displaying dialog warning user of too many files deletion
configFile.setPromptDeleteFiles(false);

// back up all old config files
QStringList backupFilesList;
QDir configDir(configFile.configPath());
const auto anyConfigFileNameList = configDir.entryInfoList({"*.cfg"}, QDir::Files);
for (const auto &oldConfig : anyConfigFileNameList) {
const auto oldConfigFileName = oldConfig.fileName();
const auto oldConfigFilePath = oldConfig.filePath();
const auto newConfigFileName = configFile.configFile();
backupFilesList.append(configFile.backup(oldConfigFileName));
if (oldConfigFilePath != newConfigFileName) {
if (!QFile::rename(oldConfigFilePath, newConfigFileName)) {
qCWarning(lcApplication) << "Failed to rename configuration file from" << oldConfigFilePath << "to" << newConfigFileName;
}
}
}

// We want to message the user either for destructive changes,
// back up all old config files and message the user either for destructive changes,
// or if we're ignoring something and the client version changed.
if (configFile.showConfigBackupWarning() && backupFilesList.count() > 0) {
if (const auto backupFilesList = configFile.backupConfigFiles(); configFile.showConfigBackupWarning() && backupFilesList.count() > 0) {
QMessageBox box(
QMessageBox::Warning,
APPLICATION_SHORTNAME,
Expand All @@ -180,7 +166,7 @@
"Continuing will mean <b>%2 these settings</b>.<br>"
"<br>"
"The current configuration file was already backed up to <i>%3</i>.")
.arg((configFile.isDowngrade() ? tr("newer", "newer software version") : tr("older", "older software version")),
.arg((migration.isDowngrade() ? tr("newer", "newer software version") : tr("older", "older software version")),
deleteKeys.isEmpty()? tr("ignoring") : tr("deleting"),
backupFilesList.join("<br>")));
box.addButton(tr("Quit"), QMessageBox::AcceptRole);
Expand Down Expand Up @@ -493,18 +479,18 @@
{
_folderManager.reset(new FolderMan);
ConfigFile configFile;
configFile.setMigrationPhase(ConfigFile::MigrationPhase::SetupUsers);
Migration migration;
migration.setPhase(Migration::Phase::SetupUsers);
const auto accountsRestoreResult = restoreLegacyAccount();
if (accountsRestoreResult == AccountManager::AccountsNotFound || accountsRestoreResult == AccountManager::AccountsRestoreFailure) {
qCWarning(lcApplication) << "Migration result: " << accountsRestoreResult;
qCDebug(lcApplication) << "is migration disabled?" << DISABLE_ACCOUNT_MIGRATION;
qCWarning(lcApplication) << "No accounts were migrated, prompting user to set up accounts and folders from scratch.";
configFile.setMigrationPhase(ConfigFile::MigrationPhase::Done);

migration.setPhase(Migration::Phase::Done);
return;
}

configFile.setMigrationPhase(ConfigFile::MigrationPhase::SetupFolders);
migration.setPhase(Migration::Phase::SetupFolders);
const auto foldersListSize = FolderMan::instance()->setupFolders();
FolderMan::instance()->setSyncEnabled(true);

Expand All @@ -519,6 +505,7 @@
const auto accounts = AccountManager::instance()->accounts();
const auto accountsListSize = accounts.size();
if (accountsRestoreResult == AccountManager::AccountsRestoreSuccessFromLegacyVersion
&& accountsListSize > 0
&& Theme::instance()->displayLegacyImportDialog()
&& !AccountManager::instance()->forceLegacyImport()
&& accountsListSize > 0) {
Expand Down
4 changes: 3 additions & 1 deletion src/gui/folderman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <pushnotifications.h>
#include <syncengine.h>
#include "updatee2eefolderusersmetadatajob.h"
#include "settings/migration.h"

#ifdef Q_OS_MACOS
#include <CoreServices/CoreServices.h>
Expand Down Expand Up @@ -351,7 +352,8 @@ int FolderMan::setupFoldersMigration()
auto configPath = _folderConfigPath;

#if !DISABLE_ACCOUNT_MIGRATION
if (const auto legacyConfigPath = ConfigFile::discoveredLegacyConfigPath();!legacyConfigPath.isEmpty()) {
Migration migration;
if (const auto legacyConfigPath = migration.discoveredLegacyConfigPath(); !legacyConfigPath.isEmpty()) {
configPath = legacyConfigPath;
qCInfo(lcFolderMan) << "Starting folder migration from legacy path:" << legacyConfigPath;
}
Expand Down
3 changes: 3 additions & 0 deletions src/gui/folderman.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#ifndef FOLDERMAN_H
#define FOLDERMAN_H

#include <QByteArray>

Check failure on line 10 in src/gui/folderman.h

View workflow job for this annotation

GitHub Actions / build

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

'QByteArray' file not found
#include <QObject>
#include <QQueue>
#include <QList>
Expand All @@ -28,6 +28,7 @@
class TestSyncConflictsModel;
class TestRemoteWipe;
class FolderManTestHelper;
class TestMigration;

Check warning on line 31 in src/gui/folderman.h

View workflow job for this annotation

GitHub Actions / build

src/gui/folderman.h:31:7 [cppcoreguidelines-avoid-non-const-global-variables]

variable 'TestMigration' is non-const and globally accessible, consider making it const

namespace OCC {

Expand All @@ -36,6 +37,7 @@
class SocketApi;
class LockWatcher;
class UpdateE2eeFolderUsersMetadataJob;
class Migration;

/**
* @brief The FolderMan class
Expand Down Expand Up @@ -418,6 +420,7 @@
friend class ::TestFolderStatusModel;
friend class ::TestRemoteWipe;
friend class ::FolderManTestHelper;
friend class ::TestMigration;
};

} // namespace OCC
Expand Down
Loading
Loading