@@ -135,6 +135,8 @@ namespace
135135 using Utils::String::parseInt;
136136 using Utils::String::parseDouble;
137137
138+ const QSet<QString> SUPPORTED_WEB_SEED_SCHEMES {u" http" _s, u" https" _s, u" ftp" _s};
139+
138140 void applyToTorrents (const QStringList &idList, const std::function<void (BitTorrent::Torrent *torrent)> &func)
139141 {
140142 if ((idList.size () == 1 ) && (idList[0 ] == u" all" ))
@@ -252,6 +254,20 @@ namespace
252254 idList << BitTorrent::TorrentID::fromString (hash);
253255 return idList;
254256 }
257+
258+ nonstd::expected<QUrl, QString> validateWebSeedUrl (const QString &urlStr)
259+ {
260+ const QString normalizedUrlStr = QUrl::fromPercentEncoding (urlStr.toLatin1 ());
261+
262+ const QUrl url {normalizedUrlStr, QUrl::StrictMode};
263+ if (!url.isValid ())
264+ return nonstd::make_unexpected (TorrentsController::tr (" \" %1\" is not a valid URL" ).arg (normalizedUrlStr));
265+
266+ if (!SUPPORTED_WEB_SEED_SCHEMES .contains (url.scheme ()))
267+ return nonstd::make_unexpected (TorrentsController::tr (" URL scheme must be one of [%1]" ).arg (SUPPORTED_WEB_SEED_SCHEMES .values ().join (u" , " )));
268+
269+ return url;
270+ }
255271}
256272
257273void TorrentsController::countAction ()
@@ -560,6 +576,84 @@ void TorrentsController::webseedsAction()
560576 setResult (webSeedList);
561577}
562578
579+ void TorrentsController::addWebSeedsAction ()
580+ {
581+ requireParams ({u" hash" _s, u" urls" _s});
582+ const QStringList paramUrls = params ()[u" urls" _s].split (u' |' , Qt::SkipEmptyParts);
583+
584+ const auto id = BitTorrent::TorrentID::fromString (params ()[u" hash" _s]);
585+ BitTorrent::Torrent *const torrent = BitTorrent::Session::instance ()->getTorrent (id);
586+ if (!torrent)
587+ throw APIError (APIErrorType::NotFound);
588+
589+ QList<QUrl> urls;
590+ urls.reserve (paramUrls.size ());
591+ for (const QString &urlStr : paramUrls)
592+ {
593+ const auto result = validateWebSeedUrl (urlStr);
594+ if (!result)
595+ throw APIError (APIErrorType::BadParams, result.error ());
596+ urls << result.value ();
597+ }
598+
599+ torrent->addUrlSeeds (urls);
600+ }
601+
602+ void TorrentsController::editWebSeedAction ()
603+ {
604+ requireParams ({u" hash" _s, u" origUrl" _s, u" newUrl" _s});
605+
606+ const auto id = BitTorrent::TorrentID::fromString (params ()[u" hash" _s]);
607+ const QString origUrlStr = params ()[u" origUrl" _s];
608+ const QString newUrlStr = params ()[u" newUrl" _s];
609+
610+ BitTorrent::Torrent *const torrent = BitTorrent::Session::instance ()->getTorrent (id);
611+ if (!torrent)
612+ throw APIError (APIErrorType::NotFound);
613+
614+ const auto origUrlResult = validateWebSeedUrl (origUrlStr);
615+ if (!origUrlResult)
616+ throw APIError (APIErrorType::BadParams, origUrlResult.error ());
617+ const QUrl origUrl = origUrlResult.value ();
618+
619+ const auto newUrlResult = validateWebSeedUrl (newUrlStr);
620+ if (!newUrlResult)
621+ throw APIError (APIErrorType::BadParams, newUrlResult.error ());
622+ const QUrl newUrl = newUrlResult.value ();
623+
624+ if (newUrl != origUrl)
625+ {
626+ if (!torrent->urlSeeds ().contains (origUrl))
627+ throw APIError (APIErrorType::Conflict, tr (" \" %1\" is not an existing URL" ).arg (origUrl.toString ()));
628+
629+ torrent->removeUrlSeeds ({origUrl});
630+ torrent->addUrlSeeds ({newUrl});
631+ }
632+ }
633+
634+ void TorrentsController::removeWebSeedsAction ()
635+ {
636+ requireParams ({u" hash" _s, u" urls" _s});
637+ const QStringList paramUrls = params ()[u" urls" _s].split (u' |' , Qt::SkipEmptyParts);
638+
639+ const auto id = BitTorrent::TorrentID::fromString (params ()[u" hash" _s]);
640+ BitTorrent::Torrent *const torrent = BitTorrent::Session::instance ()->getTorrent (id);
641+ if (!torrent)
642+ throw APIError (APIErrorType::NotFound);
643+
644+ QList<QUrl> urls;
645+ urls.reserve (paramUrls.size ());
646+ for (const QString &urlStr : paramUrls)
647+ {
648+ const auto result = validateWebSeedUrl (urlStr);
649+ if (!result)
650+ throw APIError (APIErrorType::BadParams, result.error ());
651+ urls << result.value ();
652+ }
653+
654+ torrent->removeUrlSeeds (urls);
655+ }
656+
563657// Returns the files in a torrent in JSON format.
564658// The return value is a JSON-formatted list of dictionaries.
565659// The dictionary keys are:
0 commit comments