|
|
@ -97,10 +97,6 @@ |
|
|
|
#include "tracker.h" |
|
|
|
#include "tracker.h" |
|
|
|
#include "trackerentry.h" |
|
|
|
#include "trackerentry.h" |
|
|
|
|
|
|
|
|
|
|
|
#if defined(Q_OS_WIN) && (_WIN32_WINNT < 0x0600) |
|
|
|
|
|
|
|
using NETIO_STATUS = LONG; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char PEER_ID[] = "qB"; |
|
|
|
static const char PEER_ID[] = "qB"; |
|
|
|
static const char RESUME_FOLDER[] = "BT_backup"; |
|
|
|
static const char RESUME_FOLDER[] = "BT_backup"; |
|
|
|
static const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2; |
|
|
|
static const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2; |
|
|
@ -125,17 +121,122 @@ namespace |
|
|
|
using LTString = lt::string_view; |
|
|
|
using LTString = lt::string_view; |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
bool readFile(const QString &path, QByteArray &buf); |
|
|
|
template <typename LTStr> |
|
|
|
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &queuePos, MagnetUri &magnetUri); |
|
|
|
QString fromLTString(const LTStr &str) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return QString::fromUtf8(str.data(), static_cast<int>(str.size())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionUp(const lt::torrent_handle &handle); |
|
|
|
bool readFile(const QString &path, QByteArray &buf) |
|
|
|
void torrentQueuePositionDown(const lt::torrent_handle &handle); |
|
|
|
{ |
|
|
|
void torrentQueuePositionTop(const lt::torrent_handle &handle); |
|
|
|
QFile file(path); |
|
|
|
void torrentQueuePositionBottom(const lt::torrent_handle &handle); |
|
|
|
if (!file.open(QIODevice::ReadOnly)) { |
|
|
|
|
|
|
|
qDebug("Cannot read file %s: %s", qUtf8Printable(path), qUtf8Printable(file.errorString())); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN |
|
|
|
buf = file.readAll(); |
|
|
|
QString convertIfaceNameToGuid(const QString &name); |
|
|
|
return true; |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &queuePos, MagnetUri &magnetUri) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
lt::error_code ec; |
|
|
|
|
|
|
|
lt::bdecode_node root; |
|
|
|
|
|
|
|
lt::bdecode(data.constData(), (data.constData() + data.size()), root, ec); |
|
|
|
|
|
|
|
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
torrentParams = CreateTorrentParams(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
torrentParams.restored = true; |
|
|
|
|
|
|
|
torrentParams.skipChecking = false; |
|
|
|
|
|
|
|
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name")); |
|
|
|
|
|
|
|
torrentParams.savePath = Profile::instance().fromPortablePath( |
|
|
|
|
|
|
|
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath")))); |
|
|
|
|
|
|
|
torrentParams.disableTempPath = root.dict_find_int_value("qBt-tempPathDisabled"); |
|
|
|
|
|
|
|
torrentParams.sequential = root.dict_find_int_value("qBt-sequential"); |
|
|
|
|
|
|
|
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus"); |
|
|
|
|
|
|
|
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority"); |
|
|
|
|
|
|
|
torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder"); |
|
|
|
|
|
|
|
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const bool isAutoManaged = root.dict_find_int_value("auto_managed"); |
|
|
|
|
|
|
|
const bool isPaused = root.dict_find_int_value("paused"); |
|
|
|
|
|
|
|
torrentParams.paused = root.dict_find_int_value("qBt-paused", (isPaused && !isAutoManaged)); |
|
|
|
|
|
|
|
torrentParams.forced = root.dict_find_int_value("qBt-forced", (!isPaused && !isAutoManaged)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const LTString ratioLimitString = root.dict_find_string_value("qBt-ratioLimit"); |
|
|
|
|
|
|
|
if (ratioLimitString.empty()) |
|
|
|
|
|
|
|
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// **************************************************************************************
|
|
|
|
|
|
|
|
// Workaround to convert legacy label to category
|
|
|
|
|
|
|
|
// TODO: Should be removed in future
|
|
|
|
|
|
|
|
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-label")); |
|
|
|
|
|
|
|
if (torrentParams.category.isEmpty()) |
|
|
|
|
|
|
|
// **************************************************************************************
|
|
|
|
|
|
|
|
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags"); |
|
|
|
|
|
|
|
if (tagsNode.type() == lt::bdecode_node::list_t) { |
|
|
|
|
|
|
|
for (int i = 0; i < tagsNode.list_size(); ++i) { |
|
|
|
|
|
|
|
const QString tag = fromLTString(tagsNode.list_string_value_at(i)); |
|
|
|
|
|
|
|
if (Session::isValidTag(tag)) |
|
|
|
|
|
|
|
torrentParams.tags << tag; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lt::bdecode_node addedTimeNode = root.dict_find("qBt-addedTime"); |
|
|
|
|
|
|
|
if (addedTimeNode.type() == lt::bdecode_node::int_t) |
|
|
|
|
|
|
|
torrentParams.addedTime = QDateTime::fromSecsSinceEpoch(addedTimeNode.int_value()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queuePos = root.dict_find_int_value("qBt-queuePosition"); |
|
|
|
|
|
|
|
magnetUri = MagnetUri(fromLTString(root.dict_find_string_value("qBt-magnetUri"))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionUp(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_up(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionDown(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_down(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionTop(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_top(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionBottom(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_bottom(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
QStringMap map_cast(const QVariantMap &map) |
|
|
|
QStringMap map_cast(const QVariantMap &map) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -153,12 +254,6 @@ namespace |
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename LTStr> |
|
|
|
|
|
|
|
QString fromLTString(const LTStr &str) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return QString::fromUtf8(str.data(), static_cast<int>(str.size())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QString normalizePath(const QString &path) |
|
|
|
QString normalizePath(const QString &path) |
|
|
|
{ |
|
|
|
{ |
|
|
|
QString tmp = Utils::Fs::toUniformPath(path.trimmed()); |
|
|
|
QString tmp = Utils::Fs::toUniformPath(path.trimmed()); |
|
|
@ -234,6 +329,26 @@ namespace |
|
|
|
return value; |
|
|
|
return value; |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN |
|
|
|
|
|
|
|
QString convertIfaceNameToGuid(const QString &name) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Under Windows XP or on Qt version <= 5.5 'name' will be a GUID already.
|
|
|
|
|
|
|
|
const QUuid uuid(name); |
|
|
|
|
|
|
|
if (!uuid.isNull()) |
|
|
|
|
|
|
|
return uuid.toString().toUpper(); // Libtorrent expects the GUID in uppercase
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NET_LUID luid {}; |
|
|
|
|
|
|
|
const LONG res = ::ConvertInterfaceNameToLuidW(name.toStdWString().c_str(), &luid); |
|
|
|
|
|
|
|
if (res == 0) { |
|
|
|
|
|
|
|
GUID guid; |
|
|
|
|
|
|
|
if (::ConvertInterfaceLuidToGuid(&luid, &guid) == 0) |
|
|
|
|
|
|
|
return QUuid(guid).toString().toUpper(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Session
|
|
|
|
// Session
|
|
|
@ -4478,137 +4593,3 @@ void Session::handleStateUpdateAlert(const lt::state_update_alert *p) |
|
|
|
if (!updatedTorrents.isEmpty()) |
|
|
|
if (!updatedTorrents.isEmpty()) |
|
|
|
emit torrentsUpdated(updatedTorrents); |
|
|
|
emit torrentsUpdated(updatedTorrents); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
namespace |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
bool readFile(const QString &path, QByteArray &buf) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
QFile file(path); |
|
|
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) { |
|
|
|
|
|
|
|
qDebug("Cannot read file %s: %s", qUtf8Printable(path), qUtf8Printable(file.errorString())); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
buf = file.readAll(); |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &queuePos, MagnetUri &magnetUri) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
lt::error_code ec; |
|
|
|
|
|
|
|
lt::bdecode_node root; |
|
|
|
|
|
|
|
lt::bdecode(data.constData(), (data.constData() + data.size()), root, ec); |
|
|
|
|
|
|
|
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
torrentParams = CreateTorrentParams(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
torrentParams.restored = true; |
|
|
|
|
|
|
|
torrentParams.skipChecking = false; |
|
|
|
|
|
|
|
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name")); |
|
|
|
|
|
|
|
torrentParams.savePath = Profile::instance().fromPortablePath( |
|
|
|
|
|
|
|
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath")))); |
|
|
|
|
|
|
|
torrentParams.disableTempPath = root.dict_find_int_value("qBt-tempPathDisabled"); |
|
|
|
|
|
|
|
torrentParams.sequential = root.dict_find_int_value("qBt-sequential"); |
|
|
|
|
|
|
|
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus"); |
|
|
|
|
|
|
|
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority"); |
|
|
|
|
|
|
|
torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder"); |
|
|
|
|
|
|
|
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const bool isAutoManaged = root.dict_find_int_value("auto_managed"); |
|
|
|
|
|
|
|
const bool isPaused = root.dict_find_int_value("paused"); |
|
|
|
|
|
|
|
torrentParams.paused = root.dict_find_int_value("qBt-paused", (isPaused && !isAutoManaged)); |
|
|
|
|
|
|
|
torrentParams.forced = root.dict_find_int_value("qBt-forced", (!isPaused && !isAutoManaged)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const LTString ratioLimitString = root.dict_find_string_value("qBt-ratioLimit"); |
|
|
|
|
|
|
|
if (ratioLimitString.empty()) |
|
|
|
|
|
|
|
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// **************************************************************************************
|
|
|
|
|
|
|
|
// Workaround to convert legacy label to category
|
|
|
|
|
|
|
|
// TODO: Should be removed in future
|
|
|
|
|
|
|
|
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-label")); |
|
|
|
|
|
|
|
if (torrentParams.category.isEmpty()) |
|
|
|
|
|
|
|
// **************************************************************************************
|
|
|
|
|
|
|
|
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags"); |
|
|
|
|
|
|
|
if (tagsNode.type() == lt::bdecode_node::list_t) { |
|
|
|
|
|
|
|
for (int i = 0; i < tagsNode.list_size(); ++i) { |
|
|
|
|
|
|
|
const QString tag = fromLTString(tagsNode.list_string_value_at(i)); |
|
|
|
|
|
|
|
if (Session::isValidTag(tag)) |
|
|
|
|
|
|
|
torrentParams.tags << tag; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const lt::bdecode_node addedTimeNode = root.dict_find("qBt-addedTime"); |
|
|
|
|
|
|
|
if (addedTimeNode.type() == lt::bdecode_node::int_t) |
|
|
|
|
|
|
|
torrentParams.addedTime = QDateTime::fromSecsSinceEpoch(addedTimeNode.int_value()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queuePos = root.dict_find_int_value("qBt-queuePosition"); |
|
|
|
|
|
|
|
magnetUri = MagnetUri(fromLTString(root.dict_find_string_value("qBt-magnetUri"))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionUp(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_up(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionDown(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_down(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionTop(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_top(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void torrentQueuePositionBottom(const lt::torrent_handle &handle) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
handle.queue_position_bottom(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (const std::exception &exc) { |
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN |
|
|
|
|
|
|
|
QString convertIfaceNameToGuid(const QString &name) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Under Windows XP or on Qt version <= 5.5 'name' will be a GUID already.
|
|
|
|
|
|
|
|
const QUuid uuid(name); |
|
|
|
|
|
|
|
if (!uuid.isNull()) |
|
|
|
|
|
|
|
return uuid.toString().toUpper(); // Libtorrent expects the GUID in uppercase
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NET_LUID luid {}; |
|
|
|
|
|
|
|
const LONG res = ::ConvertInterfaceNameToLuidW(name.toStdWString().c_str(), &luid); |
|
|
|
|
|
|
|
if (res == 0) { |
|
|
|
|
|
|
|
GUID guid; |
|
|
|
|
|
|
|
if (::ConvertInterfaceLuidToGuid(&luid, &guid) == 0) |
|
|
|
|
|
|
|
return QUuid(guid).toString().toUpper(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|