Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 95 additions & 133 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 Down Expand Up @@ -63,12 +63,6 @@

constexpr auto networkProxyPasswordKeychainKeySuffixC = "_proxy_password";

constexpr auto legacyRelativeConfigLocationC = "/ownCloud/owncloud.cfg";
constexpr auto legacyCfgFileNameC = "owncloud.cfg";

constexpr auto unbrandedRelativeConfigLocationC = "/Nextcloud/nextcloud.cfg";
constexpr auto unbrandedCfgFileNameC = "nextcloud.cfg";

// The maximum versions that this client can read
constexpr auto maxAccountsVersion = 13;
constexpr auto maxAccountVersion = 13;
Expand All @@ -89,12 +83,15 @@
return &instance;
}

AccountManager::AccountsRestoreResult AccountManager::restore(const bool alsoRestoreLegacySettings)
AccountManager::AccountsRestoreResult AccountManager::restore(const QString &legacyConfigFile, const bool alsoRestoreLegacySettings)

Check warning on line 86 in src/gui/accountmanager.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/accountmanager.cpp:86:55 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
QStringList skipSettingsKeys;
backwardMigrationSettingsKeys(&skipSettingsKeys, &skipSettingsKeys);

const auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
const auto isLegacyMigration = !legacyConfigFile.isEmpty();
auto settings = isLegacyMigration ? std::make_unique<QSettings>(legacyConfigFile, QSettings::IniFormat)
: ConfigFile::settingsWithGroup(QLatin1String(accountsC));

if (settings->status() != QSettings::NoError || !settings->isWritable()) {
qCWarning(lcAccountManager) << "Could not read settings from" << settings->fileName()
<< settings->status();
Expand All @@ -107,13 +104,23 @@
return AccountsRestoreSuccessWithSkipped;
}

// If there are no accounts, check the old format.
if (settings->childGroups().isEmpty() && !settings->contains(QLatin1String(versionC)) && alsoRestoreLegacySettings) {
restoreFromLegacySettings();
return AccountsRestoreSuccessFromLegacyVersion;
qCWarning(lcAccountManager) << "Restoring settings" << settings->allKeys().join(", ") << "from" << (isLegacyMigration ? legacyConfigFile
: ConfigFile().configFile());
// migrate general and proxy settings
if (isLegacyMigration && alsoRestoreLegacySettings) {
ConfigFile configFile;
configFile.setVfsEnabled(settings->value(configFile.isVfsEnabledC).toBool());
configFile.setLaunchOnSystemStartup(settings->value(configFile.launchOnSystemStartupC).toBool());
configFile.setOptionalServerNotifications(settings->value(configFile.optionalServerNotificationsC).toBool());
configFile.setPromptDeleteFiles(settings->value(configFile.promptDeleteC).toBool());
configFile.setShowCallNotifications(settings->value(configFile.showCallNotificationsC).toBool());
configFile.setShowChatNotifications(settings->value(configFile.showChatNotificationsC).toBool());
configFile.setShowInExplorerNavigationPane(settings->value(configFile.showInExplorerNavigationPaneC).toBool());
ClientProxy().saveProxyConfigurationFromSettings(*settings);
}

auto result = AccountsRestoreSuccess;
auto result = isLegacyMigration ? AccountsRestoreSuccess : AccountsRestoreSuccessFromLegacyVersion;
settings->beginGroup(accountsC);
const auto settingsChildGroups = settings->childGroups();
for (const auto &accountId : settingsChildGroups) {
settings->beginGroup(accountId);
Expand All @@ -135,6 +142,7 @@
}
settings->endGroup();
}
settings->endGroup();

return result;
}
Expand Down Expand Up @@ -164,124 +172,6 @@
}
}

bool AccountManager::restoreFromLegacySettings()
{
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();

// 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 : 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 accountsListSize = oCSettings->childGroups().size();
oCSettings->endGroup();
if (const QFileInfo configFileInfo(configFile);
configFileInfo.exists() && configFileInfo.isReadable()) {
qCInfo(lcAccountManager) << "Migrate: checking old config " << configFile;
if (!forceLegacyImport() && accountsListSize > 0 && displayLegacyImportDialog) {
wasLegacyImportDialogDisplayed = true;
const auto importQuestion = accountsListSize > 1
? tr("%1 accounts were detected from a legacy desktop client.\n"
"Should the accounts be imported?").arg(QString::number(accountsListSize))
: tr("1 account was detected from a legacy desktop client.\n"
"Should the account be imported?");
const auto importMessageBox = new QMessageBox(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;
}
}

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

ConfigFile::setDiscoveredLegacyConfigPath(configFileInfo.canonicalPath());
break;
} else {
qCInfo(lcAccountManager) << "Migrate: could not read old config " << configFile;
}
}
}

ConfigFile configFile;
configFile.setVfsEnabled(settings->value(configFile.isVfsEnabledC).toBool());
configFile.setLaunchOnSystemStartup(settings->value(configFile.launchOnSystemStartupC).toBool());
configFile.setOptionalServerNotifications(settings->value(configFile.optionalServerNotificationsC).toBool());
configFile.setPromptDeleteFiles(settings->value(configFile.promptDeleteC).toBool());
configFile.setShowCallNotifications(settings->value(configFile.showCallNotificationsC).toBool());
configFile.setShowChatNotifications(settings->value(configFile.showChatNotificationsC).toBool());
configFile.setShowInExplorerNavigationPane(settings->value(configFile.showInExplorerNavigationPaneC).toBool());
ClientProxy().saveProxyConfigurationFromSettings(*settings);
configFile.setUseUploadLimit(settings->value(configFile.useUploadLimitC).toInt());
configFile.setUploadLimit(settings->value(configFile.uploadLimitC).toInt());
configFile.setUseDownloadLimit(settings->value(configFile.useDownloadLimitC).toInt());
configFile.setDownloadLimit(settings->value(configFile.downloadLimitC).toInt());

// Try to load the single account.
if (!settings->childKeys().isEmpty()) {
settings->beginGroup(accountsC);
const auto childGroups = settings->childGroups();
for (const auto &accountId : childGroups) {
settings->beginGroup(accountId);
if (const auto acc = loadAccountHelper(*settings)) {
addAccount(acc);
}
settings->endGroup();
}
return true;
}

if (wasLegacyImportDialogDisplayed) {
QMessageBox::information(nullptr,
tr("Legacy import"),
tr("Could not import accounts from legacy client configuration."));
}

return false;
}

void AccountManager::save(bool saveCredentials)
{
const auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
Expand Down Expand Up @@ -635,9 +525,9 @@
const auto acc = Account::create();
acc->setSslErrorHandler(new SslDialogErrorHandler);
connect(acc.data(), &Account::proxyAuthenticationRequired,
ProxyAuthHandler::instance(), &ProxyAuthHandler::handleProxyAuthenticationRequired);
ProxyAuthHandler::instance(), &ProxyAuthHandler::handleProxyAuthenticationRequired);
connect(acc.data(), &Account::lockFileError,
Systray::instance(), &Systray::showErrorMessageDialog);
Systray::instance(), &Systray::showErrorMessageDialog);

return acc;
}
Expand Down Expand Up @@ -713,4 +603,76 @@
_forceLegacyImport = forceLegacyImport;
Q_EMIT forceLegacyImportChanged();
}


int AccountManager::setupAccounts()

Check warning on line 608 in src/gui/accountmanager.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/accountmanager.cpp:608:21 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
if (!confirmRestoreExistingAccounts()) {
return 0;
}

ConfigFile configFile;
const auto tryMigrate = configFile.overrideServerUrl().isEmpty();
const auto configFileToRestore = configFile.configFileToRestore();
auto accountsRestoreResult = AccountManager::AccountsRestoreResult::AccountsRestoreSuccess;
if (accountsRestoreResult = restore(configFileToRestore, tryMigrate);
accountsRestoreResult == AccountManager::AccountsRestoreFailure) {
// If there is an error reading the account settings, try again
// after a couple of seconds, if that fails, give up.
// (non-existence is not an error)
Utility::sleep(5);
if (accountsRestoreResult = restore(configFileToRestore, tryMigrate);
accountsRestoreResult == AccountManager::AccountsRestoreFailure) {
qCCritical(lcAccountManager) << "Could not read the account settings, quitting";
QMessageBox::critical(
nullptr,
tr("Error accessing the configuration file"),
tr("There was an error while accessing the configuration "
"file at %1. Please make sure the file can be accessed by your system account.")
.arg(ConfigFile().configFile()),
QMessageBox::Ok
);
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
return 0;
}
}

const auto accountsMigrated = AccountManager::instance()->accounts().size();
qCWarning(lcAccountManager) << "Migration result AccountManager::AccountsRestoreResult:" << accountsRestoreResult;
qCWarning(lcAccountManager) << "Accounts migrated:" << accountsMigrated;

return accountsMigrated;
}

bool AccountManager::confirmRestoreExistingAccounts() const

Check warning on line 647 in src/gui/accountmanager.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/accountmanager.cpp:647:22 [readability-convert-member-functions-to-static]

method 'confirmRestoreExistingAccounts' can be made static

Check warning on line 647 in src/gui/accountmanager.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/accountmanager.cpp:647:22 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
ConfigFile configFile;
const auto configFileToRestore = configFile.configFileToRestore();
auto oCSettings = std::make_unique<QSettings>(configFileToRestore, QSettings::IniFormat);
oCSettings->beginGroup(QLatin1String(accountsC));
const auto accountsListSize = oCSettings->childGroups().size();
oCSettings->endGroup();
if (!forceLegacyImport() && accountsListSize > 0
&& Theme::instance()->displayLegacyImportDialog()
&& !configFile.discoveredLegacyConfigFile().isEmpty()) {
const auto importQuestion = accountsListSize > 1
? tr("%1 accounts were detected from a legacy desktop client at %2.\n"
"Should the accounts be imported?").arg(QString::number(accountsListSize), configFile.discoveredLegacyConfigFile())
: tr("1 account was detected from a legacy desktop client at %1.\n"
"Should the account be imported?").arg(configFile.discoveredLegacyConfigFile());
const auto importMessageBox = new QMessageBox(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) {
QMessageBox::information(nullptr,
tr("Legacy import"),
tr("Could not import accounts from legacy client configuration."));
return false;
}
}

return true;
}

}
9 changes: 6 additions & 3 deletions src/gui/accountmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

#pragma once

#include "account.h"

Check failure on line 9 in src/gui/accountmanager.h

View workflow job for this annotation

GitHub Actions / build

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

'account.h' file not found
#include "accountstate.h"

class QSettings;

namespace OCC {

Check warning on line 14 in src/gui/accountmanager.h

View workflow job for this annotation

GitHub Actions / build

src/gui/accountmanager.h:14:11 [cppcoreguidelines-avoid-non-const-global-variables]

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

/**
@brief The AccountManager class
Expand Down Expand Up @@ -39,7 +41,7 @@
* Returns false if there was an error reading the settings,
* but note that settings not existing is not an error.
*/
AccountsRestoreResult restore(const bool alsoRestoreLegacySettings = true);
AccountsRestoreResult restore(const QString &legacyConfigFile, const bool alsoRestoreLegacySettings = true);

/**
* Add this account in the list of saved accounts.
Expand Down Expand Up @@ -82,6 +84,9 @@
*/
static void backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys);

bool confirmRestoreExistingAccounts() const;
int setupAccounts();

public slots:
/// Saves account data when adding user, when updating e.g. dav user, not including the credentials
void saveAccount(const OCC::AccountPtr &newAccountData);
Expand Down Expand Up @@ -113,8 +118,6 @@
void saveAccountHelper(const AccountPtr &account, QSettings &settings, bool saveCredentials = true);
AccountPtr loadAccountHelper(QSettings &settings);

bool restoreFromLegacySettings();

[[nodiscard]] bool isAccountIdAvailable(const QString &id) const;
[[nodiscard]] QString generateFreeAccountId() const;

Expand Down
Loading
Loading