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;
}
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"
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);
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) {
*/
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);
// ----------------------------------------------------------------------------------
+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)
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();
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;
#include <QMessageBox>
#include <QPushButton>
+static const char versionC[] = "version";
+
namespace OCC {
Q_LOGGING_CATEGORY(lcFolder, "nextcloud.gui.folder", QtInfoMsg)
}
settings->beginGroup(settingsGroup);
+ // Note: Each of these groups might have a "version" tag, but that's
+ // currently unused.
FolderDefinition::save(*settings, _definition);
settings->sync();
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())
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);
#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)
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
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.
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();
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;