diff --git a/src/base/bittorrent/bencoderesumedatastorage.cpp b/src/base/bittorrent/bencoderesumedatastorage.cpp index 1b6adfc96..1e1083051 100644 --- a/src/base/bittorrent/bencoderesumedatastorage.cpp +++ b/src/base/bittorrent/bencoderesumedatastorage.cpp @@ -290,6 +290,8 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre lt::add_torrent_params &p = torrentParams.ltAddTorrentParams; p = lt::read_resume_data(resumeDataRoot, ec); + if (ec) + return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message()))); if (!metadata.isEmpty()) { @@ -320,6 +322,8 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre p.save_path = Profile::instance()->fromPortablePath( Path(fromLTString(p.save_path))).toString().toStdString(); + if (p.save_path.empty()) + return nonstd::make_unexpected(tr("Corrupted resume data: %1").arg(tr("save_path is invalid"))); torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed); torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed) diff --git a/src/base/bittorrent/dbresumedatastorage.cpp b/src/base/bittorrent/dbresumedatastorage.cpp index d9320e528..f9cc18769 100644 --- a/src/base/bittorrent/dbresumedatastorage.cpp +++ b/src/base/bittorrent/dbresumedatastorage.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2021-2023 Vladimir Golovnev + * Copyright (C) 2021-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -217,80 +217,6 @@ namespace { return u"%1 %2"_s.arg(quoted(column.name), definition); } - - LoadTorrentParams parseQueryResultRow(const QSqlQuery &query) - { - LoadTorrentParams resumeData; - resumeData.name = query.value(DB_COLUMN_NAME.name).toString(); - resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString(); - const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString(); - if (!tagsData.isEmpty()) - { - const QStringList tagList = tagsData.split(u','); - resumeData.tags.insert(tagList.cbegin(), tagList.cend()); - } - resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool(); - resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool(); - resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0; - resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt(); - resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt(); - resumeData.shareLimitAction = Utils::String::toEnum( - query.value(DB_COLUMN_SHARE_LIMIT_ACTION.name).toString(), ShareLimitAction::Default); - resumeData.contentLayout = Utils::String::toEnum( - query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original); - resumeData.operatingMode = Utils::String::toEnum( - query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged); - resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool(); - resumeData.stopCondition = Utils::String::toEnum( - query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None); - resumeData.sslParameters = - { - .certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()), - .privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()), - .dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray() - }; - - resumeData.savePath = Profile::instance()->fromPortablePath( - Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString())); - resumeData.useAutoTMM = resumeData.savePath.isEmpty(); - if (!resumeData.useAutoTMM) - { - resumeData.downloadPath = Profile::instance()->fromPortablePath( - Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString())); - } - - const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray(); - const auto *pref = Preferences::instance(); - const int bdecodeDepthLimit = pref->getBdecodeDepthLimit(); - const int bdecodeTokenLimit = pref->getBdecodeTokenLimit(); - - lt::error_code ec; - const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec - , nullptr, bdecodeDepthLimit, bdecodeTokenLimit); - - lt::add_torrent_params &p = resumeData.ltAddTorrentParams; - - p = lt::read_resume_data(resumeDataRoot, ec); - - if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray() - ; !bencodedMetadata.isEmpty()) - { - const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec - , nullptr, bdecodeDepthLimit, bdecodeTokenLimit); - p.ti = std::make_shared(torentInfoRoot, ec); - } - - p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path))) - .toString().toStdString(); - - if (p.flags & lt::torrent_flags::stop_when_ready) - { - p.flags &= ~lt::torrent_flags::stop_when_ready; - resumeData.stopCondition = Torrent::StopCondition::FilesChecked; - } - - return resumeData; - } } namespace BitTorrent @@ -688,6 +614,90 @@ void BitTorrent::DBResumeDataStorage::enableWALMode() const throw RuntimeError(tr("WAL mode is probably unsupported due to filesystem limitations.")); } +LoadResumeDataResult DBResumeDataStorage::parseQueryResultRow(const QSqlQuery &query) const +{ + LoadTorrentParams resumeData; + resumeData.name = query.value(DB_COLUMN_NAME.name).toString(); + resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString(); + const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString(); + if (!tagsData.isEmpty()) + { + const QStringList tagList = tagsData.split(u','); + resumeData.tags.insert(tagList.cbegin(), tagList.cend()); + } + resumeData.hasFinishedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool(); + resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool(); + resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0; + resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt(); + resumeData.inactiveSeedingTimeLimit = query.value(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name).toInt(); + resumeData.shareLimitAction = Utils::String::toEnum( + query.value(DB_COLUMN_SHARE_LIMIT_ACTION.name).toString(), ShareLimitAction::Default); + resumeData.contentLayout = Utils::String::toEnum( + query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original); + resumeData.operatingMode = Utils::String::toEnum( + query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged); + resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool(); + resumeData.stopCondition = Utils::String::toEnum( + query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None); + resumeData.sslParameters = + { + .certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()), + .privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()), + .dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray() + }; + + resumeData.savePath = Profile::instance()->fromPortablePath( + Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString())); + resumeData.useAutoTMM = resumeData.savePath.isEmpty(); + if (!resumeData.useAutoTMM) + { + resumeData.downloadPath = Profile::instance()->fromPortablePath( + Path(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString())); + } + + const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray(); + const auto *pref = Preferences::instance(); + const int bdecodeDepthLimit = pref->getBdecodeDepthLimit(); + const int bdecodeTokenLimit = pref->getBdecodeTokenLimit(); + + lt::error_code ec; + const lt::bdecode_node resumeDataRoot = lt::bdecode(bencodedResumeData, ec, nullptr, bdecodeDepthLimit, bdecodeTokenLimit); + if (ec) + return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message()))); + + lt::add_torrent_params &p = resumeData.ltAddTorrentParams; + + p = lt::read_resume_data(resumeDataRoot, ec); + if (ec) + return nonstd::make_unexpected(tr("Cannot parse resume data: %1").arg(QString::fromStdString(ec.message()))); + + if (const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray() + ; !bencodedMetadata.isEmpty()) + { + const lt::bdecode_node torentInfoRoot = lt::bdecode(bencodedMetadata, ec + , nullptr, bdecodeDepthLimit, bdecodeTokenLimit); + if (ec) + return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message()))); + + p.ti = std::make_shared(torentInfoRoot, ec); + if (ec) + return nonstd::make_unexpected(tr("Cannot parse torrent info: %1").arg(QString::fromStdString(ec.message()))); + } + + p.save_path = Profile::instance()->fromPortablePath(Path(fromLTString(p.save_path))) + .toString().toStdString(); + if (p.save_path.empty()) + return nonstd::make_unexpected(tr("Corrupted resume data: %1").arg(tr("save_path is invalid"))); + + if (p.flags & lt::torrent_flags::stop_when_ready) + { + p.flags &= ~lt::torrent_flags::stop_when_ready; + resumeData.stopCondition = Torrent::StopCondition::FilesChecked; + } + + return resumeData; +} + BitTorrent::DBResumeDataStorage::Worker::Worker(const Path &dbPath, QReadWriteLock &dbLock, QObject *parent) : QThread(parent) , m_path {dbPath} diff --git a/src/base/bittorrent/dbresumedatastorage.h b/src/base/bittorrent/dbresumedatastorage.h index 92f0e6ea1..a7a97ab25 100644 --- a/src/base/bittorrent/dbresumedatastorage.h +++ b/src/base/bittorrent/dbresumedatastorage.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2021-2022 Vladimir Golovnev + * Copyright (C) 2021-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,9 +31,10 @@ #include #include "base/pathfwd.h" -#include "base/utils/thread.h" #include "resumedatastorage.h" +class QSqlQuery; + namespace BitTorrent { class DBResumeDataStorage final : public ResumeDataStorage @@ -58,6 +59,7 @@ namespace BitTorrent void createDB() const; void updateDB(int fromVersion) const; void enableWALMode() const; + LoadResumeDataResult parseQueryResultRow(const QSqlQuery &query) const; class Worker; Worker *m_asyncWorker = nullptr;