Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WebAPI for managing torrent WebSeeds #21043

Merged
merged 1 commit into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/webui/api/torrentscontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ namespace
using Utils::String::parseInt;
using Utils::String::parseDouble;

const QSet<QString> SUPPORTED_WEB_SEED_SCHEMES {u"http"_s, u"https"_s, u"ftp"_s};

void applyToTorrents(const QStringList &idList, const std::function<void (BitTorrent::Torrent *torrent)> &func)
{
if ((idList.size() == 1) && (idList[0] == u"all"))
Expand Down Expand Up @@ -252,6 +254,20 @@ namespace
idList << BitTorrent::TorrentID::fromString(hash);
return idList;
}

nonstd::expected<QUrl, QString> validateWebSeedUrl(const QString &urlStr)
{
const QString normalizedUrlStr = QUrl::fromPercentEncoding(urlStr.toLatin1());

const QUrl url {normalizedUrlStr, QUrl::StrictMode};
if (!url.isValid())
return nonstd::make_unexpected(TorrentsController::tr("\"%1\" is not a valid URL").arg(normalizedUrlStr));

if (!SUPPORTED_WEB_SEED_SCHEMES.contains(url.scheme()))
return nonstd::make_unexpected(TorrentsController::tr("URL scheme must be one of [%1]").arg(SUPPORTED_WEB_SEED_SCHEMES.values().join(u", ")));

return url;
}
}

void TorrentsController::countAction()
Expand Down Expand Up @@ -560,6 +576,84 @@ void TorrentsController::webseedsAction()
setResult(webSeedList);
}

void TorrentsController::addWebSeedsAction()
{
requireParams({u"hash"_s, u"urls"_s});
const QStringList paramUrls = params()[u"urls"_s].split(u'|', Qt::SkipEmptyParts);

const auto id = BitTorrent::TorrentID::fromString(params()[u"hash"_s]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->getTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);

QList<QUrl> urls;
urls.reserve(paramUrls.size());
for (const QString &urlStr : paramUrls)
{
const auto result = validateWebSeedUrl(urlStr);
if (!result)
throw APIError(APIErrorType::BadParams, result.error());
urls << result.value();
}

torrent->addUrlSeeds(urls);
}

void TorrentsController::editWebSeedAction()
{
requireParams({u"hash"_s, u"origUrl"_s, u"newUrl"_s});

const auto id = BitTorrent::TorrentID::fromString(params()[u"hash"_s]);
const QString origUrlStr = params()[u"origUrl"_s];
const QString newUrlStr = params()[u"newUrl"_s];

BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->getTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);

const auto origUrlResult = validateWebSeedUrl(origUrlStr);
if (!origUrlResult)
throw APIError(APIErrorType::BadParams, origUrlResult.error());
const QUrl origUrl = origUrlResult.value();

const auto newUrlResult = validateWebSeedUrl(newUrlStr);
if (!newUrlResult)
throw APIError(APIErrorType::BadParams, newUrlResult.error());
const QUrl newUrl = newUrlResult.value();

if (newUrl != origUrl)
{
if (!torrent->urlSeeds().contains(origUrl))
throw APIError(APIErrorType::Conflict, tr("\"%1\" is not an existing URL").arg(origUrl.toString()));

torrent->removeUrlSeeds({origUrl});
torrent->addUrlSeeds({newUrl});
}
}

void TorrentsController::removeWebSeedsAction()
{
requireParams({u"hash"_s, u"urls"_s});
const QStringList paramUrls = params()[u"urls"_s].split(u'|', Qt::SkipEmptyParts);

const auto id = BitTorrent::TorrentID::fromString(params()[u"hash"_s]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->getTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);

QList<QUrl> urls;
urls.reserve(paramUrls.size());
for (const QString &urlStr : paramUrls)
{
const auto result = validateWebSeedUrl(urlStr);
if (!result)
throw APIError(APIErrorType::BadParams, result.error());
urls << result.value();
}

torrent->removeUrlSeeds(urls);
}

// Returns the files in a torrent in JSON format.
// The return value is a JSON-formatted list of dictionaries.
// The dictionary keys are:
Expand Down
3 changes: 3 additions & 0 deletions src/webui/api/torrentscontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ private slots:
void propertiesAction();
void trackersAction();
void webseedsAction();
void addWebSeedsAction();
void editWebSeedAction();
void removeWebSeedsAction();
void filesAction();
void pieceHashesAction();
void pieceStatesAction();
Expand Down
3 changes: 3 additions & 0 deletions src/webui/webapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class WebApplication final : public ApplicationComponent<QObject>
{{u"torrents"_s, u"addPeers"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"addTags"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"addTrackers"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"addWebSeeds"_s}, Http::METHOD_POST},
{{u"transfer"_s, u"banPeers"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"bottomPrio"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"createCategory"_s}, Http::METHOD_POST},
Expand All @@ -186,13 +187,15 @@ class WebApplication final : public ApplicationComponent<QObject>
{{u"torrents"_s, u"deleteTags"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"editCategory"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"editTracker"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"editWebSeed"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"filePrio"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"increasePrio"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"reannounce"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"recheck"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"removeCategories"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"removeTags"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"removeTrackers"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"removeWebSeeds"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"rename"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"renameFile"_s}, Http::METHOD_POST},
{{u"torrents"_s, u"renameFolder"_s}, Http::METHOD_POST},
Expand Down
Loading