File indexing completed on 2024-05-12 04:57:54
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "profilemanager.h" 0019 #include "mainapplication.h" 0020 #include "datapaths.h" 0021 #include "updater.h" 0022 #include "qztools.h" 0023 #include "sqldatabase.h" 0024 0025 #include <QDir> 0026 #include <QSqlError> 0027 #include <QSqlQuery> 0028 #include <QSqlDatabase> 0029 #include <QMessageBox> 0030 #include <QSettings> 0031 #include <QStandardPaths> 0032 0033 #include <iostream> 0034 0035 ProfileManager::ProfileManager() 0036 = default; 0037 0038 void ProfileManager::initConfigDir() 0039 { 0040 QDir dir(DataPaths::path(DataPaths::Config)); 0041 0042 if (!dir.exists()) { 0043 migrateFromQupZilla(); 0044 } 0045 0046 if (QFileInfo::exists(dir.filePath(QStringLiteral("profiles/profiles.ini")))) { 0047 return; 0048 } 0049 0050 std::cout << "Falkon: Creating new profile directory" << std::endl; 0051 0052 if (!dir.exists()) { 0053 dir.mkpath(dir.absolutePath()); 0054 } 0055 0056 dir.mkdir(QStringLiteral("profiles")); 0057 dir.cd(QStringLiteral("profiles")); 0058 0059 // $Config/profiles 0060 QFile(dir.filePath(QStringLiteral("profiles.ini"))).remove(); 0061 QFile(QStringLiteral(":data/profiles.ini")).copy(dir.filePath(QStringLiteral("profiles.ini"))); 0062 QFile(dir.filePath(QStringLiteral("profiles.ini"))).setPermissions(QFile::ReadUser | QFile::WriteUser); 0063 0064 dir.mkdir(QStringLiteral("default")); 0065 dir.cd(QStringLiteral("default")); 0066 0067 // $Config/profiles/default 0068 QFile(QStringLiteral(":data/bookmarks.json")).copy(dir.filePath(QStringLiteral("bookmarks.json"))); 0069 QFile(dir.filePath(QStringLiteral("bookmarks.json"))).setPermissions(QFile::ReadUser | QFile::WriteUser); 0070 0071 QFile versionFile(dir.filePath(QStringLiteral("version"))); 0072 versionFile.open(QFile::WriteOnly); 0073 versionFile.write(Qz::VERSION); 0074 versionFile.close(); 0075 } 0076 0077 void ProfileManager::initCurrentProfile(const QString &profileName) 0078 { 0079 QString profilePath = DataPaths::path(DataPaths::Profiles) + QLatin1Char('/'); 0080 0081 if (profileName.isEmpty()) { 0082 profilePath.append(startingProfile()); 0083 } 0084 else { 0085 profilePath.append(profileName); 0086 } 0087 0088 DataPaths::setCurrentProfilePath(profilePath); 0089 0090 updateCurrentProfile(); 0091 connectDatabase(); 0092 } 0093 0094 int ProfileManager::createProfile(const QString &profileName) 0095 { 0096 QDir dir(DataPaths::path(DataPaths::Profiles)); 0097 0098 if (QDir(dir.absolutePath() + QLatin1Char('/') + profileName).exists()) { 0099 return -1; 0100 } 0101 if (!dir.mkdir(profileName)) { 0102 return -2; 0103 } 0104 0105 dir.cd(profileName); 0106 0107 QFile versionFile(dir.filePath(QStringLiteral("version"))); 0108 versionFile.open(QFile::WriteOnly); 0109 versionFile.write(Qz::VERSION); 0110 versionFile.close(); 0111 0112 return 0; 0113 } 0114 0115 bool ProfileManager::removeProfile(const QString &profileName) 0116 { 0117 QDir dir(DataPaths::path(DataPaths::Profiles) + QLatin1Char('/') + profileName); 0118 0119 if (!dir.exists()) { 0120 return false; 0121 } 0122 0123 QzTools::removeRecursively(dir.absolutePath()); 0124 return true; 0125 } 0126 0127 // static 0128 QString ProfileManager::currentProfile() 0129 { 0130 QString path = DataPaths::currentProfilePath(); 0131 return path.mid(path.lastIndexOf(QLatin1Char('/')) + 1); 0132 } 0133 0134 // static 0135 QString ProfileManager::startingProfile() 0136 { 0137 QSettings settings(DataPaths::path(DataPaths::Profiles) + QLatin1String("/profiles.ini"), QSettings::IniFormat); 0138 return settings.value(QStringLiteral("Profiles/startProfile"), QLatin1String("default")).toString(); 0139 } 0140 0141 // static 0142 void ProfileManager::setStartingProfile(const QString &profileName) 0143 { 0144 QSettings settings(DataPaths::path(DataPaths::Profiles) + QLatin1String("/profiles.ini"), QSettings::IniFormat); 0145 settings.setValue(QStringLiteral("Profiles/startProfile"), profileName); 0146 } 0147 0148 // static 0149 QStringList ProfileManager::availableProfiles() 0150 { 0151 QDir dir(DataPaths::path(DataPaths::Profiles)); 0152 return dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 0153 } 0154 0155 void ProfileManager::updateCurrentProfile() 0156 { 0157 QDir profileDir(DataPaths::currentProfilePath()); 0158 0159 if (!profileDir.exists()) { 0160 QDir newDir(profileDir.path().remove(profileDir.dirName())); 0161 newDir.mkdir(profileDir.dirName()); 0162 } 0163 0164 QFile versionFile(profileDir.filePath(QStringLiteral("version"))); 0165 0166 // If file exists, just update the profile to current version 0167 if (versionFile.exists()) { 0168 versionFile.open(QFile::ReadOnly); 0169 QString profileVersion = QString::fromUtf8(versionFile.readAll()); 0170 versionFile.close(); 0171 0172 updateProfile(QString::fromLatin1(Qz::VERSION), profileVersion.trimmed()); 0173 } 0174 else { 0175 copyDataToProfile(); 0176 } 0177 0178 versionFile.open(QFile::WriteOnly); 0179 versionFile.write(Qz::VERSION); 0180 versionFile.close(); 0181 } 0182 0183 void ProfileManager::updateProfile(const QString ¤t, const QString &profile) 0184 { 0185 if (current == profile) { 0186 return; 0187 } 0188 0189 Updater::Version prof(profile); 0190 0191 // Profile is from newer version than running application 0192 if (prof > Updater::Version(QString::fromLatin1(Qz::VERSION))) { 0193 // Only copy data when profile is not from development version 0194 if (prof.revisionNumber != 99) { 0195 copyDataToProfile(); 0196 } 0197 return; 0198 } 0199 0200 if (prof < Updater::Version(QStringLiteral("1.9.0"))) { 0201 std::cout << "Falkon: Using profile from QupZilla " << qPrintable(profile) << " is not supported!" << std::endl; 0202 return; 0203 } 0204 0205 // No change in 2.0 0206 if (prof < Updater::Version(QStringLiteral("2.9.99"))) { 0207 return; 0208 } 0209 0210 // No change in 3.0 0211 if (prof < Updater::Version(QStringLiteral("3.0.99"))) { 0212 return; 0213 } 0214 0215 // No change in 3.1 0216 if (prof < Updater::Version(QStringLiteral("3.1.99"))) { 0217 return; 0218 } 0219 } 0220 0221 void ProfileManager::copyDataToProfile() 0222 { 0223 QDir profileDir(DataPaths::currentProfilePath()); 0224 0225 QFile browseData(profileDir.filePath(QStringLiteral("browsedata.db"))); 0226 0227 if (browseData.exists()) { 0228 const QString browseDataBackup = QzTools::ensureUniqueFilename(profileDir.filePath(QStringLiteral("browsedata-backup.db"))); 0229 browseData.copy(browseDataBackup); 0230 browseData.remove(); 0231 0232 QFile settings(profileDir.filePath(QSL("settings.ini"))); 0233 if (settings.exists()) { 0234 const QString settingsBackup = QzTools::ensureUniqueFilename(profileDir.filePath(QSL("settings-backup.ini"))); 0235 settings.copy(settingsBackup); 0236 settings.remove(); 0237 } 0238 0239 QFile sessionFile(profileDir.filePath(QSL("session.dat"))); 0240 if (sessionFile.exists()) { 0241 QString oldVersion = QzTools::readAllFileContents(profileDir.filePath(QSL("version"))).trimmed(); 0242 if (oldVersion.isEmpty()) { 0243 oldVersion = QSL("unknown-version"); 0244 } 0245 const QString sessionBackup = QzTools::ensureUniqueFilename(profileDir.filePath(QSL("sessions/backup-%1.dat").arg(oldVersion))); 0246 sessionFile.copy(sessionBackup); 0247 sessionFile.remove(); 0248 } 0249 0250 const QString text = QSL("Incompatible profile version has been detected. To avoid losing your profile data, they were " 0251 "backed up in following file:<br/><br/><b>") + browseDataBackup + QSL("<br/></b>"); 0252 QMessageBox::warning(nullptr, QStringLiteral("Falkon: Incompatible profile version"), text); 0253 } 0254 } 0255 0256 void ProfileManager::migrateFromQupZilla() 0257 { 0258 if (mApp->isPortable()) { 0259 return; 0260 } 0261 0262 #if defined(Q_OS_WIN) 0263 const QString qzConfig = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QL1S("/qupzilla"); 0264 #elif defined(Q_OS_MACOS) 0265 const QString qzConfig = QDir::homePath() + QLatin1String("/Library/Application Support/QupZilla"); 0266 #else // Unix 0267 const QString qzConfig = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QL1S("/qupzilla"); 0268 #endif 0269 0270 if (!QFileInfo::exists(qzConfig)) { 0271 return; 0272 } 0273 0274 std::cout << "Falkon: Migrating config from QupZilla..." << std::endl; 0275 0276 QzTools::copyRecursively(qzConfig, DataPaths::path(DataPaths::Config)); 0277 } 0278 0279 void ProfileManager::connectDatabase() 0280 { 0281 QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")); 0282 if (!db.isValid()) { 0283 qCritical() << "Qt sqlite database driver is missing! Continuing without database...."; 0284 return; 0285 } 0286 0287 if (mApp->isPrivate()) { 0288 db.setConnectOptions(QStringLiteral("QSQLITE_OPEN_READONLY")); 0289 } 0290 0291 db.setDatabaseName(DataPaths::currentProfilePath() + QLatin1String("/browsedata.db")); 0292 0293 if (!db.open()) { 0294 qCritical() << "Cannot open SQLite database! Continuing without database...."; 0295 return; 0296 } 0297 0298 if (db.tables().isEmpty()) { 0299 const QStringList statements = QzTools::readAllFileContents(QSL(":/data/browsedata.sql")).split(QL1C(';')); 0300 for (const QString &statement : statements) { 0301 const QString stmt = statement.trimmed(); 0302 if (stmt.isEmpty()) { 0303 continue; 0304 } 0305 QSqlQuery query; 0306 if (!query.exec(stmt)) { 0307 qCritical() << "Error creating database schema" << query.lastError().text(); 0308 } 0309 } 0310 } 0311 0312 SqlDatabase::instance()->setDatabase(db); 0313 }