diff --git a/src/lib/app/datapaths.cpp b/src/lib/app/datapaths.cpp index 6e4d5c01..d2efb346 100644 --- a/src/lib/app/datapaths.cpp +++ b/src/lib/app/datapaths.cpp @@ -1,161 +1,157 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2014-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "datapaths.h" #include "qztools.h" #include "../config.h" #include "mainapplication.h" #include #include #include #include Q_GLOBAL_STATIC(DataPaths, qz_data_paths) DataPaths::DataPaths() { init(); } DataPaths::~DataPaths() { } // static void DataPaths::setCurrentProfilePath(const QString &profilePath) { qz_data_paths()->initCurrentProfile(profilePath); } // static void DataPaths::setPortableVersion() { DataPaths* d = qz_data_paths(); const QString appDir = QCoreApplication::applicationDirPath(); d->m_paths[AppData] = QStringList{appDir}; d->m_paths[Config] = QStringList{appDir + QSL("/config")}; d->m_paths[Cache] = QStringList{appDir + QSL("/cache")}; d->m_paths[Profiles] = QStringList{appDir + QSL("/config/profiles")}; d->m_paths[Themes].clear(); d->m_paths[Plugins].clear(); d->initAssetsIn(appDir); - // Make sure the Config and Temp paths exists - QDir().mkpath(d->m_paths[Config].at(0)); + // Make sure Temp path exists QDir().mkpath(d->m_paths[Temp].at(0)); } // static QString DataPaths::path(DataPaths::Path path) { Q_ASSERT(!qz_data_paths()->m_paths[path].isEmpty()); return qz_data_paths()->m_paths[path].at(0); } // static QStringList DataPaths::allPaths(DataPaths::Path type) { Q_ASSERT(!qz_data_paths()->m_paths[type].isEmpty()); return qz_data_paths()->m_paths[type]; } // static QString DataPaths::locate(Path type, const QString &file) { const QStringList dirs = allPaths(type); for (const QString &dir : dirs) { const QString fullPath = QDir(dir).absoluteFilePath(file); if (QFileInfo::exists(fullPath)) { return fullPath; } } return QString(); } // static QString DataPaths::currentProfilePath() { return path(CurrentProfile); } void DataPaths::init() { m_paths[AppData].append(QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)); #ifdef FALKON_PLUGIN_PATH m_paths[Plugins].append(QStringLiteral(FALKON_PLUGIN_PATH)); #endif for (const QString &location : qAsConst(m_paths[AppData])) { initAssetsIn(location); } if (MainApplication::isTestModeEnabled()) { m_paths[Config].append(QDir::tempPath() + QSL("/Falkon-test")); } else { m_paths[Config].append(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); } m_paths[Profiles].append(m_paths[Config].at(0) + QLatin1String("/profiles")); // We also allow to load data from Config path initAssetsIn(m_paths[Config].at(0)); // If FALKON_PLUGIN_PATH is set, only load plugins from there const QByteArray pluginPath = qgetenv("FALKON_PLUGIN_PATH"); if (!pluginPath.isNull()) { m_paths[Plugins] = QStringList{QString::fromLocal8Bit(pluginPath)}; } m_tmpdir.reset(new QTemporaryDir()); m_paths[Temp].append(m_tmpdir->path()); if (!m_tmpdir->isValid()) { qWarning() << "Failed to create temporary directory" << m_tmpdir->path(); } m_paths[Cache].append(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - - // Make sure Config path exists - QDir().mkpath(m_paths[Config].at(0)); } void DataPaths::initCurrentProfile(const QString &profilePath) { m_paths[CurrentProfile].append(profilePath); if (m_paths[Cache].isEmpty()) m_paths[Cache].append(m_paths[CurrentProfile].at(0) + QLatin1String("/cache")); if (m_paths[Sessions].isEmpty()) m_paths[Sessions].append(m_paths[CurrentProfile].at(0) + QLatin1String("/sessions")); QDir dir; dir.mkpath(m_paths[Cache].at(0)); dir.mkpath(m_paths[Sessions].at(0)); } void DataPaths::initAssetsIn(const QString &path) { m_paths[Themes].append(path + QLatin1String("/themes")); m_paths[Plugins].append(path + QLatin1String("/plugins")); } diff --git a/src/lib/app/profilemanager.cpp b/src/lib/app/profilemanager.cpp index 11b1f95c..49c15886 100644 --- a/src/lib/app/profilemanager.cpp +++ b/src/lib/app/profilemanager.cpp @@ -1,277 +1,306 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "profilemanager.h" #include "mainapplication.h" #include "datapaths.h" #include "updater.h" #include "qztools.h" #include "sqldatabase.h" #include #include #include #include #include #include +#include + #include ProfileManager::ProfileManager() { } void ProfileManager::initConfigDir() { QDir dir(DataPaths::path(DataPaths::Config)); - if (dir.exists() && QFile(dir.filePath(QLatin1String("profiles/profiles.ini"))).exists()) { + if (!dir.exists()) { + migrateFromQupZilla(); + } + + if (QFileInfo::exists(dir.filePath(QLatin1String("profiles/profiles.ini")))) { return; } std::cout << "Falkon: Creating new profile directory" << std::endl; if (!dir.exists()) { dir.mkpath(dir.absolutePath()); } dir.mkdir(QLatin1String("profiles")); dir.cd(QLatin1String("profiles")); // $Config/profiles QFile(dir.filePath(QLatin1String("profiles.ini"))).remove(); QFile(QLatin1String(":data/profiles.ini")).copy(dir.filePath(QLatin1String("profiles.ini"))); QFile(dir.filePath(QLatin1String("profiles.ini"))).setPermissions(QFile::ReadUser | QFile::WriteUser); dir.mkdir(QLatin1String("default")); dir.cd(QLatin1String("default")); // $Config/profiles/default QFile(QLatin1String(":data/bookmarks.json")).copy(dir.filePath(QLatin1String("bookmarks.json"))); QFile(dir.filePath(QLatin1String("bookmarks.json"))).setPermissions(QFile::ReadUser | QFile::WriteUser); QFile versionFile(dir.filePath(QLatin1String("version"))); versionFile.open(QFile::WriteOnly); versionFile.write(Qz::VERSION); versionFile.close(); } void ProfileManager::initCurrentProfile(const QString &profileName) { QString profilePath = DataPaths::path(DataPaths::Profiles) + QLatin1Char('/'); if (profileName.isEmpty()) { profilePath.append(startingProfile()); } else { profilePath.append(profileName); } DataPaths::setCurrentProfilePath(profilePath); updateCurrentProfile(); connectDatabase(); } int ProfileManager::createProfile(const QString &profileName) { QDir dir(DataPaths::path(DataPaths::Profiles)); if (QDir(dir.absolutePath() + QLatin1Char('/') + profileName).exists()) { return -1; } if (!dir.mkdir(profileName)) { return -2; } dir.cd(profileName); QFile versionFile(dir.filePath(QLatin1String("version"))); versionFile.open(QFile::WriteOnly); versionFile.write(Qz::VERSION); versionFile.close(); return 0; } bool ProfileManager::removeProfile(const QString &profileName) { QDir dir(DataPaths::path(DataPaths::Profiles) + QLatin1Char('/') + profileName); if (!dir.exists()) { return false; } QzTools::removeRecursively(dir.absolutePath()); return true; } // static QString ProfileManager::currentProfile() { QString path = DataPaths::currentProfilePath(); return path.mid(path.lastIndexOf(QLatin1Char('/')) + 1); } // static QString ProfileManager::startingProfile() { QSettings settings(DataPaths::path(DataPaths::Profiles) + QLatin1String("/profiles.ini"), QSettings::IniFormat); return settings.value(QLatin1String("Profiles/startProfile"), QLatin1String("default")).toString(); } // static void ProfileManager::setStartingProfile(const QString &profileName) { QSettings settings(DataPaths::path(DataPaths::Profiles) + QLatin1String("/profiles.ini"), QSettings::IniFormat); settings.setValue(QLatin1String("Profiles/startProfile"), profileName); } // static QStringList ProfileManager::availableProfiles() { QDir dir(DataPaths::path(DataPaths::Profiles)); return dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); } void ProfileManager::updateCurrentProfile() { QDir profileDir(DataPaths::currentProfilePath()); if (!profileDir.exists()) { QDir newDir(profileDir.path().remove(profileDir.dirName())); newDir.mkdir(profileDir.dirName()); } QFile versionFile(profileDir.filePath(QLatin1String("version"))); // If file exists, just update the profile to current version if (versionFile.exists()) { versionFile.open(QFile::ReadOnly); QString profileVersion = versionFile.readAll(); versionFile.close(); updateProfile(Qz::VERSION, profileVersion.trimmed()); } else { copyDataToProfile(); } versionFile.open(QFile::WriteOnly); versionFile.write(Qz::VERSION); versionFile.close(); } void ProfileManager::updateProfile(const QString ¤t, const QString &profile) { if (current == profile) { return; } Updater::Version prof(profile); // Profile is from newer version than running application if (prof > Updater::Version(Qz::VERSION)) { // Only copy data when profile is not from development version if (prof.revisionNumber != 99) { copyDataToProfile(); } return; } if (prof < Updater::Version("1.9.0")) { std::cout << "Falkon: Using profile from QupZilla " << qPrintable(profile) << " is not supported!" << std::endl; return; } // No change in 2.0 if (prof < Updater::Version("2.9.99")) { return; } // Nothing for now } void ProfileManager::copyDataToProfile() { QDir profileDir(DataPaths::currentProfilePath()); QFile browseData(profileDir.filePath(QLatin1String("browsedata.db"))); if (browseData.exists()) { const QString browseDataBackup = QzTools::ensureUniqueFilename(profileDir.filePath(QLatin1String("browsedata-backup.db"))); browseData.copy(browseDataBackup); browseData.remove(); QFile settings(profileDir.filePath(QSL("settings.ini"))); if (settings.exists()) { const QString settingsBackup = QzTools::ensureUniqueFilename(profileDir.filePath(QSL("settings-backup.ini"))); settings.copy(settingsBackup); settings.remove(); } QFile sessionFile(profileDir.filePath(QSL("session.dat"))); if (sessionFile.exists()) { QString oldVersion = QzTools::readAllFileContents(profileDir.filePath(QSL("version"))).trimmed(); if (oldVersion.isEmpty()) { oldVersion = QSL("unknown-version"); } const QString sessionBackup = QzTools::ensureUniqueFilename(profileDir.filePath(QSL("sessions/backup-%1.dat").arg(oldVersion))); sessionFile.copy(sessionBackup); sessionFile.remove(); } const QString text = "Incompatible profile version has been detected. To avoid losing your profile data, they were " "backed up in following file:

" + browseDataBackup + "
"; QMessageBox::warning(0, "Falkon: Incompatible profile version", text); } } +void ProfileManager::migrateFromQupZilla() +{ + if (mApp->isPortable()) { + return; + } + +#if defined(Q_OS_WIN) + const QString qzConfig = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QL1S("/qupzilla"); +#elif defined(Q_OS_MACOS) + const QString qzConfig = QDir::homePath() + QLatin1String("/Library/Application Support/QupZilla"); +#else // Unix + const QString qzConfig = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QL1S("/qupzilla"); +#endif + + if (!QFileInfo::exists(qzConfig)) { + return; + } + + std::cout << "Falkon: Migrating config from QupZilla..." << std::endl; + + QzTools::copyRecursively(qzConfig, DataPaths::path(DataPaths::Config)); +} + void ProfileManager::connectDatabase() { QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE")); if (!db.isValid()) { qCritical() << "Qt sqlite database driver is missing! Continuing without database...."; return; } if (mApp->isPrivate()) { db.setConnectOptions("QSQLITE_OPEN_READONLY"); } db.setDatabaseName(DataPaths::currentProfilePath() + QLatin1String("/browsedata.db")); if (!db.open()) { qCritical() << "Cannot open SQLite database! Continuing without database...."; return; } if (db.tables().isEmpty()) { const QStringList statements = QzTools::readAllFileContents(QSL(":/data/browsedata.sql")).split(QL1C(';')); for (const QString &statement : statements) { const QString stmt = statement.trimmed(); if (stmt.isEmpty()) { continue; } QSqlQuery query; if (!query.exec(stmt)) { qCritical() << "Error creating database schema" << query.lastError().text(); } } } SqlDatabase::instance()->setDatabase(db); } diff --git a/src/lib/app/profilemanager.h b/src/lib/app/profilemanager.h index efd6f973..74e26653 100644 --- a/src/lib/app/profilemanager.h +++ b/src/lib/app/profilemanager.h @@ -1,58 +1,59 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2014 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef PROFILEMANAGER_H #define PROFILEMANAGER_H #include #include "qzcommon.h" class ProfileManager { public: explicit ProfileManager(); // Make sure the config dir exists and have correct structure void initConfigDir(); // Set current profile name (from profiles.ini) and ensure dir exists with correct structure void initCurrentProfile(const QString &profileName); // Return 0 on success, -1 profile already exists, -2 cannot create directory static int createProfile(const QString &profileName); // Return false on error (profile does not exists) static bool removeProfile(const QString &profileName); // Name of current profile static QString currentProfile(); // Name of starting profile static QString startingProfile(); static void setStartingProfile(const QString &profileName); // Names of available profiles static QStringList availableProfiles(); private: void updateCurrentProfile(); void updateProfile(const QString ¤t, const QString &profile); void copyDataToProfile(); + void migrateFromQupZilla(); void connectDatabase(); }; #endif // PROFILEMANAGER_H