Browse Source

Save relative paths in fastresume files

Conditionally change absolute paths to relative in the fastresume data files.
The condition is specified by user via a command line parameter and
paths are relative to the profile dir.

On Windows the convertion to relative path is performed if the path and
the profile are on the same drive only.
adaptive-webui-19844
Eugene Shalygin 9 years ago
parent
commit
a8d95dd8bd
  1. 6
      src/app/application.cpp
  2. 11
      src/app/options.cpp
  3. 1
      src/app/options.h
  4. 3
      src/base/bittorrent/session.cpp
  5. 7
      src/base/bittorrent/torrenthandle.cpp
  6. 41
      src/base/private/profile_p.cpp
  7. 26
      src/base/private/profile_p.h
  8. 41
      src/base/profile.cpp
  9. 12
      src/base/profile.h

6
src/app/application.cpp

@ -111,7 +111,8 @@ Application::Application(const QString &id, int &argc, char **argv)
? QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(DEFAULT_PORTABLE_MODE_PROFILE_DIR) ? QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(DEFAULT_PORTABLE_MODE_PROFILE_DIR)
: m_commandLineArgs.profileDir; : m_commandLineArgs.profileDir;
Profile::initialize(profileDir, m_commandLineArgs.configurationName); Profile::initialize(profileDir, m_commandLineArgs.configurationName,
m_commandLineArgs.relativeFastresumePaths || m_commandLineArgs.portableMode);
Logger::initInstance(); Logger::initInstance();
SettingsStorage::initInstance(); SettingsStorage::initInstance();
@ -660,4 +661,7 @@ void Application::validateCommandLineParameters()
{ {
if (m_commandLineArgs.portableMode && !m_commandLineArgs.profileDir.isEmpty()) if (m_commandLineArgs.portableMode && !m_commandLineArgs.profileDir.isEmpty())
throw CommandLineParameterError(tr("Portable mode and explicit profile directory options are mutually exclusive")); 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);
} }

11
src/app/options.cpp

@ -246,6 +246,7 @@ namespace
constexpr const StringOption PROFILE_OPTION = {"profile"}; constexpr const StringOption PROFILE_OPTION = {"profile"};
constexpr const StringOption CONFIGURATION_OPTION = {"configuration"}; constexpr const StringOption CONFIGURATION_OPTION = {"configuration"};
constexpr const BoolOption PORTABLE_OPTION = {"portable"}; constexpr const BoolOption PORTABLE_OPTION = {"portable"};
constexpr const BoolOption RELATIVE_FASTRESUME = {"relative-fastresume"};
} }
QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env) QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &env)
@ -260,6 +261,7 @@ QBtCommandLineParameters::QBtCommandLineParameters(const QProcessEnvironment &en
#endif #endif
, webUiPort(WEBUI_PORT_OPTION.value(env, -1)) , webUiPort(WEBUI_PORT_OPTION.value(env, -1))
, profileDir(PROFILE_OPTION.value(env)) , profileDir(PROFILE_OPTION.value(env))
, relativeFastresumePaths(RELATIVE_FASTRESUME.value(env))
, portableMode(PORTABLE_OPTION.value(env)) , portableMode(PORTABLE_OPTION.value(env))
, configurationName(CONFIGURATION_OPTION.value(env)) , configurationName(CONFIGURATION_OPTION.value(env))
{ {
@ -301,6 +303,9 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
else if (arg == PROFILE_OPTION) { else if (arg == PROFILE_OPTION) {
result.profileDir = PROFILE_OPTION.value(arg); result.profileDir = PROFILE_OPTION.value(arg);
} }
else if (arg == RELATIVE_FASTRESUME) {
result.relativeFastresumePaths = true;
}
else if (arg == PORTABLE_OPTION) { else if (arg == PORTABLE_OPTION) {
result.portableMode = true; result.portableMode = true;
} }
@ -362,9 +367,9 @@ QString makeUsage(const QString &prgName)
stream << DAEMON_OPTION.usage() << QObject::tr("Run in daemon-mode (background)") << '\n'; stream << DAEMON_OPTION.usage() << QObject::tr("Run in daemon-mode (background)") << '\n';
#endif #endif
stream << PROFILE_OPTION.usage(QLatin1String("dir")) << QObject::tr("Store configuration files in <dir>") << '\n'; stream << PROFILE_OPTION.usage(QLatin1String("dir")) << QObject::tr("Store configuration files in <dir>") << '\n';
stream << PORTABLE_OPTION.usage() << QObject::tr("Shortcut for --profile=<exe dir>/profile") << '\n'; stream << CONFIGURATION_OPTION.usage(QLatin1String("name")) << QObject::tr("Store configuration files in directories qBittorrent_<name>") << '\n';
stream << CONFIGURATION_OPTION.usage(QLatin1String("name")) << QObject::tr("Store configuration files in directories qBittorrent_<name>") stream << RELATIVE_FASTRESUME.usage() << QObject::tr("Hack into libtorrent fastresume files and make file paths relative to the profile directory") << '\n';
<< '\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' stream << "\tfiles or urls\t\t" << QObject::tr("Downloads the torrents passed by the user") << '\n'
<< '\n'; << '\n';

1
src/app/options.h

@ -53,6 +53,7 @@ struct QBtCommandLineParameters
#endif #endif
int webUiPort; int webUiPort;
QString profileDir; QString profileDir;
bool relativeFastresumePaths;
bool portableMode; bool portableMode;
QString configurationName; QString configurationName;
QStringList torrents; QStringList torrents;

3
src/base/bittorrent/session.cpp

@ -3633,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

7
src/base/bittorrent/torrenthandle.cpp

@ -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();

41
src/base/private/profile_p.cpp

@ -207,3 +207,44 @@ SettingsPtr Private::CustomProfile::applicationSettings(const QString &name) con
const QString settingsFileName {QDir(configLocation()).absoluteFilePath(name + QLatin1String(CONF_FILE_EXTENSION))}; const QString settingsFileName {QDir(configLocation()).absoluteFilePath(name + QLatin1String(CONF_FILE_EXTENSION))};
return SettingsPtr(new QSettings(settingsFileName, QSettings::IniFormat)); 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));
}

26
src/base/private/profile_p.h

@ -91,5 +91,31 @@ namespace Private
static constexpr const char *dataDirName = "data"; static constexpr const char *dataDirName = "data";
static constexpr const char *downloadsDirName = "downloads"; 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 #endif // QBT_PROFILE_P_H

41
src/base/profile.cpp

@ -36,8 +36,9 @@
Profile *Profile::m_instance = nullptr; Profile *Profile::m_instance = nullptr;
Profile::Profile(Private::Profile *impl) Profile::Profile(Private::Profile *impl, Private::PathConverter *pathConverter)
: m_impl(impl) : m_profileImpl(impl)
, m_pathConverterImpl(pathConverter)
{ {
ensureDirectoryExists(SpecialFolder::Cache); ensureDirectoryExists(SpecialFolder::Cache);
ensureDirectoryExists(SpecialFolder::Config); ensureDirectoryExists(SpecialFolder::Config);
@ -48,11 +49,17 @@ Profile::Profile(Private::Profile *impl)
// to generate correct call to ProfilePrivate::~ProfileImpl() // to generate correct call to ProfilePrivate::~ProfileImpl()
Profile::~Profile() = default; Profile::~Profile() = default;
void Profile::initialize(const QString &rootProfilePath, const QString &configurationName) void Profile::initialize(const QString &rootProfilePath, const QString &configurationName,
bool convertPathsToProfileRelative)
{ {
m_instance = new Profile(rootProfilePath.isEmpty() QScopedPointer<Private::Profile> profile(rootProfilePath.isEmpty()
? static_cast<Private::Profile *>(new Private::DefaultProfile(configurationName)) ? static_cast<Private::Profile *>(new Private::DefaultProfile(configurationName))
: static_cast<Private::Profile *>(new Private::CustomProfile(rootProfilePath, 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() const Profile &Profile::instance()
@ -65,16 +72,16 @@ QString Profile::location(SpecialFolder folder) const
QString result; QString result;
switch (folder) { switch (folder) {
case SpecialFolder::Cache: case SpecialFolder::Cache:
result = m_impl->cacheLocation(); result = m_profileImpl->cacheLocation();
break; break;
case SpecialFolder::Config: case SpecialFolder::Config:
result = m_impl->configLocation(); result = m_profileImpl->configLocation();
break; break;
case SpecialFolder::Data: case SpecialFolder::Data:
result = m_impl->dataLocation(); result = m_profileImpl->dataLocation();
break; break;
case SpecialFolder::Downloads: case SpecialFolder::Downloads:
result = m_impl->downloadLocation(); result = m_profileImpl->downloadLocation();
break; break;
} }
@ -85,12 +92,12 @@ QString Profile::location(SpecialFolder folder) const
QString Profile::configurationName() const QString Profile::configurationName() const
{ {
return m_impl->configurationName(); return m_profileImpl->configurationName();
} }
SettingsPtr Profile::applicationSettings(const QString &name) const SettingsPtr Profile::applicationSettings(const QString &name) const
{ {
return m_impl->applicationSettings(name); return m_profileImpl->applicationSettings(name);
} }
void Profile::ensureDirectoryExists(SpecialFolder folder) void Profile::ensureDirectoryExists(SpecialFolder folder)
@ -99,3 +106,13 @@ void Profile::ensureDirectoryExists(SpecialFolder folder)
if (!locationPath.isEmpty() && !QDir().mkpath(locationPath)) if (!locationPath.isEmpty() && !QDir().mkpath(locationPath))
qFatal("Could not create required directory '%s'", qPrintable(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);
}

12
src/base/profile.h

@ -42,6 +42,7 @@ class Application;
namespace Private namespace Private
{ {
class Profile; class Profile;
class PathConverter;
} }
using SettingsPtr = std::unique_ptr<QSettings>; using SettingsPtr = std::unique_ptr<QSettings>;
@ -64,17 +65,22 @@ public:
/// or the value, supplied via parameters /// or the value, supplied via parameters
QString configurationName() const; QString configurationName() const;
QString toPortablePath(const QString &absolutePath) const;
QString fromPortablePath(const QString &portablePath) const;
static const Profile &instance(); static const Profile &instance();
private: private:
Profile(Private::Profile *impl); Profile(Private::Profile *impl, Private::PathConverter *pathConverter);
~Profile(); ~Profile();
friend class ::Application; friend class ::Application;
static void initialize(const QString &rootProfilePath, const QString &configurationName); static void initialize(const QString &rootProfilePath, const QString &configurationName,
bool convertPathsToProfileRelative);
void ensureDirectoryExists(SpecialFolder folder); void ensureDirectoryExists(SpecialFolder folder);
QScopedPointer<Private::Profile> m_impl; QScopedPointer<Private::Profile> m_profileImpl;
QScopedPointer<Private::PathConverter> m_pathConverterImpl;
static Profile *m_instance; static Profile *m_instance;
}; };

Loading…
Cancel
Save