Config: Add version flags to accounts and folders

Also, if there is too-new configuration, backup the file, show a
warning message asking the user whether it's ok to discard the
configuration from the future.

See #6504
This commit is contained in:
Christian Kamm 2018-05-02 15:40:54 +02:00 committed by Kevin Ottens
parent aa6f5f59c4
commit 87ba4e6b9c
No known key found for this signature in database
GPG key ID: 074BBBCB8DECC9E2
10 changed files with 160 additions and 2 deletions

View file

@ -36,6 +36,10 @@ static const char caCertsKeyC[] = "CaCertificates";
static const char accountsC[] = "Accounts";
static const char versionC[] = "version";
static const char serverVersionC[] = "serverVersion";
// The maximum versions that this client can read
static const int maxAccountsVersion = 2;
static const int maxAccountVersion = 1;
}
@ -79,6 +83,27 @@ bool AccountManager::restore()
return true;
}
QStringList AccountManager::backwardMigrationKeys()
{
auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
QStringList badKeys;
const int accountsVersion = settings->value(QLatin1String(versionC)).toInt();
if (accountsVersion <= maxAccountsVersion) {
foreach (const auto &accountId, settings->childGroups()) {
settings->beginGroup(accountId);
const int accountVersion = settings->value(QLatin1String(versionC), 1).toInt();
if (accountVersion > maxAccountVersion) {
badKeys.append(settings->group());
}
settings->endGroup();
}
} else {
badKeys.append(settings->group());
}
return badKeys;
}
bool AccountManager::restoreFromLegacySettings()
{
qCInfo(lcAccountManager) << "Migrate: restoreFromLegacySettings, checking settings group"
@ -139,7 +164,7 @@ bool AccountManager::restoreFromLegacySettings()
void AccountManager::save(bool saveCredentials)
{
auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
settings->setValue(QLatin1String(versionC), 2);
settings->setValue(QLatin1String(versionC), maxAccountsVersion);
for (const auto &acc : qAsConst(_accounts)) {
settings->beginGroup(acc->account()->id());
saveAccountHelper(acc->account().data(), *settings, saveCredentials);
@ -177,6 +202,7 @@ void AccountManager::saveAccountState(AccountState *a)
void AccountManager::saveAccountHelper(Account *acc, QSettings &settings, bool saveCredentials)
{
settings.setValue(QLatin1String(versionC), maxAccountVersion);
settings.setValue(QLatin1String(urlC), acc->_url.toString());
settings.setValue(QLatin1String(serverVersionC), acc->_serverVersion);
if (acc->_credentials) {

View file

@ -76,6 +76,12 @@ public:
*/
static AccountPtr createAccount();
/**
* Returns the list of settings keys that can't be read because
* they are from the future.
*/
static QStringList backwardMigrationKeys();
private:
// saving and loading Account to settings
void saveAccountHelper(Account *account, QSettings &settings, bool saveCredentials = true);

View file

@ -102,6 +102,50 @@ namespace {
// ----------------------------------------------------------------------------------
bool Application::configBackwardMigration()
{
auto accountKeys = AccountManager::backwardMigrationKeys();
auto folderKeys = FolderMan::backwardMigrationKeys();
bool containsFutureData = !accountKeys.isEmpty() || !folderKeys.isEmpty();
// Deal with unreadable accounts
if (!containsFutureData)
return true;
const auto backupFile = ConfigFile().backup();
QMessageBox box(
QMessageBox::Warning,
APPLICATION_SHORTNAME,
tr("Some settings were configured in newer versions of this client and "
"use features that are not available in this version.<br>"
"<br>"
"<b>Continuing will mean losing these settings.</b><br>"
"<br>"
"The current configuration file was already backed up to <i>%1</i>.")
.arg(backupFile));
box.addButton(tr("Quit"), QMessageBox::AcceptRole);
auto continueBtn = box.addButton(tr("Continue"), QMessageBox::DestructiveRole);
box.exec();
if (box.clickedButton() != continueBtn) {
QTimer::singleShot(0, qApp, SLOT(quit()));
return false;
}
auto settings = ConfigFile::settingsWithGroup("foo");
settings->endGroup();
// Wipe the keys from the future
for (const auto &badKey : accountKeys)
settings->remove(badKey);
for (const auto &badKey : folderKeys)
settings->remove(badKey);
return true;
}
Application::Application(int &argc, char **argv)
: SharedTools::QtSingleApplication(Theme::instance()->appName(), argc, argv)
, _gui(nullptr)
@ -187,8 +231,12 @@ Application::Application(int &argc, char **argv)
setupLogging();
setupTranslations();
// The timeout is initialized with an environment variable, if not, override with the value from the config
if (!configBackwardMigration()) {
return;
}
ConfigFile cfg;
// The timeout is initialized with an environment variable, if not, override with the value from the config
if (!AbstractNetworkJob::httpTimeout)
AbstractNetworkJob::httpTimeout = cfg.timeout();

View file

@ -103,6 +103,12 @@ protected slots:
private:
void setHelp();
/**
* Maybe a newer version of the client was used with this config file:
* if so, backup, confirm with user and remove the config that can't be read.
*/
bool configBackwardMigration();
QPointer<ownCloudGui> _gui;
Theme *_theme;

View file

@ -42,6 +42,8 @@
#include <QMessageBox>
#include <QPushButton>
static const char versionC[] = "version";
namespace OCC {
Q_LOGGING_CATEGORY(lcFolder, "nextcloud.gui.folder", QtInfoMsg)
@ -571,6 +573,8 @@ void Folder::saveToSettings() const
}
settings->beginGroup(settingsGroup);
// Note: Each of these groups might have a "version" tag, but that's
// currently unused.
FolderDefinition::save(*settings, _definition);
settings->sync();
@ -1127,6 +1131,7 @@ void FolderDefinition::save(QSettings &settings, const FolderDefinition &folder)
settings.setValue(QLatin1String("paused"), folder.paused);
settings.setValue(QLatin1String("ignoreHiddenFiles"), folder.ignoreHiddenFiles);
settings.setValue(QLatin1String("usePlaceholders"), folder.useVirtualFiles);
settings.setValue(QLatin1String(versionC), maxSettingsVersion());
// Happens only on Windows when the explorer integration is enabled.
if (!folder.navigationPaneClsid.isNull())

View file

@ -72,6 +72,9 @@ public:
static bool load(QSettings &settings, const QString &alias,
FolderDefinition *folder);
/// The highest version in the settings that load() can read
static int maxSettingsVersion() { return 1; }
/// Ensure / as separator and trailing /.
static QString prepareLocalPath(const QString &path);

View file

@ -39,6 +39,9 @@
#include <QSet>
#include <QNetworkProxy>
static const char versionC[] = "version";
static const int maxFoldersVersion = 1;
namespace OCC {
Q_LOGGING_CATEGORY(lcFolderMan, "nextcloud.gui.folder.manager", QtInfoMsg)
@ -303,6 +306,39 @@ int FolderMan::setupFoldersMigration()
return _folderMap.size();
}
QStringList FolderMan::backwardMigrationKeys()
{
QStringList badKeys;
auto settings = ConfigFile::settingsWithGroup(QLatin1String("Accounts"));
auto processSubgroup = [&](const QString &name) {
settings->beginGroup(name);
const int foldersVersion = settings->value(QLatin1String(versionC), 1).toInt();
if (foldersVersion <= maxFoldersVersion) {
foreach (const auto &folderAlias, settings->childGroups()) {
settings->beginGroup(folderAlias);
const int folderVersion = settings->value(QLatin1String(versionC), 1).toInt();
if (folderVersion > FolderDefinition::maxSettingsVersion()) {
badKeys.append(settings->group());
}
settings->endGroup();
}
} else {
badKeys.append(settings->group());
}
settings->endGroup();
};
for (const auto &accountId : settings->childGroups()) {
settings->beginGroup(accountId);
processSubgroup("Folders");
processSubgroup("Multifolders");
processSubgroup("FoldersWithPlaceholders");
settings->endGroup();
}
return badKeys;
}
bool FolderMan::ensureJournalGone(const QString &journalDbFile)
{
// remove the old journal file

View file

@ -68,6 +68,12 @@ public:
int setupFolders();
int setupFoldersMigration();
/**
* Returns a list of keys that can't be read because they are from
* future versions.
*/
static QStringList backwardMigrationKeys();
OCC::Folder::Map map();
/** Adds a folder for an account, ensures the journal is gone and saves it in the settings.

View file

@ -411,6 +411,21 @@ QString ConfigFile::excludeFileFromSystem()
return fi.absoluteFilePath();
}
QString ConfigFile::backup() const
{
QString baseFile = configFile();
QString backupFile = QString("%1.backup_%2").arg(baseFile, QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"));
// If this exact file already exists it's most likely that a backup was
// already done. (two backup calls directly after each other, potentially
// even with source alterations in between!)
if (!QFile::exists(backupFile)) {
QFile f(baseFile);
f.copy(backupFile);
}
return backupFile;
}
QString ConfigFile::configFile() const
{
return configPath() + Theme::instance()->configFileName();

View file

@ -48,6 +48,13 @@ public:
QString excludeFile(Scope scope) const;
static QString excludeFileFromSystem(); // doesn't access config dir
/**
* Creates a backup of the file
*
* Returns the path of the new backup.
*/
QString backup() const;
bool exists();
QString defaultConnection() const;