1
0
mirror of https://github.com/d47081/qBittorrent.git synced 2025-02-11 14:24:23 +00:00

Merge pull request #5214 from evsh/adjustable-config-name

Implement configurations and portable mode. Closes #465
This commit is contained in:
sledgehammer999 2017-04-12 02:20:34 +03:00 committed by GitHub
commit 07af8c9648
26 changed files with 1228 additions and 439 deletions

View File

@ -4,11 +4,13 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(QBT_APP_HEADERS set(QBT_APP_HEADERS
application.h application.h
filelogger.h filelogger.h
options.h
) )
set(QBT_APP_SOURCES set(QBT_APP_SOURCES
application.cpp application.cpp
filelogger.cpp filelogger.cpp
options.cpp
main.cpp main.cpp
) )

View File

@ -16,11 +16,13 @@ usesystemqtsingleapplication {
HEADERS += \ HEADERS += \
$$PWD/application.h \ $$PWD/application.h \
$$PWD/filelogger.h $$PWD/filelogger.h \
$$PWD/options.h
SOURCES += \ SOURCES += \
$$PWD/application.cpp \ $$PWD/application.cpp \
$$PWD/filelogger.cpp \ $$PWD/filelogger.cpp \
$$PWD/options.cpp \
$$PWD/main.cpp $$PWD/main.cpp
unix: HEADERS += $$PWD/stacktrace.h unix: HEADERS += $$PWD/stacktrace.h

View File

@ -63,6 +63,7 @@
#include "base/logger.h" #include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/settingsstorage.h" #include "base/settingsstorage.h"
#include "base/profile.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/iconprovider.h" #include "base/iconprovider.h"
@ -93,17 +94,34 @@ namespace
const QString LOG_FOLDER("logs"); const QString LOG_FOLDER("logs");
const char PARAMS_SEPARATOR[] = "|"; const char PARAMS_SEPARATOR[] = "|";
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
} }
Application::Application(const QString &id, int &argc, char **argv) Application::Application(const QString &id, int &argc, char **argv)
: BaseApplication(id, argc, argv) : BaseApplication(id, argc, argv)
, m_running(false) , m_running(false)
, m_shutdownAct(ShutdownDialogAction::Exit) , m_shutdownAct(ShutdownDialogAction::Exit)
, m_commandLineArgs(parseCommandLine(this->arguments()))
{ {
setApplicationName("qBittorrent");
validateCommandLineParameters();
QString profileDir = m_commandLineArgs.portableMode
? QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(DEFAULT_PORTABLE_MODE_PROFILE_DIR)
: m_commandLineArgs.profileDir;
Profile::initialize(profileDir, m_commandLineArgs.configurationName,
m_commandLineArgs.relativeFastresumePaths || m_commandLineArgs.portableMode);
Logger::initInstance(); Logger::initInstance();
SettingsStorage::initInstance(); SettingsStorage::initInstance();
Preferences::initInstance(); Preferences::initInstance();
if (m_commandLineArgs.webUiPort > 0) { // it will be -1 when user did not set any value
Preferences::instance()->setWebUiPort(m_commandLineArgs.webUiPort);
}
#if defined(Q_OS_MACX) && !defined(DISABLE_GUI) #if defined(Q_OS_MACX) && !defined(DISABLE_GUI)
if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) { if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
// fix Mac OS X 10.9 (mavericks) font issue // fix Mac OS X 10.9 (mavericks) font issue
@ -111,7 +129,6 @@ Application::Application(const QString &id, int &argc, char **argv)
QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
} }
#endif #endif
setApplicationName("qBittorrent");
initializeTranslation(); initializeTranslation();
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
@ -137,6 +154,11 @@ QPointer<MainWindow> Application::mainWindow()
} }
#endif #endif
const QBtCommandLineParameters &Application::commandLineArgs() const
{
return m_commandLineArgs;
}
bool Application::isFileLoggerEnabled() const bool Application::isFileLoggerEnabled() const
{ {
return settings()->loadValue(KEY_FILELOGGER_ENABLED, true).toBool(); return settings()->loadValue(KEY_FILELOGGER_ENABLED, true).toBool();
@ -153,7 +175,8 @@ void Application::setFileLoggerEnabled(bool value)
QString Application::fileLoggerPath() const QString Application::fileLoggerPath() const
{ {
return settings()->loadValue(KEY_FILELOGGER_PATH, QVariant(Utils::Fs::QDesktopServicesDataLocation() + LOG_FOLDER)).toString(); return settings()->loadValue(KEY_FILELOGGER_PATH,
QVariant(specialFolderLocation(SpecialFolder::Data) + LOG_FOLDER)).toString();
} }
void Application::setFileLoggerPath(const QString &value) void Application::setFileLoggerPath(const QString &value)
@ -633,3 +656,12 @@ void Application::cleanup()
Utils::Misc::shutdownComputer(m_shutdownAct); Utils::Misc::shutdownComputer(m_shutdownAct);
} }
} }
void Application::validateCommandLineParameters()
{
if (m_commandLineArgs.portableMode && !m_commandLineArgs.profileDir.isEmpty())
throw CommandLineParameterError(tr("Portable mode and explicit profile directory options are mutually exclusive"));
if (m_commandLineArgs.portableMode && m_commandLineArgs.relativeFastresumePaths)
Logger::instance()->addMessage(tr("Portable mode implies relative fastresume"), Log::WARNING);
}

View File

@ -51,6 +51,7 @@ typedef QtSingleCoreApplication BaseApplication;
#endif #endif
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "options.h"
#ifndef DISABLE_WEBUI #ifndef DISABLE_WEBUI
class WebUI; class WebUI;
@ -80,6 +81,8 @@ public:
QPointer<MainWindow> mainWindow(); QPointer<MainWindow> mainWindow();
#endif #endif
const QBtCommandLineParameters &commandLineArgs() const;
// FileLogger properties // FileLogger properties
bool isFileLoggerEnabled() const; bool isFileLoggerEnabled() const;
void setFileLoggerEnabled(bool value); void setFileLoggerEnabled(bool value);
@ -116,6 +119,7 @@ private slots:
private: private:
bool m_running; bool m_running;
ShutdownDialogAction m_shutdownAct; ShutdownDialogAction m_shutdownAct;
QBtCommandLineParameters m_commandLineArgs;
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
QPointer<MainWindow> m_window; QPointer<MainWindow> m_window;
@ -136,6 +140,7 @@ private:
void processParams(const QStringList &params); void processParams(const QStringList &params);
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const; void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent); void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
void validateCommandLineParameters();
}; };
#endif // APPLICATION_H #endif // APPLICATION_H

View File

@ -68,6 +68,8 @@ Q_IMPORT_PLUGIN(QICOPlugin)
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include "application.h" #include "application.h"
#include "options.h"
#include "base/profile.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/preferences.h" #include "base/preferences.h"
@ -86,44 +88,12 @@ const char *sysSigName[] = {
}; };
#endif #endif
struct QBtCommandLineParameters
{
bool showHelp;
#ifndef Q_OS_WIN
bool showVersion;
#endif
#ifndef DISABLE_GUI
bool noSplash;
#else
bool shouldDaemonize;
#endif
int webUiPort;
QStringList torrents;
QString unknownParameter;
QBtCommandLineParameters()
: showHelp(false)
#ifndef Q_OS_WIN
, showVersion(false)
#endif
#ifndef DISABLE_GUI
, noSplash(Preferences::instance()->isSplashScreenDisabled())
#else
, shouldDaemonize(false)
#endif
, webUiPort(Preferences::instance()->getWebUiPort())
{
}
};
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
void showSplashScreen(); void showSplashScreen();
#endif #endif
void displayVersion(); void displayVersion();
void displayUsage(const QString &prg_name);
bool userAgreesWithLegalNotice(); bool userAgreesWithLegalNotice();
void displayBadArgMessage(const QString &message); void displayBadArgMessage(const QString &message);
QBtCommandLineParameters parseCommandLine();
// Main // Main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -137,55 +107,39 @@ int main(int argc, char *argv[])
macMigratePlists(); macMigratePlists();
#endif #endif
#ifndef DISABLE_GUI try {
migrateRSS();
#endif
// Create Application // Create Application
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString(); QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
QScopedPointer<Application> app(new Application(appId, argc, argv)); QScopedPointer<Application> app(new Application(appId, argc, argv));
#ifndef DISABLE_GUI
const QBtCommandLineParameters params = parseCommandLine(); // after the application object creation because we need a profile to be set already
// for the migration
migrateRSS();
#endif
const QBtCommandLineParameters &params = app->commandLineArgs();
if (!params.unknownParameter.isEmpty()) { if (!params.unknownParameter.isEmpty()) {
displayBadArgMessage(QObject::tr("%1 is an unknown command line parameter.", "--random-parameter is an unknown command line parameter.") throw CommandLineParameterError(QObject::tr("%1 is an unknown command line parameter.",
"--random-parameter is an unknown command line parameter.")
.arg(params.unknownParameter)); .arg(params.unknownParameter));
return EXIT_FAILURE;
} }
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
if (params.showVersion) { if (params.showVersion) {
if (isOneArg) { if (isOneArg) {
displayVersion(); displayVersion();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
else { throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
displayBadArgMessage(QObject::tr("%1 must be the single command line parameter.")
.arg(QLatin1String("-v (or --version)"))); .arg(QLatin1String("-v (or --version)")));
return EXIT_FAILURE;
}
} }
#endif #endif
if (params.showHelp) { if (params.showHelp) {
if (isOneArg) { if (isOneArg) {
displayUsage(argv[0]); displayUsage(argv[0]);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
else { throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
displayBadArgMessage(QObject::tr("%1 must be the single command line parameter.")
.arg(QLatin1String("-h (or --help)"))); .arg(QLatin1String("-h (or --help)")));
return EXIT_FAILURE;
}
}
if ((params.webUiPort > 0) && (params.webUiPort <= 65535)) {
Preferences::instance()->setWebUiPort(params.webUiPort);
}
else {
displayBadArgMessage(QObject::tr("%1 must specify the correct port (1 to 65535).")
.arg(QLatin1String("--webui-port")));
return EXIT_FAILURE;
} }
// Set environment variable // Set environment variable
@ -207,9 +161,8 @@ int main(int argc, char *argv[])
if (app->isRunning()) { if (app->isRunning()) {
#ifdef DISABLE_GUI #ifdef DISABLE_GUI
if (params.shouldDaemonize) { if (params.shouldDaemonize) {
displayBadArgMessage(QObject::tr("You cannot use %1: qBittorrent is already running for this user.") throw CommandLineParameterError(QObject::tr("You cannot use %1: qBittorrent is already running for this user.")
.arg(QLatin1String("-d (or --daemon)"))); .arg(QLatin1String("-d (or --daemon)")));
return EXIT_FAILURE;
} }
else else
#endif #endif
@ -254,7 +207,6 @@ int main(int argc, char *argv[])
&& isatty(fileno(stdin)) && isatty(fileno(stdin))
&& isatty(fileno(stdout)))) return EXIT_FAILURE; && isatty(fileno(stdout)))) return EXIT_FAILURE;
#endif #endif
#ifdef DISABLE_GUI #ifdef DISABLE_GUI
if (params.shouldDaemonize) { if (params.shouldDaemonize) {
app.reset(); // Destroy current application app.reset(); // Destroy current application
@ -271,7 +223,7 @@ int main(int argc, char *argv[])
} }
} }
#else #else
if (!params.noSplash) if (!(params.noSplash || Preferences::instance()->isSplashScreenDisabled()))
showSplashScreen(); showSplashScreen();
#endif #endif
@ -283,59 +235,11 @@ int main(int argc, char *argv[])
#endif #endif
return app->exec(params.torrents); return app->exec(params.torrents);
}
QBtCommandLineParameters parseCommandLine()
{
QBtCommandLineParameters result;
QStringList appArguments = qApp->arguments();
for (int i = 1; i < appArguments.size(); ++i) {
const QString& arg = appArguments[i];
if ((arg.startsWith("--") && !arg.endsWith(".torrent")) ||
(arg.startsWith("-") && arg.size() == 2)) {
//Parse known parameters
if ((arg == QLatin1String("-h")) || (arg == QLatin1String("--help"))) {
result.showHelp = true;
} }
#ifndef Q_OS_WIN catch (CommandLineParameterError &er) {
else if ((arg == QLatin1String("-v")) || (arg == QLatin1String("--version"))) { displayBadArgMessage(er.messageForUser());
result.showVersion = true; return EXIT_FAILURE;
} }
#endif
else if (arg.startsWith(QLatin1String("--webui-port="))) {
QStringList parts = arg.split(QLatin1Char('='));
if (parts.size() == 2)
result.webUiPort = parts.last().toInt();
}
#ifndef DISABLE_GUI
else if (arg == QLatin1String("--no-splash")) {
result.noSplash = true;
}
#else
else if ((arg == QLatin1String("-d")) || (arg == QLatin1String("--daemon"))) {
result.shouldDaemonize = true;
}
#endif
else {
//Unknown argument
result.unknownParameter = arg;
break;
}
}
else {
QFileInfo torrentPath;
torrentPath.setFile(arg);
if (torrentPath.exists())
result.torrents += torrentPath.absoluteFilePath();
else
result.torrents += arg;
}
}
return result;
} }
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN) #if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
@ -396,53 +300,6 @@ void displayVersion()
std::cout << qPrintable(qApp->applicationName()) << " " << QBT_VERSION << std::endl; std::cout << qPrintable(qApp->applicationName()) << " " << QBT_VERSION << std::endl;
} }
QString makeUsage(const QString &prg_name)
{
QString text;
text += QObject::tr("Usage:") + QLatin1Char('\n');
#ifndef Q_OS_WIN
text += QLatin1Char('\t') + prg_name + QLatin1String(" (-v | --version)") + QLatin1Char('\n');
#endif
text += QLatin1Char('\t') + prg_name + QLatin1String(" (-h | --help)") + QLatin1Char('\n');
text += QLatin1Char('\t') + prg_name
+ QLatin1String(" [--webui-port=<port>]")
#ifndef DISABLE_GUI
+ QLatin1String(" [--no-splash]")
#else
+ QLatin1String(" [-d | --daemon]")
#endif
+ QLatin1String("[(<filename> | <url>)...]") + QLatin1Char('\n');
text += QObject::tr("Options:") + QLatin1Char('\n');
#ifndef Q_OS_WIN
text += QLatin1String("\t-v | --version\t\t") + QObject::tr("Displays program version") + QLatin1Char('\n');
#endif
text += QLatin1String("\t-h | --help\t\t") + QObject::tr("Displays this help message") + QLatin1Char('\n');
text += QLatin1String("\t--webui-port=<port>\t")
+ QObject::tr("Changes the Web UI port (current: %1)").arg(QString::number(Preferences::instance()->getWebUiPort()))
+ QLatin1Char('\n');
#ifndef DISABLE_GUI
text += QLatin1String("\t--no-splash\t\t") + QObject::tr("Disable splash screen") + QLatin1Char('\n');
#else
text += QLatin1String("\t-d | --daemon\t\t") + QObject::tr("Run in daemon-mode (background)") + QLatin1Char('\n');
#endif
text += QLatin1String("\tfiles or urls\t\t") + QObject::tr("Downloads the torrents passed by the user");
return text;
}
void displayUsage(const QString& prg_name)
{
#ifndef Q_OS_WIN
std::cout << qPrintable(makeUsage(prg_name)) << std::endl;
#else
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prg_name), QMessageBox::Ok);
msgBox.show(); // Need to be shown or to moveToCenter does not work
msgBox.move(Utils::Misc::screenCenter(&msgBox));
msgBox.exec();
#endif
}
void displayBadArgMessage(const QString& message) void displayBadArgMessage(const QString& message)
{ {
QString help = QObject::tr("Run application with -h option to read about command line parameters."); QString help = QObject::tr("Run application with -h option to read about command line parameters.");

398
src/app/options.cpp Normal file
View File

@ -0,0 +1,398 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "options.h"
#include <iostream>
#include <QDebug>
#include <QFileInfo>
#include <QProcessEnvironment>
#include <QTextStream>
#ifdef Q_OS_WIN
#include <QMessageBox>
#endif
#include "base/utils/misc.h"
namespace
{
// Base option class. Encapsulates name operations.
class Option
{
protected:
constexpr Option(const char *name, char shortcut = 0)
: m_name {name}
, m_shortcut {shortcut}
{
}
QString fullParameter() const
{
return QLatin1String("--") + QLatin1String(m_name);
}
QString shortcutParameter() const
{
return QLatin1String("-") + QLatin1Char(m_shortcut);
}
bool hasShortcut() const
{
return m_shortcut != 0;
}
QString envVarName() const
{
return QLatin1String("QBT_")
+ QString(QLatin1String(m_name)).toUpper().replace(QLatin1Char('-'), QLatin1Char('_'));
}
static QString padUsageText(const QString &usage)
{
const int TAB_WIDTH = 8;
QString res = QLatin1String("\t") + usage;
if (usage.size() < 2 * TAB_WIDTH)
return res + QLatin1String("\t\t");
else
return res + QLatin1String("\t");
}
private:
const char *m_name;
const char m_shortcut;
};
// Boolean option.
class BoolOption: protected Option
{
public:
constexpr BoolOption(const char *name, char shortcut = 0)
: Option {name, shortcut}
{
}
bool operator==(const QString &arg) const
{
return (hasShortcut() && ((arg.size() == 2) && (arg == shortcutParameter())))
|| (arg == fullParameter());
}
bool value(const QProcessEnvironment &env) const
{
QString val = env.value(envVarName());
// we accept "1" and "true" (upper or lower cased) as boolean 'true' values
return (val == QLatin1String("1") || val.toUpper() == QLatin1String("TRUE"));
}
QString usage() const
{
QString res;
if (hasShortcut())
res += shortcutParameter() + QLatin1String(" | ");
res += fullParameter();
return padUsageText(res);
}
};
inline bool operator==(const QString &s, const BoolOption &o)
{
return o == s;
}
// Option with string value. May not have a shortcut
struct StringOption: protected Option
{
public:
constexpr StringOption(const char *name)
: Option {name, 0}
{
}
bool operator==(const QString &arg) const
{
return arg.startsWith(parameterAssignment());
}
QString value(const QString &arg) const
{
QStringList parts = arg.split(QLatin1Char('='));
if (parts.size() == 2)
return unquote(parts[1]);
throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'",
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'")
.arg(fullParameter()).arg(QLatin1String("<value>")));
}
QString value(const QProcessEnvironment &env, const QString &defaultValue = QString()) const
{
QString val = env.value(envVarName());
return val.isEmpty() ? defaultValue : unquote(val);
}
QString usage(const QString &valueName) const
{
return padUsageText(parameterAssignment() + QLatin1Char('<') + valueName + QLatin1Char('>'));
}
private:
QString parameterAssignment() const
{
return fullParameter() + QLatin1Char('=');
}
static QString unquote(const QString &s)
{
auto isStringQuoted =
[](const QString &s, QChar quoteChar)
{
return (s.startsWith(quoteChar) && s.endsWith(quoteChar));
};
if ((s.size() >= 2) && (isStringQuoted(s, QLatin1Char('\'')) || isStringQuoted(s, QLatin1Char('"'))))
return s.mid(1, s.size() - 2);
return s;
}
};
inline bool operator==(const QString &s, const StringOption &o)
{
return o == s;
}
// Option with integer value. May not have a shortcut
class IntOption: protected StringOption
{
public:
constexpr IntOption(const char *name)
: StringOption {name}
{
}
using StringOption::operator==;
using StringOption::usage;
int value(const QString &arg) const
{
QString val = StringOption::value(arg);
bool ok = false;
int res = val.toInt(&ok);
if (!ok)
throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'",
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=<value>'")
.arg(fullParameter()).arg(QLatin1String("<integer value>")));
return res;
}
int value(const QProcessEnvironment &env, int defaultValue) const
{
QString val = env.value(envVarName());
if (val.isEmpty()) return defaultValue;
bool ok;
int res = val.toInt(&ok);
if (!ok) {
qDebug() << QObject::tr("Expected integer number in environment variable '%1', but got '%2'")
.arg(envVarName()).arg(val);
return defaultValue;
}
return res;
}
};
inline bool operator==(const QString &s, const IntOption &o)
{
return o == s;
}
constexpr const BoolOption SHOW_HELP_OPTION = {"help", 'h'};
constexpr const BoolOption SHOW_VERSION_OPTION = {"version", 'v'};
#ifdef DISABLE_GUI
constexpr const BoolOption DAEMON_OPTION = {"daemon", 'd'};
#else
constexpr const BoolOption NO_SPLASH_OPTION = {"no-splash"};
#endif
constexpr const IntOption WEBUI_PORT_OPTION = {"webui-port"};
constexpr const StringOption PROFILE_OPTION = {"profile"};
constexpr const StringOption CONFIGURATION_OPTION = {"configuration"};
constexpr const BoolOption PORTABLE_OPTION = {"portable"};
constexpr const BoolOption RELATIVE_FASTRESUME = {"relative-fastresume"};
}
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
: showHelp(false)
#ifndef Q_OS_WIN
, showVersion(false)
#endif
#ifndef DISABLE_GUI
, noSplash(NO_SPLASH_OPTION.value(env))
#else
, shouldDaemonize(DAEMON_OPTION.value(env))
#endif
, webUiPort(WEBUI_PORT_OPTION.value(env, -1))
, profileDir(PROFILE_OPTION.value(env))
, relativeFastresumePaths(RELATIVE_FASTRESUME.value(env))
, portableMode(PORTABLE_OPTION.value(env))
, configurationName(CONFIGURATION_OPTION.value(env))
{
}
QBtCommandLineParameters parseCommandLine(const QStringList &args)
{
QBtCommandLineParameters result {QProcessEnvironment::systemEnvironment()};
for (int i = 1; i < args.count(); ++i) {
const QString &arg = args[i];
if ((arg.startsWith("--") && !arg.endsWith(".torrent"))
|| (arg.startsWith("-") && (arg.size() == 2))) {
// Parse known parameters
if ((arg == SHOW_HELP_OPTION)) {
result.showHelp = true;
}
#ifndef Q_OS_WIN
else if (arg == SHOW_VERSION_OPTION) {
result.showVersion = true;
}
#endif
else if (arg == WEBUI_PORT_OPTION) {
result.webUiPort = WEBUI_PORT_OPTION.value(arg);
if ((result.webUiPort < 1) || (result.webUiPort > 65535))
throw CommandLineParameterError(QObject::tr("%1 must specify the correct port (1 to 65535).")
.arg(QLatin1String("--webui-port")));
}
#ifndef DISABLE_GUI
else if (arg == NO_SPLASH_OPTION) {
result.noSplash = true;
}
#else
else if (arg == DAEMON_OPTION) {
result.shouldDaemonize = true;
}
#endif
else if (arg == PROFILE_OPTION) {
result.profileDir = PROFILE_OPTION.value(arg);
}
else if (arg == RELATIVE_FASTRESUME) {
result.relativeFastresumePaths = true;
}
else if (arg == PORTABLE_OPTION) {
result.portableMode = true;
}
else if (arg == CONFIGURATION_OPTION) {
result.configurationName = CONFIGURATION_OPTION.value(arg);
}
else {
// Unknown argument
result.unknownParameter = arg;
break;
}
}
else {
QFileInfo torrentPath;
torrentPath.setFile(arg);
if (torrentPath.exists())
result.torrents += torrentPath.absoluteFilePath();
else
result.torrents += arg;
}
}
return result;
}
CommandLineParameterError::CommandLineParameterError(const QString &messageForUser)
: std::runtime_error(messageForUser.toLocal8Bit().data())
, m_messageForUser(messageForUser)
{
}
const QString& CommandLineParameterError::messageForUser() const
{
return m_messageForUser;
}
QString makeUsage(const QString &prgName)
{
QString text;
QTextStream stream(&text, QIODevice::WriteOnly);
stream << QObject::tr("Usage:") << '\n';
#ifndef Q_OS_WIN
stream << '\t' << prgName << " [options] [(<filename> | <url>)...]" << '\n';
#endif
stream << QObject::tr("Options:") << '\n';
#ifndef Q_OS_WIN
stream << SHOW_VERSION_OPTION.usage() << QObject::tr("Displays program version and exit") << '\n';
#endif
stream << SHOW_HELP_OPTION.usage() << QObject::tr("Displays this help message and exit") << '\n';
stream << WEBUI_PORT_OPTION.usage(QLatin1String("port"))
<< QObject::tr("Changes the Web UI port")
<< '\n';
#ifndef DISABLE_GUI
stream << NO_SPLASH_OPTION.usage() << QObject::tr("Disable splash screen") << '\n';
#else
stream << DAEMON_OPTION.usage() << QObject::tr("Run in daemon-mode (background)") << '\n';
#endif
stream << PROFILE_OPTION.usage(QLatin1String("dir")) << QObject::tr("Store configuration files in <dir>") << '\n';
stream << CONFIGURATION_OPTION.usage(QLatin1String("name")) << QObject::tr("Store configuration files in directories qBittorrent_<name>") << '\n';
stream << RELATIVE_FASTRESUME.usage() << QObject::tr("Hack into libtorrent fastresume files and make file paths relative to the profile directory") << '\n';
stream << PORTABLE_OPTION.usage() << QObject::tr("Shortcut for --profile=<exe dir>/profile --relative-fastresume") << '\n';
stream << "\tfiles or urls\t\t" << QObject::tr("Downloads the torrents passed by the user") << '\n'
<< '\n';
stream << QObject::tr("Option values may be supplied via environment variables.") << '\n'
<< QObject::tr("For option named 'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper case, '-' replaced with '_')") << '\n'
<< QObject::tr("To pass flag values, set the variable to '1' or 'TRUE'.") << '\n'
<< QObject::tr("For example, to disable the splash screen: ")
<< "QBT_NO_SPLASH=1 " << prgName << '\n'
<< '\n'
<< QObject::tr("Command line parameters take precedence over environment variables") << '\n';
stream << flush;
return text;
}
void displayUsage(const QString &prgName)
{
#ifndef Q_OS_WIN
std::cout << qPrintable(makeUsage(prgName)) << std::endl;
#else
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prgName), QMessageBox::Ok);
msgBox.show(); // Need to be shown or to moveToCenter does not work
msgBox.move(Utils::Misc::screenCenter(&msgBox));
msgBox.exec();
#endif
}

View File

@ -1,5 +1,7 @@
/* /*
* Bittorrent Client using Qt4 and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez * Copyright (C) 2006 Christophe Dumez
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -28,46 +30,49 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#ifndef QINISETTINGS_H #ifndef APP_OPTIONS_H
#define QINISETTINGS_H #define APP_OPTIONS_H
#include <QSettings> #include <stdexcept>
class QIniSettings : public QSettings { #include <QString>
Q_OBJECT #include <QStringList>
Q_DISABLE_COPY (QIniSettings)
public: class QProcessEnvironment;
QIniSettings(const QString &organization = "qBittorrent", const QString &application = "qBittorrent", QObject *parent = 0 ):
#if defined(Q_OS_WIN) || defined(Q_OS_MAC) struct QBtCommandLineParameters
QSettings(QSettings::IniFormat, QSettings::UserScope, organization, application, parent) {
bool showHelp;
#ifndef Q_OS_WIN
bool showVersion;
#endif
#ifndef DISABLE_GUI
bool noSplash;
#else #else
QSettings(organization, application, parent) bool shouldDaemonize;
#endif #endif
{ int webUiPort;
QString profileDir;
bool relativeFastresumePaths;
bool portableMode;
QString configurationName;
QStringList torrents;
QString unknownParameter;
} QBtCommandLineParameters(const QProcessEnvironment&);
QIniSettings(const QString &fileName, Format format, QObject *parent = 0 ) : QSettings(fileName, format, parent) {
}
#ifdef Q_OS_WIN
QVariant value(const QString & key, const QVariant &defaultValue = QVariant()) const {
QString key_tmp(key);
QVariant ret = QSettings::value(key_tmp);
if (ret.isNull())
return defaultValue;
return ret;
}
void setValue(const QString &key, const QVariant &val) {
QString key_tmp(key);
if (format() == QSettings::NativeFormat) // Using registry, don't touch replace here
key_tmp.replace("\\", "/");
QSettings::setValue(key_tmp, val);
}
#endif
}; };
#endif // QINISETTINGS_H class CommandLineParameterError: public std::runtime_error
{
public:
CommandLineParameterError(const QString &messageForUser);
const QString& messageForUser() const;
private:
const QString m_messageForUser;
};
QBtCommandLineParameters parseCommandLine(const QStringList &args);
void displayUsage(const QString &prgName);
#endif // APP_OPTIONS_H

View File

@ -47,13 +47,16 @@
#endif #endif
#include <QRegExp> #include <QRegExp>
#include <QString> #include <QString>
#ifdef Q_OS_MAC
#include <QSettings>
#endif
#include "base/logger.h" #include "base/logger.h"
#include "base/profile.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/qinisettings.h"
bool userAcceptsUpgrade() bool userAcceptsUpgrade()
{ {
@ -145,7 +148,7 @@ bool upgrade(bool ask = true)
// Upgrade preferences // Upgrade preferences
Preferences::instance()->upgrade(); Preferences::instance()->upgrade();
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup"); QString backupFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + "BT_backup");
QDir backupFolderDir(backupFolderPath); QDir backupFolderDir(backupFolderPath);
// **************************************************************************************** // ****************************************************************************************
@ -156,15 +159,10 @@ bool upgrade(bool ask = true)
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile)); upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
// **************************************************************************************** // ****************************************************************************************
#ifdef Q_OS_MAC SettingsPtr oldResumeSettings = Profile::instance().applicationSettings(QLatin1String("qBittorrent-resume"));
// native .plist
QSettings *oldResumeSettings = new QSettings("qBittorrent", "qBittorrent-resume");
#else
QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume");
#endif
QString oldResumeFilename = oldResumeSettings->fileName(); QString oldResumeFilename = oldResumeSettings->fileName();
QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash(); QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash();
delete oldResumeSettings; oldResumeSettings.reset();
if (oldResumeData.isEmpty()) { if (oldResumeData.isEmpty()) {
Utils::Fs::forceRemove(oldResumeFilename); Utils::Fs::forceRemove(oldResumeFilename);
@ -231,7 +229,7 @@ bool upgrade(bool ask = true)
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
void migratePlistToIni(const QString &application) void migratePlistToIni(const QString &application)
{ {
QIniSettings iniFile("qBittorrent", application); QSettings iniFile(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", application);
if (!iniFile.allKeys().isEmpty()) return; // We copy the contents of plist, only if inifile does not exist(is empty). if (!iniFile.allKeys().isEmpty()) return; // We copy the contents of plist, only if inifile does not exist(is empty).
QSettings *plistFile = new QSettings("qBittorrent", application); QSettings *plistFile = new QSettings("qBittorrent", application);
@ -260,16 +258,16 @@ void macMigratePlists()
void migrateRSS() void migrateRSS()
{ {
// Copy old feed items to new file if needed // Copy old feed items to new file if needed
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss-feeds"); SettingsPtr qBTRSS = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss-feeds"));
if (!qBTRSS.allKeys().isEmpty()) return; // We move the contents of RSS old_items only if inifile does not exist (is empty). if (!qBTRSS->allKeys().isEmpty()) return; // We move the contents of RSS old_items only if inifile does not exist (is empty).
QIniSettings qBTRSSLegacy("qBittorrent", "qBittorrent-rss"); SettingsPtr qBTRSSLegacy = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss"));
QHash<QString, QVariant> allOldItems = qBTRSSLegacy.value("old_items", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> allOldItems = qBTRSSLegacy->value("old_items", QHash<QString, QVariant>()).toHash();
if (!allOldItems.empty()) { if (!allOldItems.empty()) {
qDebug("Moving %d old items for feeds to qBittorrent-rss-feeds", allOldItems.size()); qDebug("Moving %d old items for feeds to qBittorrent-rss-feeds", allOldItems.size());
qBTRSS.setValue("old_items", allOldItems); qBTRSS->setValue("old_items", allOldItems);
qBTRSSLegacy.remove("old_items"); qBTRSSLegacy->remove("old_items");
} }
} }
#endif #endif

View File

@ -33,6 +33,7 @@ net/private/geoipdatabase.h
net/proxyconfigurationmanager.h net/proxyconfigurationmanager.h
net/reverseresolution.h net/reverseresolution.h
net/smtp.h net/smtp.h
private/profile_p.h
rss/private/rssparser.h rss/private/rssparser.h
rss/rssarticle.h rss/rssarticle.h
rss/rssdownloadrule.h rss/rssdownloadrule.h
@ -52,7 +53,7 @@ iconprovider.h
indexrange.h indexrange.h
logger.h logger.h
preferences.h preferences.h
qinisettings.h profile.h
scanfoldersmodel.h scanfoldersmodel.h
searchengine.h searchengine.h
settingsstorage.h settingsstorage.h
@ -94,6 +95,7 @@ net/private/geoipdatabase.cpp
net/proxyconfigurationmanager.cpp net/proxyconfigurationmanager.cpp
net/reverseresolution.cpp net/reverseresolution.cpp
net/smtp.cpp net/smtp.cpp
private/profile_p.cpp
rss/private/rssparser.cpp rss/private/rssparser.cpp
rss/rssarticle.cpp rss/rssarticle.cpp
rss/rssdownloadrule.cpp rss/rssdownloadrule.cpp
@ -112,6 +114,7 @@ filesystemwatcher.cpp
iconprovider.cpp iconprovider.cpp
logger.cpp logger.cpp
preferences.cpp preferences.cpp
profile.cpp
scanfoldersmodel.cpp scanfoldersmodel.cpp
searchengine.cpp searchengine.cpp
settingsstorage.cpp settingsstorage.cpp

View File

@ -2,7 +2,6 @@ HEADERS += \
$$PWD/types.h \ $$PWD/types.h \
$$PWD/tristatebool.h \ $$PWD/tristatebool.h \
$$PWD/filesystemwatcher.h \ $$PWD/filesystemwatcher.h \
$$PWD/qinisettings.h \
$$PWD/logger.h \ $$PWD/logger.h \
$$PWD/settingsstorage.h \ $$PWD/settingsstorage.h \
$$PWD/settingvalue.h \ $$PWD/settingvalue.h \
@ -55,6 +54,8 @@ HEADERS += \
$$PWD/utils/net.h \ $$PWD/utils/net.h \
$$PWD/utils/random.h \ $$PWD/utils/random.h \
$$PWD/utils/string.h \ $$PWD/utils/string.h \
$$PWD/profile.h \
$$PWD/private/profile_p.h \
$$PWD/unicodestrings.h \ $$PWD/unicodestrings.h \
$$PWD/torrentfileguard.h \ $$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \ $$PWD/torrentfilter.h \
@ -112,6 +113,8 @@ SOURCES += \
$$PWD/utils/net.cpp \ $$PWD/utils/net.cpp \
$$PWD/utils/random.cpp \ $$PWD/utils/random.cpp \
$$PWD/utils/string.cpp \ $$PWD/utils/string.cpp \
$$PWD/profile.cpp \
$$PWD/private/profile_p.cpp \
$$PWD/torrentfileguard.cpp \ $$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \ $$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp \ $$PWD/scanfoldersmodel.cpp \

View File

@ -1,11 +1,12 @@
#include "statistics.h"
#include <QDateTime> #include <QDateTime>
#include <libtorrent/session.hpp> #include <libtorrent/session.hpp>
#include "base/qinisettings.h"
#include "base/bittorrent/sessionstatus.h" #include "base/bittorrent/sessionstatus.h"
#include "base/profile.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "statistics.h"
static const qint64 SAVE_INTERVAL = 15 * 60 * 1000; static const qint64 SAVE_INTERVAL = 15 * 60 * 1000;
@ -64,19 +65,19 @@ void Statistics::save() const
if (!m_dirty || ((now - m_lastWrite) < SAVE_INTERVAL)) if (!m_dirty || ((now - m_lastWrite) < SAVE_INTERVAL))
return; return;
QIniSettings s("qBittorrent", "qBittorrent-data"); SettingsPtr s = Profile::instance().applicationSettings(QLatin1String("qBittorrent-data"));
QVariantHash v; QVariantHash v;
v.insert("AlltimeDL", m_alltimeDL + m_sessionDL); v.insert("AlltimeDL", m_alltimeDL + m_sessionDL);
v.insert("AlltimeUL", m_alltimeUL + m_sessionUL); v.insert("AlltimeUL", m_alltimeUL + m_sessionUL);
s.setValue("Stats/AllStats", v); s->setValue("Stats/AllStats", v);
m_dirty = false; m_dirty = false;
m_lastWrite = now; m_lastWrite = now;
} }
void Statistics::load() void Statistics::load()
{ {
QIniSettings s("qBittorrent", "qBittorrent-data"); SettingsPtr s = Profile::instance().applicationSettings(QLatin1String("qBittorrent-data"));
QVariantHash v = s.value("Stats/AllStats").toHash(); QVariantHash v = s->value("Stats/AllStats").toHash();
m_alltimeDL = v["AlltimeDL"].toULongLong(); m_alltimeDL = v["AlltimeDL"].toULongLong();
m_alltimeUL = v["AlltimeUL"].toULongLong(); m_alltimeUL = v["AlltimeUL"].toULongLong();

View File

@ -66,6 +66,7 @@
#include <libtorrent/torrent_info.hpp> #include <libtorrent/torrent_info.hpp>
#include "base/logger.h" #include "base/logger.h"
#include "base/profile.h"
#include "base/net/downloadhandler.h" #include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
#include "base/net/portforwarder.h" #include "base/net/portforwarder.h"
@ -132,7 +133,7 @@ namespace
return tmp; return tmp;
} }
QString normalizeSavePath(QString path, const QString &defaultPath = Utils::Fs::QDesktopServicesDownloadLocation()) QString normalizeSavePath(QString path, const QString &defaultPath = specialFolderLocation(SpecialFolder::Downloads))
{ {
path = path.trimmed(); path = path.trimmed();
if (path.isEmpty()) if (path.isEmpty())
@ -266,7 +267,7 @@ Session::Session(QObject *parent)
, m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false) , m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false)
, m_storedCategories(BITTORRENT_SESSION_KEY("Categories")) , m_storedCategories(BITTORRENT_SESSION_KEY("Categories"))
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause) , m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
, m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), Utils::Fs::QDesktopServicesDownloadLocation(), normalizePath) , m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), normalizePath)
, m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath) , m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath)
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false) , m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
, m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false) , m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
@ -3003,7 +3004,7 @@ bool Session::hasPerTorrentRatioLimit() const
void Session::initResumeFolder() void Session::initResumeFolder()
{ {
m_resumeFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + RESUME_FOLDER); m_resumeFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER);
QDir resumeFolderDir(m_resumeFolderPath); QDir resumeFolderDir(m_resumeFolderPath);
if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath())) { if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath())) {
m_resumeFolderLock.setFileName(resumeFolderDir.absoluteFilePath("session.lock")); m_resumeFolderLock.setFileName(resumeFolderDir.absoluteFilePath("session.lock"));
@ -3632,7 +3633,8 @@ namespace
if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false; if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false;
#endif #endif
torrentData.savePath = Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath"))); torrentData.savePath = Profile::instance().fromPortablePath(
Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath"))));
torrentData.ratioLimit = QString::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble(); torrentData.ratioLimit = QString::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble();
// ************************************************************************************** // **************************************************************************************
// Workaround to convert legacy label to category // Workaround to convert legacy label to category

View File

@ -54,6 +54,7 @@
#include "base/logger.h" #include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/profile.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
@ -1477,7 +1478,11 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
resumeData["qBt-paused"] = isPaused(); resumeData["qBt-paused"] = isPaused();
resumeData["qBt-forced"] = isForced(); resumeData["qBt-forced"] = isForced();
} }
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : m_savePath.toStdString(); else {
auto savePath = resumeData.find_key("save_path")->string();
resumeData["save_path"] = Profile::instance().toPortablePath(QString::fromStdString(savePath)).toStdString();
}
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance().toPortablePath(m_savePath).toStdString();
resumeData["qBt-ratioLimit"] = QString::number(m_ratioLimit).toStdString(); resumeData["qBt-ratioLimit"] = QString::number(m_ratioLimit).toStdString();
resumeData["qBt-category"] = m_category.toStdString(); resumeData["qBt-category"] = m_category.toStdString();
resumeData["qBt-name"] = m_name.toStdString(); resumeData["qBt-name"] = m_name.toStdString();

View File

@ -35,6 +35,7 @@
#include "base/logger.h" #include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/profile.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/gzip.h" #include "base/utils/gzip.h"
#include "downloadmanager.h" #include "downloadmanager.h"
@ -94,7 +95,7 @@ void GeoIPManager::loadDatabase()
} }
QString filepath = Utils::Fs::expandPathAbs( QString filepath = Utils::Fs::expandPathAbs(
QString("%1%2/%3").arg(Utils::Fs::QDesktopServicesDataLocation()) QString("%1%2/%3").arg(specialFolderLocation(SpecialFolder::Data))
.arg(GEOIP_FOLDER).arg(GEOIP_FILENAME)); .arg(GEOIP_FOLDER).arg(GEOIP_FILENAME));
QString error; QString error;
@ -431,7 +432,7 @@ void GeoIPManager::downloadFinished(const QString &url, QByteArray data)
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()), .arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
Log::INFO); Log::INFO);
QString targetPath = Utils::Fs::expandPathAbs( QString targetPath = Utils::Fs::expandPathAbs(
Utils::Fs::QDesktopServicesDataLocation() + GEOIP_FOLDER); specialFolderLocation(SpecialFolder::Data) + GEOIP_FOLDER);
if (!QDir(targetPath).exists()) if (!QDir(targetPath).exists())
QDir().mkpath(targetPath); QDir().mkpath(targetPath);
QFile targetFile(QString("%1/%2").arg(targetPath).arg(GEOIP_FILENAME)); QFile targetFile(QString("%1/%2").arg(targetPath).arg(GEOIP_FILENAME));

View File

@ -0,0 +1,250 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2012 Christophe Dumez
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
*/
#include "profile_p.h"
#include <QCoreApplication>
#include <QStandardPaths>
#ifdef Q_OS_MAC
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>
#endif
#ifdef Q_OS_WIN
#include <shlobj.h>
#endif
#include "base/utils/fs.h"
Private::Profile::Profile(const QString &configurationName)
: m_configurationName {configurationName.isEmpty()
? QCoreApplication::applicationName()
: QCoreApplication::applicationName() + QLatin1Char('_') + configurationName}
{
}
QString Private::Profile::configurationName() const
{
return m_configurationName;
}
Private::DefaultProfile::DefaultProfile(const QString &configurationName)
: Profile(configurationName)
{
}
QString Private::DefaultProfile::baseDirectory() const
{
return QDir::homePath();
}
QString Private::DefaultProfile::cacheLocation() const
{
QString result;
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
result = dataLocation() + QLatin1String("cache");
#else
#ifdef Q_OS_MAC
// http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
FSRef ref;
OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref);
if (err)
return QString();
QByteArray ba(2048, 0);
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
result += QLatin1Char('/') + configurationName();
#else
QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
if (xdgCacheHome.isEmpty())
xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
xdgCacheHome += QLatin1Char('/') + configurationName();
result = xdgCacheHome;
#endif
#endif
if (!result.endsWith("/"))
result += "/";
return result;
}
QString Private::DefaultProfile::configLocation() const
{
QString result;
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
result = dataLocation() + QLatin1String("config");
#else
#ifdef Q_OS_MAC
result = QDir::homePath() + QLatin1String("/Library/Preferences/") + configurationName();
#else
QString xdgConfigHome = QLatin1String(qgetenv("XDG_CONFIG_HOME"));
if (xdgConfigHome.isEmpty())
xdgConfigHome = QDir::homePath() + QLatin1String("/.config");
xdgConfigHome += QLatin1Char('/') + configurationName();
result = xdgConfigHome;
#endif
#endif
return result;
}
QString Private::DefaultProfile::dataLocation() const
{
QString result;
#if defined(Q_OS_WIN)
wchar_t path[MAX_PATH + 1] = {L'\0'};
if (SHGetSpecialFolderPathW(0, path, CSIDL_LOCAL_APPDATA, FALSE))
result = Utils::Fs::fromNativePath(QString::fromWCharArray(path));
if (!QCoreApplication::applicationName().isEmpty())
result += QLatin1String("/") + qApp->applicationName();
#elif defined(Q_OS_MAC)
FSRef ref;
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
if (err)
return QString();
QByteArray ba(2048, 0);
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
result += QLatin1Char('/') + qApp->applicationName();
#else
QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
if (xdgDataHome.isEmpty())
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
xdgDataHome += QLatin1String("/data/")
+ qApp->applicationName();
result = xdgDataHome;
#endif
if (!result.endsWith("/"))
result += "/";
return result;
}
QString Private::DefaultProfile::downloadLocation() const
{
#if defined(Q_OS_WIN)
if (QSysInfo::windowsVersion() <= QSysInfo::WV_XP) // Windows XP
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
#endif
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
}
SettingsPtr Private::DefaultProfile::applicationSettings(const QString &name) const
{
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
return SettingsPtr(new QSettings(QSettings::IniFormat, QSettings::UserScope, configurationName(), name));
#else
return SettingsPtr(new QSettings(configurationName(), name));
#endif
}
Private::CustomProfile::CustomProfile(const QString &rootPath, const QString &configurationName)
: Profile {configurationName}
, m_rootDirectory {QDir(rootPath).absoluteFilePath(this->configurationName())}
{
}
QString Private::CustomProfile::baseDirectory() const
{
return m_rootDirectory.canonicalPath();
}
QString Private::CustomProfile::cacheLocation() const
{
return m_rootDirectory.absoluteFilePath(QLatin1String(cacheDirName));
}
QString Private::CustomProfile::configLocation() const
{
return m_rootDirectory.absoluteFilePath(QLatin1String(configDirName));
}
QString Private::CustomProfile::dataLocation() const
{
return m_rootDirectory.absoluteFilePath(QLatin1String(dataDirName));
}
QString Private::CustomProfile::downloadLocation() const
{
return m_rootDirectory.absoluteFilePath(QLatin1String(downloadsDirName));
}
SettingsPtr Private::CustomProfile::applicationSettings(const QString &name) const
{
// here we force QSettings::IniFormat format always because we need it to be portable across platforms
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
constexpr const char *CONF_FILE_EXTENSION = ".ini";
#else
constexpr const char *CONF_FILE_EXTENSION = ".conf";
#endif
const QString settingsFileName {QDir(configLocation()).absoluteFilePath(name + QLatin1String(CONF_FILE_EXTENSION))};
return SettingsPtr(new QSettings(settingsFileName, QSettings::IniFormat));
}
QString Private::NoConvertConverter::fromPortablePath(const QString &portablePath) const
{
return portablePath;
}
QString Private::NoConvertConverter::toPortablePath(const QString &path) const
{
return path;
}
Private::Converter::Converter(const QString &basePath)
: m_baseDir {basePath}
{
m_baseDir.makeAbsolute();
}
QString Private::Converter::toPortablePath(const QString &path) const
{
if (path.isEmpty() || m_baseDir.path().isEmpty())
return path;
#ifdef Q_OS_WIN
if (QDir::isAbsolutePath(path)) {
QChar driveLeter = path[0].toUpper();
QChar baseDriveLetter = m_baseDir.path()[0].toUpper();
bool onSameDrive = (driveLeter.category() == QChar::Letter_Uppercase) && (driveLeter == baseDriveLetter);
if (!onSameDrive)
return path;
}
#endif
return m_baseDir.relativeFilePath(path);
}
QString Private::Converter::fromPortablePath(const QString &portablePath) const
{
if (QDir::isAbsolutePath(portablePath))
return portablePath;
return QDir::cleanPath(m_baseDir.absoluteFilePath(portablePath));
}

View File

@ -0,0 +1,121 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2012 Christophe Dumez
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
*/
#ifndef QBT_PROFILE_P_H
#define QBT_PROFILE_P_H
#include <QDir>
#include "base/profile.h"
namespace Private
{
class Profile
{
public:
virtual QString baseDirectory() const = 0;
virtual QString cacheLocation() const = 0;
virtual QString configLocation() const = 0;
virtual QString dataLocation() const = 0;
virtual QString downloadLocation() const = 0;
virtual SettingsPtr applicationSettings(const QString &name) const = 0;
virtual ~Profile() = default;
QString configurationName() const;
protected:
Profile(const QString &configurationName);
private:
QString m_configurationName;
};
/// Default implementation. Takes paths from system
class DefaultProfile: public Profile
{
public:
DefaultProfile(const QString &configurationName);
QString baseDirectory() const override;
QString cacheLocation() const override;
QString configLocation() const override;
QString dataLocation() const override;
QString downloadLocation() const override;
SettingsPtr applicationSettings(const QString &name) const override;
};
/// Custom tree: creates directories under the specified root directory
class CustomProfile: public Profile
{
public:
CustomProfile(const QString &rootPath, const QString &configurationName);
QString baseDirectory() const override;
QString cacheLocation() const override;
QString configLocation() const override;
QString dataLocation() const override;
QString downloadLocation() const override;
SettingsPtr applicationSettings(const QString &name) const override;
private:
QDir m_rootDirectory;
static constexpr const char *cacheDirName = "cache";
static constexpr const char *configDirName = "config";
static constexpr const char *dataDirName = "data";
static constexpr const char *downloadsDirName = "downloads";
};
class PathConverter
{
public:
virtual QString toPortablePath(const QString &path) const = 0;
virtual QString fromPortablePath(const QString &portablePath) const = 0;
virtual ~PathConverter() = default;
};
class NoConvertConverter: public PathConverter
{
public:
QString toPortablePath(const QString &path) const override;
QString fromPortablePath(const QString &portablePath) const override;
};
class Converter: public PathConverter
{
public:
Converter(const QString &basePath);
QString toPortablePath(const QString &path) const override;
QString fromPortablePath(const QString &portablePath) const override;
private:
QDir m_baseDir;
};
}
#endif // QBT_PROFILE_P_H

118
src/base/profile.cpp Normal file
View File

@ -0,0 +1,118 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2012 Christophe Dumez
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
*/
#include "profile.h"
#include <QCoreApplication>
#include "private/profile_p.h"
Profile *Profile::m_instance = nullptr;
Profile::Profile(Private::Profile *impl, Private::PathConverter *pathConverter)
: m_profileImpl(impl)
, m_pathConverterImpl(pathConverter)
{
ensureDirectoryExists(SpecialFolder::Cache);
ensureDirectoryExists(SpecialFolder::Config);
ensureDirectoryExists(SpecialFolder::Data);
ensureDirectoryExists(SpecialFolder::Downloads);
}
// to generate correct call to ProfilePrivate::~ProfileImpl()
Profile::~Profile() = default;
void Profile::initialize(const QString &rootProfilePath, const QString &configurationName,
bool convertPathsToProfileRelative)
{
QScopedPointer<Private::Profile> profile(rootProfilePath.isEmpty()
? static_cast<Private::Profile *>(new Private::DefaultProfile(configurationName))
: static_cast<Private::Profile *>(new Private::CustomProfile(rootProfilePath, configurationName)));
QScopedPointer<Private::PathConverter> converter(convertPathsToProfileRelative
? static_cast<Private::PathConverter *>(new Private::Converter(profile->baseDirectory()))
: static_cast<Private::PathConverter *>(new Private::NoConvertConverter()));
m_instance = new Profile(profile.take(), converter.take());
}
const Profile &Profile::instance()
{
return *m_instance;
}
QString Profile::location(SpecialFolder folder) const
{
QString result;
switch (folder) {
case SpecialFolder::Cache:
result = m_profileImpl->cacheLocation();
break;
case SpecialFolder::Config:
result = m_profileImpl->configLocation();
break;
case SpecialFolder::Data:
result = m_profileImpl->dataLocation();
break;
case SpecialFolder::Downloads:
result = m_profileImpl->downloadLocation();
break;
}
if (!result.endsWith(QLatin1Char('/')))
result += QLatin1Char('/');
return result;
}
QString Profile::configurationName() const
{
return m_profileImpl->configurationName();
}
SettingsPtr Profile::applicationSettings(const QString &name) const
{
return m_profileImpl->applicationSettings(name);
}
void Profile::ensureDirectoryExists(SpecialFolder folder)
{
QString locationPath = location(folder);
if (!locationPath.isEmpty() && !QDir().mkpath(locationPath))
qFatal("Could not create required directory '%s'", qPrintable(locationPath));
}
QString Profile::toPortablePath(const QString &absolutePath) const
{
return m_pathConverterImpl->toPortablePath(absolutePath);
}
QString Profile::fromPortablePath(const QString &portablePath) const
{
return m_pathConverterImpl->fromPortablePath(portablePath);
}

92
src/base/profile.h Normal file
View File

@ -0,0 +1,92 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2012 Christophe Dumez
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
*/
#ifndef QBT_PROFILE_H
#define QBT_PROFILE_H
#include <memory>
#include <QString>
#include <QScopedPointer>
#include <QSettings>
class Application;
namespace Private
{
class Profile;
class PathConverter;
}
using SettingsPtr = std::unique_ptr<QSettings>;
enum class SpecialFolder
{
Cache,
Config,
Data,
Downloads
};
class Profile
{
public:
QString location(SpecialFolder folder) const;
SettingsPtr applicationSettings(const QString &name) const;
/// Returns either default name for configuration file (QCoreApplication::applicationName())
/// or the value, supplied via parameters
QString configurationName() const;
QString toPortablePath(const QString &absolutePath) const;
QString fromPortablePath(const QString &portablePath) const;
static const Profile &instance();
private:
Profile(Private::Profile *impl, Private::PathConverter *pathConverter);
~Profile();
friend class ::Application;
static void initialize(const QString &rootProfilePath, const QString &configurationName,
bool convertPathsToProfileRelative);
void ensureDirectoryExists(SpecialFolder folder);
QScopedPointer<Private::Profile> m_profileImpl;
QScopedPointer<Private::PathConverter> m_pathConverterImpl;
static Profile *m_instance;
};
inline QString specialFolderLocation(SpecialFolder folder)
{
return Profile::instance().location(folder);
}
#endif // QBT_PROFILE_H

View File

@ -28,13 +28,14 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "rssdownloadrulelist.h"
#include <QFile> #include <QFile>
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>
#include "base/preferences.h" #include "base/preferences.h"
#include "base/qinisettings.h" #include "base/profile.h"
#include "rssdownloadrulelist.h"
using namespace Rss; using namespace Rss;
@ -66,14 +67,14 @@ void DownloadRuleList::replace(DownloadRuleList *other)
void DownloadRuleList::saveRulesToStorage() void DownloadRuleList::saveRulesToStorage()
{ {
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); SettingsPtr qBTRSS = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss"));
qBTRSS.setValue("download_rules", toVariantHash()); qBTRSS->setValue("download_rules", toVariantHash());
} }
void DownloadRuleList::loadRulesFromStorage() void DownloadRuleList::loadRulesFromStorage()
{ {
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); SettingsPtr qBTRSS = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss"));
loadRulesFromVariantHash(qBTRSS.value("download_rules").toHash()); loadRulesFromVariantHash(qBTRSS->value("download_rules").toHash());
} }
QVariantHash DownloadRuleList::toVariantHash() const QVariantHash DownloadRuleList::toVariantHash() const

View File

@ -30,11 +30,13 @@
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org * Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
*/ */
#include "rssfeed.h"
#include <QDebug> #include <QDebug>
#include "base/preferences.h" #include "base/preferences.h"
#include "base/qinisettings.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/profile.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/magneturi.h" #include "base/bittorrent/magneturi.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
@ -46,7 +48,6 @@
#include "rssarticle.h" #include "rssarticle.h"
#include "rssfolder.h" #include "rssfolder.h"
#include "rssmanager.h" #include "rssmanager.h"
#include "rssfeed.h"
namespace Rss namespace Rss
{ {
@ -99,7 +100,7 @@ void Feed::saveItemsToDisk()
m_dirty = false; m_dirty = false;
QIniSettings qBTRSSFeeds("qBittorrent", "qBittorrent-rss-feeds"); SettingsPtr qBTRSSFeeds = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss-feeds"));
QVariantList oldItems; QVariantList oldItems;
ArticleHash::ConstIterator it = m_articles.begin(); ArticleHash::ConstIterator it = m_articles.begin();
@ -107,16 +108,15 @@ void Feed::saveItemsToDisk()
for (; it != itend; ++it) for (; it != itend; ++it)
oldItems << it.value()->toHash(); oldItems << it.value()->toHash();
qDebug("Saving %d old items for feed %s", oldItems.size(), qPrintable(displayName())); qDebug("Saving %d old items for feed %s", oldItems.size(), qPrintable(displayName()));
QHash<QString, QVariant> allOldItems = qBTRSSFeeds.value("old_items", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> allOldItems = qBTRSSFeeds->value("old_items", QHash<QString, QVariant>()).toHash();
allOldItems[m_url] = oldItems; allOldItems[m_url] = oldItems;
qBTRSSFeeds.setValue("old_items", allOldItems); qBTRSSFeeds->setValue("old_items", allOldItems);
} }
void Feed::loadItemsFromDisk() void Feed::loadItemsFromDisk()
{ {
QIniSettings qBTRSSFeeds("qBittorrent", "qBittorrent-rss-feeds"); SettingsPtr qBTRSSFeeds = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss-feeds"));
QHash<QString, QVariant> allOldItems = qBTRSSFeeds.value("old_items", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> allOldItems = qBTRSSFeeds->value("old_items", QHash<QString, QVariant>()).toHash();
const QVariantList oldItems = allOldItems.value(m_url, QVariantList()).toList(); const QVariantList oldItems = allOldItems.value(m_url, QVariantList()).toList();
qDebug("Loading %d old items for feed %s", oldItems.size(), qPrintable(displayName())); qDebug("Loading %d old items for feed %s", oldItems.size(), qPrintable(displayName()));
@ -193,22 +193,22 @@ QString Feed::id() const
void Feed::removeAllSettings() void Feed::removeAllSettings()
{ {
qDebug() << "Removing all settings / history for feed: " << m_url; qDebug() << "Removing all settings / history for feed: " << m_url;
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); SettingsPtr qBTRSS = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss"));
QVariantHash feedsWDownloader = qBTRSS.value("downloader_on", QVariantHash()).toHash(); QVariantHash feedsWDownloader = qBTRSS->value("downloader_on", QVariantHash()).toHash();
if (feedsWDownloader.contains(m_url)) { if (feedsWDownloader.contains(m_url)) {
feedsWDownloader.remove(m_url); feedsWDownloader.remove(m_url);
qBTRSS.setValue("downloader_on", feedsWDownloader); qBTRSS->setValue("downloader_on", feedsWDownloader);
} }
QVariantHash allFeedsFilters = qBTRSS.value("feed_filters", QVariantHash()).toHash(); QVariantHash allFeedsFilters = qBTRSS->value("feed_filters", QVariantHash()).toHash();
if (allFeedsFilters.contains(m_url)) { if (allFeedsFilters.contains(m_url)) {
allFeedsFilters.remove(m_url); allFeedsFilters.remove(m_url);
qBTRSS.setValue("feed_filters", allFeedsFilters); qBTRSS->setValue("feed_filters", allFeedsFilters);
} }
QIniSettings qBTRSSFeeds("qBittorrent", "qBittorrent-rss-feeds"); SettingsPtr qBTRSSFeeds = Profile::instance().applicationSettings(QLatin1String("qBittorrent-rss-feeds"));
QVariantHash allOldItems = qBTRSSFeeds.value("old_items", QVariantHash()).toHash(); QVariantHash allOldItems = qBTRSSFeeds->value("old_items", QVariantHash()).toHash();
if (allOldItems.contains(m_url)) { if (allOldItems.contains(m_url)) {
allOldItems.remove(m_url); allOldItems.remove(m_url);
qBTRSSFeeds.setValue("old_items", allOldItems); qBTRSSFeeds->setValue("old_items", allOldItems);
} }
} }

View File

@ -37,6 +37,7 @@
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/profile.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h" #include "base/net/downloadhandler.h"
#include "searchengine.h" #include "searchengine.h"
@ -325,7 +326,7 @@ QString SearchEngine::engineLocation()
QString folder = "nova"; QString folder = "nova";
if (Utils::Misc::pythonVersion() >= 3) if (Utils::Misc::pythonVersion() >= 3)
folder = "nova3"; folder = "nova3";
const QString location = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + folder); const QString location = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + folder);
QDir locationDir(location); QDir locationDir(location);
if (!locationDir.exists()) if (!locationDir.exists())
locationDir.mkpath(locationDir.absolutePath()); locationDir.mkpath(locationDir.absolutePath());

View File

@ -33,9 +33,9 @@
#include <QFile> #include <QFile>
#include <QHash> #include <QHash>
#include <QStringList> #include <QStringList>
#include <QSettings>
#include "logger.h" #include "logger.h"
#include "profile.h"
#include "utils/fs.h" #include "utils/fs.h"
namespace namespace
@ -62,16 +62,6 @@ namespace
QString deserialize(const QString &name, QVariantHash &data); QString deserialize(const QString &name, QVariantHash &data);
QString serialize(const QString &name, const QVariantHash &data); QString serialize(const QString &name, const QVariantHash &data);
using SettingsPtr = std::unique_ptr<QSettings>;
SettingsPtr createSettings(const QString &name)
{
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
return SettingsPtr(new QSettings(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", name));
#else
return SettingsPtr(new QSettings("qBittorrent", name));
#endif
}
QString m_name; QString m_name;
}; };
@ -290,7 +280,7 @@ bool TransactionalSettings::write(const QVariantHash &data)
QString TransactionalSettings::deserialize(const QString &name, QVariantHash &data) QString TransactionalSettings::deserialize(const QString &name, QVariantHash &data)
{ {
SettingsPtr settings = createSettings(name); SettingsPtr settings = Profile::instance().applicationSettings(name);
if (settings->allKeys().isEmpty()) if (settings->allKeys().isEmpty())
return QString(); return QString();
@ -306,7 +296,7 @@ QString TransactionalSettings::deserialize(const QString &name, QVariantHash &da
QString TransactionalSettings::serialize(const QString &name, const QVariantHash &data) QString TransactionalSettings::serialize(const QString &name, const QVariantHash &data)
{ {
SettingsPtr settings = createSettings(name); SettingsPtr settings = Profile::instance().applicationSettings(name);
for (auto i = data.begin(); i != data.end(); ++i) for (auto i = data.begin(); i != data.end(); ++i)
settings->setValue(i.key(), i.value()); settings->setValue(i.key(), i.value());

View File

@ -28,19 +28,15 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "fs.h"
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QSettings>
#include <QDirIterator> #include <QDirIterator>
#include <QCoreApplication> #include <QCoreApplication>
#ifdef Q_OS_MAC
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>
#endif
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) #if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
#include <sys/param.h> #include <sys/param.h>
@ -51,16 +47,9 @@
#include <sys/vfs.h> #include <sys/vfs.h>
#endif #endif
#else #else
#include <shlobj.h> #include <Windows.h>
#include <winbase.h>
#endif #endif
#include <QStandardPaths>
#include "misc.h"
#include "fs.h"
/** /**
* Converts a path to a string suitable for display. * Converts a path to a string suitable for display.
* This function makes sure the directory separator used is consistent * This function makes sure the directory separator used is consistent
@ -326,85 +315,6 @@ QString Utils::Fs::expandPathAbs(const QString& path)
return ret; return ret;
} }
QString Utils::Fs::QDesktopServicesDataLocation()
{
QString result;
#if defined(Q_OS_WIN)
wchar_t path[MAX_PATH + 1] = {L'\0'};
if (SHGetSpecialFolderPathW(0, path, CSIDL_LOCAL_APPDATA, FALSE))
result = fromNativePath(QString::fromWCharArray(path));
if (!QCoreApplication::applicationName().isEmpty())
result += QLatin1String("/") + qApp->applicationName();
#elif defined(Q_OS_MAC)
FSRef ref;
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
if (err)
return QString();
QByteArray ba(2048, 0);
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
result += QLatin1Char('/') + qApp->applicationName();
#else
QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
if (xdgDataHome.isEmpty())
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
xdgDataHome += QLatin1String("/data/")
+ qApp->applicationName();
result = xdgDataHome;
#endif
if (!result.endsWith("/"))
result += "/";
return result;
}
QString Utils::Fs::QDesktopServicesCacheLocation()
{
QString result;
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
result = QDesktopServicesDataLocation() + QLatin1String("cache");
#else
#ifdef Q_OS_MAC
// http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
FSRef ref;
OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref);
if (err)
return QString();
QByteArray ba(2048, 0);
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
result += QLatin1Char('/') + qApp->applicationName();
#else
QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
if (xdgCacheHome.isEmpty())
xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName();
result = xdgCacheHome;
#endif
#endif
if (!result.endsWith("/"))
result += "/";
return result;
}
QString Utils::Fs::QDesktopServicesDownloadLocation()
{
#if defined(Q_OS_WIN)
if (QSysInfo::windowsVersion() <= QSysInfo::WV_XP) // Windows XP
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
#endif
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
}
QString Utils::Fs::cacheLocation()
{
QString location = expandPathAbs(QDesktopServicesCacheLocation());
QDir locationDir(location);
if (!locationDir.exists())
locationDir.mkpath(locationDir.absolutePath());
return location;
}
QString Utils::Fs::tempPath() QString Utils::Fs::tempPath()
{ {
static const QString path = QDir::tempPath() + "/.qBittorrent/"; static const QString path = QDir::tempPath() + "/.qBittorrent/";

View File

@ -60,13 +60,6 @@ namespace Utils
bool forceRemove(const QString& file_path); bool forceRemove(const QString& file_path);
void removeDirRecursive(const QString& dirName); void removeDirRecursive(const QString& dirName);
/* Ported from Qt4 to drop dependency on QtGui */
QString QDesktopServicesDataLocation();
QString QDesktopServicesCacheLocation();
QString QDesktopServicesDownloadLocation();
/* End of Qt4 code */
QString cacheLocation();
QString tempPath(); QString tempPath();
} }
} }

View File

@ -35,7 +35,6 @@
#include <QByteArray> #include <QByteArray>
#include <QDebug> #include <QDebug>
#include <QProcess> #include <QProcess>
#include <QSettings>
#include <QThread> #include <QThread>
#include <QSysInfo> #include <QSysInfo>
#include <boost/version.hpp> #include <boost/version.hpp>

View File

@ -10,14 +10,14 @@
#include <QDateTime> #include <QDateTime>
#include <QScrollBar> #include <QScrollBar>
#include "base/utils/fs.h" #include "base/profile.h"
HtmlBrowser::HtmlBrowser(QWidget* parent) HtmlBrowser::HtmlBrowser(QWidget* parent)
: QTextBrowser(parent) : QTextBrowser(parent)
{ {
m_netManager = new QNetworkAccessManager(this); m_netManager = new QNetworkAccessManager(this);
m_diskCache = new QNetworkDiskCache(this); m_diskCache = new QNetworkDiskCache(this);
m_diskCache->setCacheDirectory(QDir::cleanPath(Utils::Fs::cacheLocation() + "/rss")); m_diskCache->setCacheDirectory(QDir::cleanPath(specialFolderLocation(SpecialFolder::Cache) + "/rss"));
m_diskCache->setMaximumCacheSize(50 * 1024 * 1024); m_diskCache->setMaximumCacheSize(50 * 1024 * 1024);
qDebug() << "HtmlBrowser cache path:" << m_diskCache->cacheDirectory() << " max size:" << m_diskCache->maximumCacheSize() / 1024 / 1024 << "MB"; qDebug() << "HtmlBrowser cache path:" << m_diskCache->cacheDirectory() << " max size:" << m_diskCache->maximumCacheSize() / 1024 / 1024 << "MB";
m_netManager->setCache(m_diskCache); m_netManager->setCache(m_diskCache);