bool AccountManager::restore()
{
+ QStringList skipSettingsKeys;
+ backwardMigrationSettingsKeys(&skipSettingsKeys, &skipSettingsKeys);
+
auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
if (settings->status() != QSettings::NoError || !settings->isWritable()) {
qCWarning(lcAccountManager) << "Could not read settings from" << settings->fileName()
return false;
}
+ if (skipSettingsKeys.contains(settings->group())) {
+ // Should not happen: bad container keys should have been deleted
+ qCWarning(lcAccountManager) << "Accounts structure is too new, ignoring";
+ return true;
+ }
+
// If there are no accounts, check the old format.
if (settings->childGroups().isEmpty()
&& !settings->contains(QLatin1String(versionC))) {
for (const auto &accountId : settings->childGroups()) {
settings->beginGroup(accountId);
- if (auto acc = loadAccountHelper(*settings)) {
- acc->_id = accountId;
- if (auto accState = AccountState::loadFromSettings(acc, *settings)) {
- addAccountState(accState);
+ if (!skipSettingsKeys.contains(settings->group())) {
+ if (auto acc = loadAccountHelper(*settings)) {
+ acc->_id = accountId;
+ if (auto accState = AccountState::loadFromSettings(acc, *settings)) {
+ addAccountState(accState);
+ }
}
+ } else {
+ qCInfo(lcAccountManager) << "Account" << accountId << "is too new, ignoring";
+ _additionalBlockedAccountIds.insert(accountId);
}
settings->endGroup();
}
return true;
}
-QStringList AccountManager::backwardMigrationKeys()
+void AccountManager::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys)
{
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());
+ ignoreKeys->append(settings->group());
}
settings->endGroup();
}
} else {
- badKeys.append(settings->group());
+ deleteKeys->append(settings->group());
}
- return badKeys;
}
bool AccountManager::restoreFromLegacySettings()
bool AccountManager::isAccountIdAvailable(const QString &id) const
{
+ if (_additionalBlockedAccountIds.contains(id))
+ return false;
+
return std::none_of(_accounts.cbegin(), _accounts.cend(), [id](const auto &acc) {
return acc->account()->id() == id;
});
* Returns the list of settings keys that can't be read because
* they are from the future.
*/
- static QStringList backwardMigrationKeys();
+ static void backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys);
private:
// saving and loading Account to settings
AccountManager() = default;
QList<AccountStatePtr> _accounts;
+ /// Account ids from settings that weren't read
+ QSet<QString> _additionalBlockedAccountIds;
public slots:
/// Saves account data, not including the credentials
// ----------------------------------------------------------------------------------
-bool Application::configBackwardMigration()
+bool Application::configVersionMigration()
{
- auto accountKeys = AccountManager::backwardMigrationKeys();
- auto folderKeys = FolderMan::backwardMigrationKeys();
+ QStringList deleteKeys, ignoreKeys;
+ AccountManager::backwardMigrationSettingsKeys(&deleteKeys, &ignoreKeys);
+ FolderMan::backwardMigrationSettingsKeys(&deleteKeys, &ignoreKeys);
- bool containsFutureData = !accountKeys.isEmpty() || !folderKeys.isEmpty();
+ ConfigFile configFile;
- // Deal with unreadable accounts
- if (!containsFutureData)
+ // Did the client version change?
+ // (The client version is adjusted further down)
+ bool versionChanged = configFile.clientVersionString() != MIRALL_VERSION_STRING;
+
+ // We want to message the user either for destructive changes,
+ // or if we're ignoring something and the client version changed.
+ bool warningMessage = !deleteKeys.isEmpty() || (!ignoreKeys.isEmpty() && versionChanged);
+
+ if (!versionChanged && !warningMessage)
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;
- }
+ const auto backupFile = configFile.backup();
+
+ if (warningMessage) {
+ QString boldMessage;
+ if (!deleteKeys.isEmpty()) {
+ boldMessage = tr("Continuing will mean <b>deleting these settings</b>.");
+ } else {
+ boldMessage = tr("Continuing will mean <b>ignoring these settings</b>.");
+ }
- auto settings = ConfigFile::settingsWithGroup("foo");
- settings->endGroup();
+ 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>"
+ "%1<br>"
+ "<br>"
+ "The current configuration file was already backed up to <i>%2</i>.")
+ .arg(boldMessage, 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;
+ }
- // Wipe the keys from the future
- for (const auto &badKey : accountKeys)
- settings->remove(badKey);
- for (const auto &badKey : folderKeys)
- settings->remove(badKey);
+ auto settings = ConfigFile::settingsWithGroup("foo");
+ settings->endGroup();
+
+ // Wipe confusing keys from the future, ignore the others
+ for (const auto &badKey : deleteKeys)
+ settings->remove(badKey);
+ }
+ configFile.setClientVersionString(MIRALL_VERSION_STRING);
return true;
}
setupLogging();
setupTranslations();
- if (!configBackwardMigration()) {
+ if (!configVersionMigration()) {
return;
}
* 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();
+ bool configVersionMigration();
QPointer<ownCloudGui> _gui;
{
unloadAndDeleteAllFolders();
+ QStringList skipSettingsKeys;
+ backwardMigrationSettingsKeys(&skipSettingsKeys, &skipSettingsKeys);
+
auto settings = ConfigFile::settingsWithGroup(QLatin1String("Accounts"));
const auto accountsWithSettings = settings->childGroups();
if (accountsWithSettings.isEmpty()) {
}
settings->beginGroup(id);
- settings->beginGroup(QLatin1String("Folders"));
- setupFoldersHelper(*settings, account, true);
- settings->endGroup();
+ // The "backwardsCompatible" flag here is related to migrating old
+ // database locations
+ auto process = [&](const QString &groupName, bool backwardsCompatible = false) {
+ settings->beginGroup(groupName);
+ if (skipSettingsKeys.contains(settings->group())) {
+ // Should not happen: bad container keys should have been deleted
+ qCWarning(lcFolderMan) << "Folder structure" << groupName << "is too new, ignoring";
+ } else {
+ setupFoldersHelper(*settings, account, backwardsCompatible, skipSettingsKeys);
+ }
+ settings->endGroup();
+ };
- // See Folder::saveToSettings for details about why this exists.
- settings->beginGroup(QLatin1String("Multifolders"));
- setupFoldersHelper(*settings, account, false);
- settings->endGroup();
+ process(QStringLiteral("Folders"), true);
- // See Folder::saveToSettings for details about why this exists.
- settings->beginGroup(QLatin1String("FoldersWithPlaceholders"));
- setupFoldersHelper(*settings, account, false);
- settings->endGroup();
+ // See Folder::saveToSettings for details about why these exists.
+ process(QStringLiteral("Multifolders"));
+ process(QStringLiteral("FoldersWithPlaceholders"));
settings->endGroup(); // <account>
}
return _folderMap.size();
}
-void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible)
+void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible, const QStringList &ignoreKeys)
{
for (const auto &folderAlias : settings.childGroups()) {
+ // Skip folders with too-new version
+ settings.beginGroup(folderAlias);
+ if (ignoreKeys.contains(settings.group())) {
+ qCInfo(lcFolderMan) << "Folder" << folderAlias << "is too new, ignoring";
+ _additionalBlockedFolderAliases.insert(folderAlias);
+ settings.endGroup();
+ continue;
+ }
+ settings.endGroup();
+
FolderDefinition folderDefinition;
if (FolderDefinition::load(settings, folderAlias, &folderDefinition)) {
auto defaultJournalPath = folderDefinition.defaultJournalPath(account->account());
return _folderMap.size();
}
-QStringList FolderMan::backwardMigrationKeys()
+void FolderMan::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys)
{
- QStringList badKeys;
auto settings = ConfigFile::settingsWithGroup(QLatin1String("Accounts"));
auto processSubgroup = [&](const QString &name) {
settings->beginGroup(folderAlias);
const int folderVersion = settings->value(QLatin1String(versionC), 1).toInt();
if (folderVersion > FolderDefinition::maxSettingsVersion()) {
- badKeys.append(settings->group());
+ ignoreKeys->append(settings->group());
}
settings->endGroup();
}
} else {
- badKeys.append(settings->group());
+ deleteKeys->append(settings->group());
}
settings->endGroup();
};
processSubgroup("FoldersWithPlaceholders");
settings->endGroup();
}
- return badKeys;
}
bool FolderMan::ensureJournalGone(const QString &journalDbFile)
{
auto alias = folderDefinition.alias;
int count = 0;
- while (folderDefinition.alias.isEmpty() || _folderMap.contains(folderDefinition.alias)) {
+ while (folderDefinition.alias.isEmpty()
+ || _folderMap.contains(folderDefinition.alias)
+ || _additionalBlockedFolderAliases.contains(folderDefinition.alias)) {
// There is already a folder configured with this name and folder names need to be unique
folderDefinition.alias = alias + QString::number(++count);
}
* Returns a list of keys that can't be read because they are from
* future versions.
*/
- static QStringList backwardMigrationKeys();
+ static void backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys);
OCC::Folder::Map map();
// restarts the application (Linux only)
void restartApplication();
- void setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible);
+ void setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible, const QStringList &ignoreKeys);
QSet<Folder *> _disabledFolders;
Folder::Map _folderMap;
QPointer<Folder> _lastSyncFolder;
bool _syncEnabled = true;
+ /// Folder aliases from the settings that weren't read
+ QSet<QString> _additionalBlockedFolderAliases;
+
/// Starts regular etag query jobs
QTimer _etagPollTimer;
/// The currently running etag query
#include "configfile.h"
#include "theme.h"
+#include "version.h"
#include "common/utility.h"
#include "common/asserts.h"
#include "version.h"
static const char logExpireC[] = "logExpire";
static const char logFlushC[] = "logFlush";
static const char showExperimentalOptionsC[] = "showExperimentalOptions";
+static const char clientVersionC[] = "clientVersion";
static const char proxyHostC[] = "Proxy/host";
static const char proxyTypeC[] = "Proxy/type";
QString ConfigFile::backup() const
{
QString baseFile = configFile();
- QString backupFile = QString("%1.backup_%2").arg(baseFile, QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"));
+ auto versionString = clientVersionString();
+ if (!versionString.isEmpty())
+ versionString.prepend('_');
+ QString backupFile =
+ QString("%1.backup_%2%3")
+ .arg(baseFile)
+ .arg(QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"))
+ .arg(versionString);
// If this exact file already exists it's most likely that a backup was
// already done. (two backup calls directly after each other, potentially
settings.sync();
}
+QString ConfigFile::clientVersionString() const
+{
+ QSettings settings(configFile(), QSettings::IniFormat);
+ return settings.value(QLatin1String(clientVersionC), QString()).toString();
+}
+
+void ConfigFile::setClientVersionString(const QString &version)
+{
+ QSettings settings(configFile(), QSettings::IniFormat);
+ settings.setValue(QLatin1String(clientVersionC), version);
+}
+
Q_GLOBAL_STATIC(QString, g_configFileName)
std::unique_ptr<QSettings> ConfigFile::settingsWithGroup(const QString &group, QObject *parent)
QString certificatePasswd() const;
void setCertificatePasswd(const QString &cPasswd);
+ /** The client version that last used this settings file.
+ Updated by configVersionMigration() at client startup. */
+ QString clientVersionString() const;
+ void setClientVersionString(const QString &version);
+
/** Returns a new settings pre-set in a specific group. The Settings will be created
with the given parent. If no parent is specified, the caller must destroy the settings */
static std::unique_ptr<QSettings> settingsWithGroup(const QString &group, QObject *parent = nullptr);