mirror of
https://github.com/transmission/transmission
synced 2025-02-19 04:41:11 +00:00
refactor: add tr_announce_list (#2308)
* refactor: add tr_announce_list (#2308)
This commit is contained in:
parent
3e072f9bd4
commit
656df477f2
25 changed files with 1141 additions and 734 deletions
|
@ -155,6 +155,8 @@
|
|||
A257C1820CAD3003004E121C /* PeerTableView.mm in Sources */ = {isa = PBXBuildFile; fileRef = A257C1800CAD3003004E121C /* PeerTableView.mm */; };
|
||||
A25892640CF1F7E800CCCDDF /* StatsWindowController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A25892630CF1F7E800CCCDDF /* StatsWindowController.mm */; };
|
||||
A259317E0A73B2CC002F4FE7 /* TransmissionHelp in Resources */ = {isa = PBXBuildFile; fileRef = A259316A0A73B2CC002F4FE7 /* TransmissionHelp */; };
|
||||
66F977825E65AD498C028BB0 /* announce-list.cc in Sources */ = {isa = PBXBuildFile; fileRef = 66F977825E65AD498C028BB1 /* announcer-list.cc */; };
|
||||
66F977825E65AD498C028BB2 /* announce-list.h in Headers */ = {isa = PBXBuildFile; fileRef = 66F977825E65AD498C028BB3 /* announce-list.h */; };
|
||||
A25964A6106D73A800453B31 /* announcer.cc in Sources */ = {isa = PBXBuildFile; fileRef = A25964A4106D73A800453B31 /* announcer.cc */; };
|
||||
A25964A7106D73A800453B31 /* announcer.h in Headers */ = {isa = PBXBuildFile; fileRef = A25964A5106D73A800453B31 /* announcer.h */; };
|
||||
A25BFD69167BED3B0039D1AA /* variant-benc.cc in Sources */ = {isa = PBXBuildFile; fileRef = A25BFD63167BED3B0039D1AA /* variant-benc.cc */; };
|
||||
|
@ -702,6 +704,8 @@
|
|||
A25892620CF1F7E800CCCDDF /* StatsWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StatsWindowController.h; sourceTree = "<group>"; };
|
||||
A25892630CF1F7E800CCCDDF /* StatsWindowController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = StatsWindowController.mm; sourceTree = "<group>"; };
|
||||
A259316A0A73B2CC002F4FE7 /* TransmissionHelp */ = {isa = PBXFileReference; lastKnownFileType = folder; path = TransmissionHelp; sourceTree = "<group>"; };
|
||||
66F977825E65AD498C028BB1 /* announce-list.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = announce-list.cc; sourceTree = "<group>"; };
|
||||
66F977825E65AD498C028BB3 /* announce-list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = announce-list.h; sourceTree = "<group>"; };
|
||||
A25964A4106D73A800453B31 /* announcer.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = announcer.cc; sourceTree = "<group>"; };
|
||||
A25964A5106D73A800453B31 /* announcer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = announcer.h; sourceTree = "<group>"; };
|
||||
A25BFD63167BED3B0039D1AA /* variant-benc.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "variant-benc.cc"; sourceTree = "<group>"; };
|
||||
|
@ -1447,6 +1451,8 @@
|
|||
C10C644C1D9AF328003C1B4C /* session-id.h */,
|
||||
A20152790D1C26EB0081714F /* torrent-ctor.cc */,
|
||||
A23F299F132A447400E9A83B /* announcer-common.h */,
|
||||
66F977825E65AD498C028BB1 /* announce-list.cc */,
|
||||
66F977825E65AD498C028BB3 /* announce-list.h */,
|
||||
A25964A4106D73A800453B31 /* announcer.cc */,
|
||||
A25964A5106D73A800453B31 /* announcer.h */,
|
||||
A23F29A0132A447400E9A83B /* announcer-http.cc */,
|
||||
|
@ -1869,6 +1875,7 @@
|
|||
0A6169A80FE5C9A200C66CE6 /* bitfield.h in Headers */,
|
||||
1BB44E07B1B52E28291B4E33 /* file-piece-map.h in Headers */,
|
||||
A25964A7106D73A800453B31 /* announcer.h in Headers */,
|
||||
66F977825E65AD498C028BB2 /* announce-list.h in Headers */,
|
||||
ED8A16412735A8AA000D61F9 /* peer-mgr-wishlist.h in Headers */,
|
||||
4D8017EB10BBC073008A4AF2 /* torrent-magnet.h in Headers */,
|
||||
4D80185A10BBC0B0008A4AF2 /* magnet-metainfo.h in Headers */,
|
||||
|
@ -2465,6 +2472,7 @@
|
|||
0A6169A70FE5C9A200C66CE6 /* bitfield.cc in Sources */,
|
||||
1BB44E07B1B52E28291B4E32 /* file-piece-map.cc in Sources */,
|
||||
A25964A6106D73A800453B31 /* announcer.cc in Sources */,
|
||||
66F977825E65AD498C028BB0 /* announce-list.cc in Sources */,
|
||||
4D8017EA10BBC073008A4AF2 /* torrent-magnet.cc in Sources */,
|
||||
4D80185910BBC0B0008A4AF2 /* magnet-metainfo.cc in Sources */,
|
||||
A220EC5B118C8A060022B4BE /* tr-lpd.cc in Sources */,
|
||||
|
|
|
@ -2219,9 +2219,10 @@ void DetailsDialog::Impl::on_edit_trackers_response(int response, std::shared_pt
|
|||
auto const tracker_text = text_buffer->get_text(false);
|
||||
std::istringstream tracker_strings(tracker_text);
|
||||
|
||||
std::vector<tr_tracker_info> trackers;
|
||||
std::list<std::string> announce_urls;
|
||||
int tier = 0;
|
||||
auto announce_url_strings = std::vector<std::string>{};
|
||||
auto announce_urls = std::vector<char const*>{};
|
||||
auto tiers = std::vector<tr_tracker_tier_t>{};
|
||||
auto tier = tr_tracker_tier_t{ 0 };
|
||||
|
||||
std::string str;
|
||||
while (std::getline(tracker_strings, str))
|
||||
|
@ -2232,13 +2233,17 @@ void DetailsDialog::Impl::on_edit_trackers_response(int response, std::shared_pt
|
|||
}
|
||||
else
|
||||
{
|
||||
announce_urls.push_front(str);
|
||||
trackers.push_back(tr_tracker_info{ tier, announce_urls.front().data(), nullptr, 0 });
|
||||
announce_url_strings.push_back(str);
|
||||
tiers.push_back(tier);
|
||||
}
|
||||
}
|
||||
|
||||
/* update the torrent */
|
||||
if (tr_torrentSetAnnounceList(tor, trackers.data(), trackers.size()))
|
||||
std::transform(
|
||||
std::begin(announce_url_strings),
|
||||
std::end(announce_url_strings),
|
||||
std::back_inserter(announce_urls),
|
||||
[](auto const& url) { return url.c_str(); });
|
||||
if (tr_torrentSetAnnounceList(tor, std::data(announce_urls), std::data(tiers), std::size(announce_urls)))
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ configure_file(
|
|||
)
|
||||
|
||||
set(PROJECT_FILES
|
||||
announce-list.cc
|
||||
announcer-http.cc
|
||||
announcer-udp.cc
|
||||
announcer.cc
|
||||
|
@ -143,6 +144,7 @@ set(${PROJECT_NAME}_PUBLIC_HEADERS
|
|||
)
|
||||
|
||||
set(${PROJECT_NAME}_PRIVATE_HEADERS
|
||||
announce-list.h
|
||||
announcer-common.h
|
||||
announcer.h
|
||||
bandwidth.h
|
||||
|
|
259
libtransmission/announce-list.cc
Normal file
259
libtransmission/announce-list.cc
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "metainfo.h"
|
||||
#include "utils.h"
|
||||
#include "variant.h"
|
||||
|
||||
size_t tr_announce_list::set(char const* const* announce_urls, tr_tracker_tier_t const* tiers, size_t n)
|
||||
{
|
||||
trackers_.clear();
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
add(tiers[i], announce_urls[i]);
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
bool tr_announce_list::remove(std::string_view announce_url)
|
||||
{
|
||||
auto it = find(announce_url);
|
||||
if (it == std::end(trackers_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
trackers_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tr_announce_list::remove(tr_tracker_id_t id)
|
||||
{
|
||||
auto it = find(id);
|
||||
if (it == std::end(trackers_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
trackers_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tr_announce_list::replace(tr_tracker_id_t id, std::string_view announce_url_sv)
|
||||
{
|
||||
auto const announce = tr_urlParseTracker(announce_url_sv);
|
||||
if (!announce || !canAdd(*announce))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = find(id);
|
||||
if (it == std::end(trackers_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const tier = it->tier;
|
||||
trackers_.erase(it);
|
||||
return add(tier, announce_url_sv);
|
||||
}
|
||||
|
||||
bool tr_announce_list::add(tr_tracker_tier_t tier, std::string_view announce_url_sv)
|
||||
{
|
||||
auto const announce = tr_urlParseTracker(announce_url_sv);
|
||||
if (!announce || !canAdd(*announce))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tracker = tracker_info{};
|
||||
tracker.announce_interned = tr_quark_new(announce_url_sv);
|
||||
tracker.announce = *tr_urlParseTracker(tr_quark_get_string_view(tracker.announce_interned));
|
||||
tracker.tier = getTier(tier, *announce);
|
||||
tracker.id = nextUniqueId();
|
||||
auto host = std::string{ tracker.announce.host };
|
||||
host += ':';
|
||||
host += tracker.announce.portstr;
|
||||
tracker.host = tr_quark_new(host);
|
||||
|
||||
auto const scrape_str = announceToScrape(announce_url_sv);
|
||||
if (scrape_str)
|
||||
{
|
||||
tracker.scrape_interned = tr_quark_new(*scrape_str);
|
||||
tracker.scrape = *tr_urlParseTracker(tr_quark_get_string_view(tracker.scrape_interned));
|
||||
}
|
||||
|
||||
auto const it = std::lower_bound(std::begin(trackers_), std::end(trackers_), tracker);
|
||||
trackers_.insert(it, tracker);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> tr_announce_list::announceToScrape(std::string_view announce)
|
||||
{
|
||||
// To derive the scrape URL use the following steps:
|
||||
// Begin with the announce URL. Find the last '/' in it.
|
||||
// If the text immediately following that '/' isn't 'announce'
|
||||
// it will be taken as a sign that that tracker doesn't support
|
||||
// the scrape convention. If it does, substitute 'scrape' for
|
||||
// 'announce' to find the scrape page.
|
||||
auto constexpr oldval = std::string_view{ "/announce" };
|
||||
if (auto pos = announce.rfind(oldval.front()); pos != std::string_view::npos && announce.find(oldval, pos) == pos)
|
||||
{
|
||||
auto const prefix = announce.substr(0, pos);
|
||||
auto const suffix = announce.substr(pos + std::size(oldval));
|
||||
return tr_strvJoin(prefix, std::string_view{ "/scrape" }, suffix);
|
||||
}
|
||||
|
||||
// some torrents with UDP announce URLs don't have /announce
|
||||
if (tr_strvStartsWith(announce, std::string_view{ "udp:" }))
|
||||
{
|
||||
return std::string{ announce };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
tr_quark tr_announce_list::announceToScrape(tr_quark announce)
|
||||
{
|
||||
auto const scrape_str = announceToScrape(tr_quark_get_string_view(announce));
|
||||
if (scrape_str)
|
||||
{
|
||||
return tr_quark_new(*scrape_str);
|
||||
}
|
||||
return TR_KEY_NONE;
|
||||
}
|
||||
|
||||
std::set<tr_tracker_tier_t> tr_announce_list::tiers() const
|
||||
{
|
||||
auto tiers = std::set<tr_tracker_tier_t>{};
|
||||
for (auto const& tracker : trackers_)
|
||||
{
|
||||
tiers.insert(tracker.tier);
|
||||
}
|
||||
|
||||
return tiers;
|
||||
}
|
||||
|
||||
tr_tracker_tier_t tr_announce_list::nextTier() const
|
||||
{
|
||||
return std::empty(trackers_) ? 0 : trackers_.back().tier + 1;
|
||||
}
|
||||
|
||||
tr_tracker_id_t tr_announce_list::nextUniqueId()
|
||||
{
|
||||
static tr_tracker_id_t id = 0;
|
||||
return id++;
|
||||
}
|
||||
|
||||
tr_announce_list::trackers_t::iterator tr_announce_list::find(tr_tracker_id_t id)
|
||||
{
|
||||
auto const test = [&id](auto const& tracker)
|
||||
{
|
||||
return tracker.id == id;
|
||||
};
|
||||
return std::find_if(std::begin(trackers_), std::end(trackers_), test);
|
||||
}
|
||||
|
||||
tr_announce_list::trackers_t::iterator tr_announce_list::find(std::string_view announce)
|
||||
{
|
||||
auto const test = [&announce](auto const& tracker)
|
||||
{
|
||||
return announce == tracker.announce.full;
|
||||
};
|
||||
return std::find_if(std::begin(trackers_), std::end(trackers_), test);
|
||||
}
|
||||
|
||||
// if two announce URLs differ only by scheme, put them in the same tier.
|
||||
// (note: this can leave gaps in the `tier' values, but since the calling
|
||||
// function doesn't care, there's no point in removing the gaps...)
|
||||
tr_tracker_tier_t tr_announce_list::getTier(tr_tracker_tier_t tier, tr_url_parsed_t const& announce) const
|
||||
{
|
||||
auto const is_sibling = [&announce](tracker_info const& tracker)
|
||||
{
|
||||
return tracker.announce.host == announce.host && tracker.announce.path == announce.path;
|
||||
};
|
||||
|
||||
auto const it = std::find_if(std::begin(trackers_), std::end(trackers_), is_sibling);
|
||||
return it != std::end(trackers_) ? it->tier : tier;
|
||||
}
|
||||
|
||||
bool tr_announce_list::canAdd(tr_url_parsed_t const& announce)
|
||||
{
|
||||
// looking at components instead of the full original URL lets
|
||||
// us weed out implicit-vs-explicit port duplicates e.g.
|
||||
// "http://tracker/announce" + "http://tracker:80/announce"
|
||||
auto const is_same = [&announce](auto const& tracker)
|
||||
{
|
||||
return tracker.announce.scheme == announce.scheme && tracker.announce.host == announce.host &&
|
||||
tracker.announce.port == announce.port && tracker.announce.path == announce.path;
|
||||
};
|
||||
return std::none_of(std::begin(trackers_), std::end(trackers_), is_same);
|
||||
}
|
||||
|
||||
bool tr_announce_list::save(char const* torrent_file, tr_error** error) const
|
||||
{
|
||||
// load the .torrent file
|
||||
auto metainfo = tr_variant{};
|
||||
if (!tr_variantFromFile(&metainfo, TR_VARIANT_PARSE_BENC, torrent_file, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove the old fields
|
||||
tr_variantDictRemove(&metainfo, TR_KEY_announce);
|
||||
tr_variantDictRemove(&metainfo, TR_KEY_announce_list);
|
||||
|
||||
// add the new fields
|
||||
if (this->size() == 1)
|
||||
{
|
||||
tr_variantDictAddQuark(&metainfo, TR_KEY_announce, at(0).announce_interned);
|
||||
}
|
||||
else if (this->size() > 1)
|
||||
{
|
||||
tr_variant* tier_list = tr_variantDictAddList(&metainfo, TR_KEY_announce_list, 0);
|
||||
|
||||
auto current_tier = std::optional<tr_tracker_tier_t>{};
|
||||
tr_variant* tracker_list = nullptr;
|
||||
|
||||
for (auto const& tracker : *this)
|
||||
{
|
||||
if (tracker_list == nullptr || !current_tier || current_tier != tracker.tier)
|
||||
{
|
||||
tracker_list = tr_variantListAddList(tier_list, 1);
|
||||
current_tier = tracker.tier;
|
||||
}
|
||||
|
||||
tr_variantListAddQuark(tracker_list, tracker.announce_interned);
|
||||
}
|
||||
}
|
||||
|
||||
// confirm that it's good by parsing it back again
|
||||
if (!tr_metainfoParse(nullptr, &metainfo, error))
|
||||
{
|
||||
tr_variantFree(&metainfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
// save it
|
||||
auto contents_len = size_t{};
|
||||
auto* const contents = tr_variantToStr(&metainfo, TR_VARIANT_FMT_BENC, &contents_len);
|
||||
tr_variantFree(&metainfo);
|
||||
auto const success = tr_saveFile(torrent_file, { contents, contents_len }, error);
|
||||
tr_free(contents);
|
||||
return success;
|
||||
}
|
123
libtransmission/announce-list.h
Normal file
123
libtransmission/announce-list.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if 0 // TODO(ckerr): re-enable this after tr_info is made private
|
||||
#ifndef __TRANSMISSION__
|
||||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "quark.h"
|
||||
#include "tr-assert.h"
|
||||
#include "utils.h"
|
||||
#include "web-utils.h"
|
||||
|
||||
class tr_announce_list
|
||||
{
|
||||
public:
|
||||
struct tracker_info
|
||||
{
|
||||
tr_quark host;
|
||||
tr_url_parsed_t announce;
|
||||
tr_url_parsed_t scrape;
|
||||
tr_quark announce_interned = TR_KEY_NONE;
|
||||
tr_quark scrape_interned = TR_KEY_NONE;
|
||||
tr_tracker_tier_t tier = 0;
|
||||
tr_tracker_id_t id = 0;
|
||||
|
||||
int compare(tracker_info const& that) const // <=>
|
||||
{
|
||||
if (this->tier != that.tier)
|
||||
{
|
||||
return this->tier < that.tier ? -1 : 1;
|
||||
}
|
||||
|
||||
if (this->announce.full != that.announce.full)
|
||||
{
|
||||
return this->announce.full < that.announce.full ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator<(tracker_info const& that) const
|
||||
{
|
||||
return compare(that) < 0;
|
||||
}
|
||||
|
||||
bool operator==(tracker_info const& that) const
|
||||
{
|
||||
return compare(that) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
using trackers_t = std::vector<tracker_info>;
|
||||
|
||||
public:
|
||||
auto begin() const
|
||||
{
|
||||
return std::begin(trackers_);
|
||||
}
|
||||
auto end() const
|
||||
{
|
||||
return std::end(trackers_);
|
||||
}
|
||||
bool empty() const
|
||||
{
|
||||
return std::empty(trackers_);
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
return std::size(trackers_);
|
||||
}
|
||||
tracker_info const& at(size_t i) const
|
||||
{
|
||||
return trackers_.at(i);
|
||||
}
|
||||
|
||||
std::set<tr_tracker_tier_t> tiers() const;
|
||||
tr_tracker_tier_t nextTier() const;
|
||||
|
||||
bool add(tr_tracker_tier_t tier, std::string_view announce_url_sv);
|
||||
bool remove(std::string_view announce_url);
|
||||
bool remove(tr_tracker_id_t id);
|
||||
bool replace(tr_tracker_id_t id, std::string_view announce_url_sv);
|
||||
size_t set(char const* const* announce_urls, tr_tracker_tier_t const* tiers, size_t n);
|
||||
void clear()
|
||||
{
|
||||
return trackers_.clear();
|
||||
}
|
||||
|
||||
bool save(char const* torrent_file, tr_error** error = nullptr) const;
|
||||
|
||||
static std::optional<std::string> announceToScrape(std::string_view announce);
|
||||
static tr_quark announceToScrape(tr_quark announce);
|
||||
|
||||
private:
|
||||
tr_tracker_tier_t getTier(tr_tracker_tier_t tier, tr_url_parsed_t const& announce) const;
|
||||
|
||||
bool canAdd(tr_url_parsed_t const& announce);
|
||||
tr_tracker_id_t nextUniqueId();
|
||||
trackers_t::iterator find(std::string_view announce);
|
||||
trackers_t::iterator find(tr_tracker_id_t id);
|
||||
|
||||
trackers_t trackers_;
|
||||
};
|
|
@ -23,8 +23,10 @@
|
|||
#define LIBTRANSMISSION_ANNOUNCER_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "announcer.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "announcer-common.h"
|
||||
#include "announcer.h"
|
||||
#include "crypto-utils.h" /* tr_rand_int(), tr_rand_int_weak() */
|
||||
#include "log.h"
|
||||
#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
|
||||
|
@ -249,13 +251,14 @@ tr_quark tr_announcerGetKey(tr_url_parsed_t const& parsed)
|
|||
return tr_quark_new(buf);
|
||||
}
|
||||
|
||||
static void trackerConstruct(tr_announcer* announcer, tr_tracker* tracker, tr_tracker_info const* inf)
|
||||
static void trackerConstruct(tr_announcer* announcer, tr_tracker* tracker, tr_announce_list::tracker_info const& info)
|
||||
{
|
||||
memset(tracker, 0, sizeof(tr_tracker));
|
||||
tracker->key = tr_announcerGetKey(inf->announce);
|
||||
tracker->announce_url = tr_quark_new(tr_strvStrip(inf->announce));
|
||||
tracker->scrape_info = inf->scrape == nullptr ? nullptr : tr_announcerGetScrapeInfo(announcer, tr_quark_new(inf->scrape));
|
||||
tracker->id = inf->id;
|
||||
tracker->key = info.host;
|
||||
tracker->announce_url = info.announce_interned;
|
||||
tracker->scrape_info = info.scrape_interned == TR_KEY_NONE ? nullptr :
|
||||
tr_announcerGetScrapeInfo(announcer, info.scrape_interned);
|
||||
tracker->id = info.id;
|
||||
tracker->seederCount = -1;
|
||||
tracker->leecherCount = -1;
|
||||
tracker->downloadCount = -1;
|
||||
|
@ -270,7 +273,7 @@ static void trackerDestruct(tr_tracker* tracker)
|
|||
****
|
||||
***/
|
||||
|
||||
struct tr_torrent_tiers;
|
||||
struct tr_announcer_tiers;
|
||||
|
||||
/** @brief A group of trackers in a single tier, as per the multitracker spec */
|
||||
struct tr_tier
|
||||
|
@ -398,7 +401,7 @@ static void tierIncrementTracker(tr_tier* tier)
|
|||
*
|
||||
* this opaque data structure can be found in tr_torrent.tiers
|
||||
*/
|
||||
struct tr_torrent_tiers
|
||||
struct tr_announcer_tiers
|
||||
{
|
||||
tr_tier* tiers;
|
||||
int tier_count;
|
||||
|
@ -410,12 +413,12 @@ struct tr_torrent_tiers
|
|||
void* callbackData;
|
||||
};
|
||||
|
||||
static tr_torrent_tiers* tiersNew(void)
|
||||
static tr_announcer_tiers* tiersNew(void)
|
||||
{
|
||||
return tr_new0(tr_torrent_tiers, 1);
|
||||
return tr_new0(tr_announcer_tiers, 1);
|
||||
}
|
||||
|
||||
static void tiersDestruct(tr_torrent_tiers* tt)
|
||||
static void tiersDestruct(tr_announcer_tiers* tt)
|
||||
{
|
||||
for (int i = 0; i < tt->tracker_count; ++i)
|
||||
{
|
||||
|
@ -432,7 +435,7 @@ static void tiersDestruct(tr_torrent_tiers* tt)
|
|||
tr_free(tt->tiers);
|
||||
}
|
||||
|
||||
static void tiersFree(tr_torrent_tiers* tt)
|
||||
static void tiersFree(tr_announcer_tiers* tt)
|
||||
{
|
||||
tiersDestruct(tt);
|
||||
tr_free(tt);
|
||||
|
@ -447,9 +450,9 @@ static tr_tier* getTier(tr_announcer* announcer, tr_sha1_digest_t const& info_ha
|
|||
tr_session* session = announcer->session;
|
||||
tr_torrent* tor = tr_torrentFindFromHash(session, info_hash);
|
||||
|
||||
if (tor != nullptr && tor->tiers != nullptr)
|
||||
if (tor != nullptr && tor->announcer_tiers != nullptr)
|
||||
{
|
||||
tr_torrent_tiers* tt = tor->tiers;
|
||||
tr_announcer_tiers* tt = tor->announcer_tiers;
|
||||
|
||||
for (int i = 0; tier == nullptr && i < tt->tier_count; ++i)
|
||||
{
|
||||
|
@ -470,9 +473,10 @@ static tr_tier* getTier(tr_announcer* announcer, tr_sha1_digest_t const& info_ha
|
|||
|
||||
static void publishMessage(tr_tier* tier, char const* msg, TrackerEventType type)
|
||||
{
|
||||
if (tier != nullptr && tier->tor != nullptr && tier->tor->tiers != nullptr && tier->tor->tiers->callback != nullptr)
|
||||
if (tier != nullptr && tier->tor != nullptr && tier->tor->announcer_tiers != nullptr &&
|
||||
tier->tor->announcer_tiers->callback != nullptr)
|
||||
{
|
||||
tr_torrent_tiers* tiers = tier->tor->tiers;
|
||||
tr_announcer_tiers* tiers = tier->tor->announcer_tiers;
|
||||
auto event = tr_tracker_event{};
|
||||
event.messageType = type;
|
||||
event.text = msg;
|
||||
|
@ -503,7 +507,7 @@ static void publishError(tr_tier* tier, char const* msg)
|
|||
|
||||
static void publishPeerCounts(tr_tier* tier, int seeders, int leechers)
|
||||
{
|
||||
if (tier->tor->tiers->callback != nullptr)
|
||||
if (tier->tor->announcer_tiers->callback != nullptr)
|
||||
{
|
||||
auto e = tr_tracker_event{};
|
||||
e.messageType = TR_TRACKER_COUNTS;
|
||||
|
@ -511,13 +515,13 @@ static void publishPeerCounts(tr_tier* tier, int seeders, int leechers)
|
|||
e.leechers = leechers;
|
||||
dbgmsg(tier, "peer counts: %d seeders, %d leechers.", seeders, leechers);
|
||||
|
||||
(*tier->tor->tiers->callback)(tier->tor, &e, nullptr);
|
||||
(*tier->tor->announcer_tiers->callback)(tier->tor, &e, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void publishPeersPex(tr_tier* tier, int seeders, int leechers, tr_pex const* pex, int n)
|
||||
{
|
||||
if (tier->tor->tiers->callback != nullptr)
|
||||
if (tier->tor->announcer_tiers->callback != nullptr)
|
||||
{
|
||||
auto e = tr_tracker_event{};
|
||||
e.messageType = TR_TRACKER_PEERS;
|
||||
|
@ -527,7 +531,7 @@ static void publishPeersPex(tr_tier* tier, int seeders, int leechers, tr_pex con
|
|||
e.pexCount = n;
|
||||
dbgmsg(tier, "tracker knows of %d seeders and %d leechers and gave a list of %d peers.", seeders, leechers, n);
|
||||
|
||||
(*tier->tor->tiers->callback)(tier->tor, &e, nullptr);
|
||||
(*tier->tor->announcer_tiers->callback)(tier->tor, &e, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,152 +539,46 @@ static void publishPeersPex(tr_tier* tier, int seeders, int leechers, tr_pex con
|
|||
****
|
||||
***/
|
||||
|
||||
struct AnnTrackerInfo
|
||||
static void addTorrentToTier(tr_announcer_tiers* tt, tr_torrent* tor)
|
||||
{
|
||||
AnnTrackerInfo(tr_tracker_info const& info_in, tr_url_parsed_t const& url_in)
|
||||
: info{ info_in }
|
||||
, url{ url_in }
|
||||
{
|
||||
}
|
||||
auto const n = tor->trackerCount();
|
||||
auto const tiers = tor->tiers();
|
||||
|
||||
tr_tracker_info info;
|
||||
tr_url_parsed_t url;
|
||||
|
||||
/* primary key: tier
|
||||
* secondary key: udp comes before http */
|
||||
int compare(AnnTrackerInfo const& that) const // <=>
|
||||
{
|
||||
if (this->info.tier != that.info.tier)
|
||||
{
|
||||
return this->info.tier - that.info.tier;
|
||||
}
|
||||
|
||||
return -this->url.scheme.compare(that.url.scheme);
|
||||
}
|
||||
|
||||
bool operator<(AnnTrackerInfo const& that) const // less than
|
||||
{
|
||||
return this->compare(that) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Massages the incoming list of trackers into something we can use.
|
||||
*/
|
||||
static tr_tracker_info* filter_trackers(tr_tracker_info const* input, int input_count, int* setme_count)
|
||||
{
|
||||
auto tmp = std::vector<AnnTrackerInfo>{};
|
||||
tmp.reserve(input_count);
|
||||
|
||||
// build a list of valid trackers
|
||||
for (auto const *walk = input, *const end = walk + input_count; walk != end; ++walk)
|
||||
{
|
||||
auto const parsed = tr_urlParseTracker(walk->announce);
|
||||
if (!parsed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// weed out implicit-vs-explicit port duplicates e.g.
|
||||
// "http://tracker/announce" + "http://tracker:80/announce"
|
||||
auto const is_same = [&parsed](auto const& item)
|
||||
{
|
||||
return item.url.scheme == parsed->scheme && item.url.host == parsed->host && item.url.port == parsed->port &&
|
||||
item.url.path == parsed->path;
|
||||
};
|
||||
if (std::any_of(std::begin(tmp), std::end(tmp), is_same))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp.emplace_back(*walk, *parsed);
|
||||
}
|
||||
|
||||
// if two announce URLs differ only by scheme, put them in the same tier.
|
||||
// (note: this can leave gaps in the `tier' values, but since the calling
|
||||
// function doesn't care, there's no point in removing the gaps...)
|
||||
for (size_t i = 0, n = std::size(tmp); i < n; ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < n; ++j)
|
||||
{
|
||||
auto const& a = tmp[i];
|
||||
auto& b = tmp[j];
|
||||
|
||||
if ((a.info.tier != b.info.tier) && (a.url.port == b.url.port) && (a.url.host == b.url.host) &&
|
||||
(a.url.path == b.url.path))
|
||||
{
|
||||
b.info.tier = a.info.tier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort them, for two reasons:
|
||||
// 1. unjumble the tiers from the previous step
|
||||
// 2. move the UDP trackers to the front of each tier
|
||||
std::sort(std::begin(tmp), std::end(tmp));
|
||||
|
||||
// build the output
|
||||
auto const n = std::size(tmp);
|
||||
*setme_count = n;
|
||||
auto* const ret = tr_new0(tr_tracker_info, n);
|
||||
std::transform(std::begin(tmp), std::end(tmp), ret, [](auto const& item) { return item.info; });
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void addTorrentToTier(tr_torrent_tiers* tt, tr_torrent* tor)
|
||||
{
|
||||
auto n = int{};
|
||||
tr_tracker_info* infos = filter_trackers(tor->info.trackers, tor->info.trackerCount, &n);
|
||||
|
||||
/* build the array of trackers */
|
||||
// build the tracker and tier arrays
|
||||
tt->trackers = tr_new0(tr_tracker, n);
|
||||
tt->tracker_count = n;
|
||||
tt->tiers = tr_new0(tr_tier, std::size(tiers));
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
trackerConstruct(tor->session->announcer, &tt->trackers[i], &infos[i]);
|
||||
}
|
||||
|
||||
/* count how many tiers there are */
|
||||
auto tier_count = int{};
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
if (i == 0 || infos[i].tier != infos[i - 1].tier)
|
||||
{
|
||||
++tier_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* build the array of tiers */
|
||||
auto prev_tier = std::optional<tr_tracker_tier_t>{};
|
||||
tr_tier* tier = nullptr;
|
||||
tt->tiers = tr_new0(tr_tier, tier_count);
|
||||
tt->tier_count = 0;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
if (i != 0 && infos[i].tier == infos[i - 1].tier)
|
||||
{
|
||||
++tier->tracker_count;
|
||||
}
|
||||
else
|
||||
auto const info = tor->tracker(i);
|
||||
|
||||
trackerConstruct(tor->session->announcer, &tt->trackers[i], info);
|
||||
|
||||
if (!prev_tier || *prev_tier != info.tier)
|
||||
{
|
||||
tier = &tt->tiers[tt->tier_count++];
|
||||
tierConstruct(tier, tor);
|
||||
tier->trackers = &tt->trackers[i];
|
||||
tier->tracker_count = 1;
|
||||
tierIncrementTracker(tier);
|
||||
|
||||
prev_tier = info.tier;
|
||||
}
|
||||
else
|
||||
{
|
||||
++tier->tracker_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_free(infos);
|
||||
}
|
||||
|
||||
tr_torrent_tiers* tr_announcerAddTorrent(tr_torrent* tor, tr_tracker_callback callback, void* callbackData)
|
||||
tr_announcer_tiers* tr_announcerAddTorrent(tr_torrent* tor, tr_tracker_callback callback, void* callbackData)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
tr_torrent_tiers* tiers = tiersNew();
|
||||
tr_announcer_tiers* tiers = tiersNew();
|
||||
tiers->callback = callback;
|
||||
tiers->callbackData = callbackData;
|
||||
|
||||
|
@ -701,13 +599,13 @@ static bool tierCanManualAnnounce(tr_tier const* tier)
|
|||
bool tr_announcerCanManualAnnounce(tr_torrent const* tor)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tor->tiers != nullptr);
|
||||
TR_ASSERT(tor->announcer_tiers != nullptr);
|
||||
|
||||
struct tr_torrent_tiers const* tt = nullptr;
|
||||
struct tr_announcer_tiers const* tt = nullptr;
|
||||
|
||||
if (tor->isRunning)
|
||||
{
|
||||
tt = tor->tiers;
|
||||
tt = tor->announcer_tiers;
|
||||
}
|
||||
|
||||
/* return true if any tier can manual announce */
|
||||
|
@ -725,7 +623,7 @@ bool tr_announcerCanManualAnnounce(tr_torrent const* tor)
|
|||
time_t tr_announcerNextManualAnnounce(tr_torrent const* tor)
|
||||
{
|
||||
time_t ret = ~(time_t)0;
|
||||
struct tr_torrent_tiers const* tt = tor->tiers;
|
||||
struct tr_announcer_tiers const* tt = tor->announcer_tiers;
|
||||
|
||||
/* find the earliest manual announce time from all peers */
|
||||
for (int i = 0; tt != nullptr && i < tt->tier_count; ++i)
|
||||
|
@ -850,7 +748,7 @@ static tr_announce_event tier_announce_event_pull(tr_tier* tier)
|
|||
|
||||
static void torrentAddAnnounce(tr_torrent* tor, tr_announce_event e, time_t announceAt)
|
||||
{
|
||||
struct tr_torrent_tiers* tt = tor->tiers;
|
||||
struct tr_announcer_tiers* tt = tor->announcer_tiers;
|
||||
|
||||
/* walk through each tier and tell them to announce */
|
||||
for (int i = 0; i < tt->tier_count; ++i)
|
||||
|
@ -893,7 +791,7 @@ void tr_announcerAddBytes(tr_torrent* tor, int type, uint32_t byteCount)
|
|||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(type == TR_ANN_UP || type == TR_ANN_DOWN || type == TR_ANN_CORRUPT);
|
||||
|
||||
struct tr_torrent_tiers* tt = tor->tiers;
|
||||
struct tr_announcer_tiers* tt = tor->announcer_tiers;
|
||||
|
||||
for (int i = 0; i < tt->tier_count; ++i)
|
||||
{
|
||||
|
@ -933,7 +831,7 @@ static void announce_request_free(tr_announce_request* req);
|
|||
|
||||
void tr_announcerRemoveTorrent(tr_announcer* announcer, tr_torrent* tor)
|
||||
{
|
||||
struct tr_torrent_tiers const* tt = tor->tiers;
|
||||
struct tr_announcer_tiers const* tt = tor->announcer_tiers;
|
||||
|
||||
if (tt != nullptr)
|
||||
{
|
||||
|
@ -957,8 +855,8 @@ void tr_announcerRemoveTorrent(tr_announcer* announcer, tr_torrent* tor)
|
|||
}
|
||||
}
|
||||
|
||||
tiersFree(tor->tiers);
|
||||
tor->tiers = nullptr;
|
||||
tiersFree(tor->announcer_tiers);
|
||||
tor->announcer_tiers = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1080,7 +978,7 @@ static void on_announce_done(tr_announce_response const* response, void* vdata)
|
|||
Don't bother publishing if there are other trackers -- it's
|
||||
all too common for people to load up dozens of dead trackers
|
||||
in a torrent's metainfo... */
|
||||
if (tier->tor->info.trackerCount < 2)
|
||||
if (tier->tor->trackerCount() < 2)
|
||||
{
|
||||
publishError(tier, response->errmsg);
|
||||
}
|
||||
|
@ -1323,7 +1221,7 @@ static void on_scrape_error(tr_session const* session, tr_tier* tier, char const
|
|||
|
||||
static tr_tier* find_tier(tr_torrent* tor, tr_quark scrape_url)
|
||||
{
|
||||
struct tr_torrent_tiers* tt = tor->tiers;
|
||||
struct tr_announcer_tiers* tt = tor->announcer_tiers;
|
||||
|
||||
for (int i = 0; tt != nullptr && i < tt->tier_count; ++i)
|
||||
{
|
||||
|
@ -1637,7 +1535,7 @@ static void scrapeAndAnnounceMore(tr_announcer* announcer)
|
|||
auto scrape_me = std::vector<tr_tier*>{};
|
||||
for (auto* tor : announcer->session->torrents)
|
||||
{
|
||||
struct tr_torrent_tiers* tt = tor->tiers;
|
||||
struct tr_announcer_tiers* tt = tor->announcer_tiers;
|
||||
|
||||
for (int i = 0; tt != nullptr && i < tt->tier_count; ++i)
|
||||
{
|
||||
|
@ -1798,25 +1696,25 @@ static tr_tracker_view trackerView(tr_torrent const& tor, int tier_index, tr_tie
|
|||
}
|
||||
|
||||
TR_ASSERT(0 <= view.tier);
|
||||
TR_ASSERT(view.tier < tor.tiers->tier_count);
|
||||
TR_ASSERT(view.tier < tor.announcer_tiers->tier_count);
|
||||
return view;
|
||||
}
|
||||
|
||||
size_t tr_announcerTrackerCount(tr_torrent const* tor)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tor->tiers != nullptr);
|
||||
TR_ASSERT(tor->announcer_tiers != nullptr);
|
||||
|
||||
return tor->tiers->tracker_count;
|
||||
return tor->announcer_tiers->tracker_count;
|
||||
}
|
||||
|
||||
tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tor->tiers != nullptr);
|
||||
TR_ASSERT(tor->announcer_tiers != nullptr);
|
||||
|
||||
// find the nth tracker
|
||||
struct tr_torrent_tiers const* const tt = tor->tiers;
|
||||
struct tr_announcer_tiers const* const tt = tor->announcer_tiers;
|
||||
if (nth >= size_t(tt->tracker_count))
|
||||
{
|
||||
return {};
|
||||
|
@ -1869,7 +1767,7 @@ static void copy_tier_attributes_impl(struct tr_tier* tgt, int trackerIndex, tr_
|
|||
tgt->currentTracker->downloaderCount = src->currentTracker->downloaderCount;
|
||||
}
|
||||
|
||||
static void copy_tier_attributes(struct tr_torrent_tiers* tt, tr_tier const* src)
|
||||
static void copy_tier_attributes(struct tr_announcer_tiers* tt, tr_tier const* src)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
|
@ -1889,12 +1787,12 @@ static void copy_tier_attributes(struct tr_torrent_tiers* tt, tr_tier const* src
|
|||
|
||||
void tr_announcerResetTorrent(tr_announcer* /*announcer*/, tr_torrent* tor)
|
||||
{
|
||||
TR_ASSERT(tor->tiers != nullptr);
|
||||
TR_ASSERT(tor->announcer_tiers != nullptr);
|
||||
|
||||
time_t const now = tr_time();
|
||||
|
||||
struct tr_torrent_tiers* tt = tor->tiers;
|
||||
tr_torrent_tiers old = *tt;
|
||||
tr_announcer_tiers* tt = tor->announcer_tiers;
|
||||
tr_announcer_tiers old = *tt;
|
||||
|
||||
/* remove the old tiers / trackers */
|
||||
tt->tiers = nullptr;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "quark.h"
|
||||
|
||||
struct tr_announcer;
|
||||
struct tr_torrent_tiers;
|
||||
struct tr_announcer_tiers;
|
||||
|
||||
/**
|
||||
* *** Tracker Publish / Subscribe
|
||||
|
@ -66,7 +66,7 @@ void tr_announcerClose(tr_session*);
|
|||
*** For torrent customers
|
||||
**/
|
||||
|
||||
struct tr_torrent_tiers* tr_announcerAddTorrent(tr_torrent* torrent, tr_tracker_callback cb, void* cbdata);
|
||||
struct tr_announcer_tiers* tr_announcerAddTorrent(tr_torrent* torrent, tr_tracker_callback cb, void* cbdata);
|
||||
|
||||
void tr_announcerResetTorrent(struct tr_announcer*, tr_torrent*);
|
||||
|
||||
|
|
|
@ -123,10 +123,10 @@ std::string tr_magnet_metainfo::magnet() const
|
|||
tr_http_escape(s, name, true);
|
||||
}
|
||||
|
||||
for (auto const& [tier, tracker] : trackers)
|
||||
for (auto const& tracker : this->announce_list)
|
||||
{
|
||||
s += "&tr="sv;
|
||||
tr_http_escape(s, tr_quark_get_string_view(tracker.announce_url), true);
|
||||
tr_http_escape(s, tracker.announce.full, true);
|
||||
}
|
||||
|
||||
for (auto const& webseed : webseed_urls)
|
||||
|
@ -138,33 +138,6 @@ std::string tr_magnet_metainfo::magnet() const
|
|||
return s;
|
||||
}
|
||||
|
||||
static tr_quark announceToScrape(std::string_view announce)
|
||||
{
|
||||
auto buf = std::string{};
|
||||
|
||||
if (!tr_magnet_metainfo::convertAnnounceToScrape(buf, announce))
|
||||
{
|
||||
return TR_KEY_NONE;
|
||||
}
|
||||
|
||||
return tr_quark_new(buf);
|
||||
}
|
||||
|
||||
bool tr_magnet_metainfo::addTracker(tr_tracker_tier_t tier, std::string_view announce_sv)
|
||||
{
|
||||
announce_sv = tr_strvStrip(announce_sv);
|
||||
|
||||
if (!tr_urlIsValidTracker(announce_sv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const announce_url = tr_quark_new(announce_sv);
|
||||
auto const scrape_url = announceToScrape(announce_sv);
|
||||
this->trackers.insert({ tier, { announce_url, scrape_url, tier } });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tr_magnet_metainfo::parseMagnet(std::string_view magnet_link, tr_error** error)
|
||||
{
|
||||
auto const parsed = tr_urlParse(magnet_link);
|
||||
|
@ -175,7 +148,6 @@ bool tr_magnet_metainfo::parseMagnet(std::string_view magnet_link, tr_error** er
|
|||
}
|
||||
|
||||
bool got_checksum = false;
|
||||
auto tier = tr_tracker_tier_t{ 0 };
|
||||
for (auto const& [key, value] : tr_url_query_view{ parsed->query })
|
||||
{
|
||||
if (key == "dn"sv)
|
||||
|
@ -185,8 +157,7 @@ bool tr_magnet_metainfo::parseMagnet(std::string_view magnet_link, tr_error** er
|
|||
else if (key == "tr"sv || tr_strvStartsWith(key, "tr."sv))
|
||||
{
|
||||
// "tr." explanation @ https://trac.transmissionbt.com/ticket/3341
|
||||
addTracker(tier, tr_urlPercentDecode(value));
|
||||
++tier;
|
||||
this->announce_list.add(this->announce_list.nextTier(), tr_urlPercentDecode(value));
|
||||
}
|
||||
else if (key == "ws"sv)
|
||||
{
|
||||
|
@ -228,49 +199,31 @@ bool tr_magnet_metainfo::parseMagnet(std::string_view magnet_link, tr_error** er
|
|||
return got_checksum;
|
||||
}
|
||||
|
||||
bool tr_magnet_metainfo::convertAnnounceToScrape(std::string& out, std::string_view in)
|
||||
{
|
||||
// To derive the scrape URL use the following steps:
|
||||
// Begin with the announce URL. Find the last '/' in it.
|
||||
// If the text immediately following that '/' isn't 'announce'
|
||||
// it will be taken as a sign that that tracker doesn't support
|
||||
// the scrape convention. If it does, substitute 'scrape' for
|
||||
// 'announce' to find the scrape page.
|
||||
auto constexpr oldval = "/announce"sv;
|
||||
if (auto pos = in.rfind(oldval.front()); pos != in.npos && in.find(oldval, pos) == pos)
|
||||
{
|
||||
auto const prefix = in.substr(0, pos);
|
||||
auto const suffix = in.substr(pos + std::size(oldval));
|
||||
tr_buildBuf(out, prefix, "/scrape"sv, suffix);
|
||||
return true;
|
||||
}
|
||||
|
||||
// some torrents with UDP announce URLs don't have /announce
|
||||
if (tr_strvStartsWith(in, "udp:"sv))
|
||||
{
|
||||
out = in;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void tr_magnet_metainfo::toVariant(tr_variant* top) const
|
||||
{
|
||||
tr_variantInitDict(top, 4);
|
||||
|
||||
// announce list
|
||||
auto n = std::size(this->trackers);
|
||||
auto n = std::size(this->announce_list);
|
||||
if (n == 1)
|
||||
{
|
||||
tr_variantDictAddStr(top, TR_KEY_announce, tr_quark_get_string_view(std::begin(this->trackers)->second.announce_url));
|
||||
tr_variantDictAddQuark(top, TR_KEY_announce, this->announce_list.at(0).announce_interned);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* list = tr_variantDictAddList(top, TR_KEY_announce_list, n);
|
||||
for (auto const& [tier, tracker] : this->trackers)
|
||||
auto current_tier = tr_tracker_tier_t{};
|
||||
tr_variant* tracker_list = nullptr;
|
||||
|
||||
auto* tier_list = tr_variantDictAddList(top, TR_KEY_announce_list, n);
|
||||
for (auto const& tracker : this->announce_list)
|
||||
{
|
||||
tr_variantListAddStr(tr_variantListAddList(list, 1), tr_quark_get_string_view(tracker.announce_url));
|
||||
if (tracker_list == nullptr || current_tier != tracker.tier)
|
||||
{
|
||||
tracker_list = tr_variantListAddList(tier_list, 1);
|
||||
current_tier = tracker.tier;
|
||||
}
|
||||
|
||||
tr_variantListAddQuark(tracker_list, tracker.announce_interned);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "error.h"
|
||||
#include "quark.h"
|
||||
|
||||
|
@ -32,43 +33,19 @@ struct tr_magnet_metainfo
|
|||
|
||||
void toVariant(tr_variant*) const;
|
||||
|
||||
static bool convertAnnounceToScrape(std::string& setme, std::string_view announce_url);
|
||||
|
||||
std::string_view infoHashString() const
|
||||
{
|
||||
// trim one byte off the end because of zero termination
|
||||
return std::string_view{ std::data(info_hash_chars), std::size(info_hash_chars) - 1 };
|
||||
}
|
||||
|
||||
struct tracker_t
|
||||
{
|
||||
tr_quark announce_url;
|
||||
tr_quark scrape_url;
|
||||
tr_tracker_tier_t tier;
|
||||
|
||||
tracker_t(tr_quark announce_in, tr_quark scrape_in, tr_tracker_tier_t tier_in)
|
||||
: announce_url{ announce_in }
|
||||
, scrape_url{ scrape_in }
|
||||
, tier{ tier_in }
|
||||
{
|
||||
}
|
||||
|
||||
bool operator<(tracker_t const& that) const
|
||||
{
|
||||
return announce_url < that.announce_url;
|
||||
}
|
||||
};
|
||||
tr_announce_list announce_list;
|
||||
|
||||
std::vector<std::string> webseed_urls;
|
||||
|
||||
std::string name;
|
||||
|
||||
std::multimap<tr_tracker_tier_t, tracker_t> trackers;
|
||||
|
||||
tr_sha1_digest_string_t info_hash_chars;
|
||||
|
||||
tr_sha1_digest_t info_hash;
|
||||
|
||||
protected:
|
||||
bool addTracker(tr_tracker_tier_t tier, std::string_view announce_url);
|
||||
};
|
||||
|
|
|
@ -26,6 +26,14 @@ enum tr_metainfo_builder_err
|
|||
TR_MAKEMETA_IO_WRITE /* see builder.errfile, builder.my_errno */
|
||||
};
|
||||
|
||||
struct tr_tracker_info
|
||||
{
|
||||
int tier;
|
||||
char* announce;
|
||||
char* scrape;
|
||||
uint32_t id; /* unique identifier used to match to a tr_tracker_stat */
|
||||
};
|
||||
|
||||
struct tr_metainfo_builder
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -236,122 +236,44 @@ static char const* parseFiles(tr_info* inf, tr_variant* files, tr_variant const*
|
|||
return errstr;
|
||||
}
|
||||
|
||||
static char* tr_convertAnnounceToScrape(std::string_view url)
|
||||
{
|
||||
char* scrape = nullptr;
|
||||
|
||||
/* To derive the scrape URL use the following steps:
|
||||
* Begin with the announce URL. Find the last '/' in it.
|
||||
* If the text immediately following that '/' isn't 'announce'
|
||||
* it will be taken as a sign that that tracker doesn't support
|
||||
* the scrape convention. If it does, substitute 'scrape' for
|
||||
* 'announce' to find the scrape page. */
|
||||
|
||||
auto constexpr oldval = "/announce"sv;
|
||||
auto pos = url.rfind(oldval.front());
|
||||
if (pos != url.npos && url.find(oldval, pos) == pos)
|
||||
{
|
||||
auto constexpr newval = "/scrape"sv;
|
||||
auto const prefix = url.substr(0, pos);
|
||||
auto const suffix = url.substr(pos + std::size(oldval));
|
||||
auto const n = std::size(prefix) + std::size(newval) + std::size(suffix);
|
||||
scrape = tr_new(char, n + 1);
|
||||
auto* walk = scrape;
|
||||
walk = std::copy(std::begin(prefix), std::end(prefix), walk);
|
||||
walk = std::copy(std::begin(newval), std::end(newval), walk);
|
||||
walk = std::copy(std::begin(suffix), std::end(suffix), walk);
|
||||
*walk = '\0';
|
||||
TR_ASSERT(scrape + n == walk);
|
||||
}
|
||||
// some torrents with UDP announce URLs don't have /announce
|
||||
else if (url.find("udp:"sv) == 0)
|
||||
{
|
||||
scrape = tr_strvDup(url);
|
||||
}
|
||||
|
||||
return scrape;
|
||||
}
|
||||
|
||||
static char const* getannounce(tr_info* inf, tr_variant* meta)
|
||||
{
|
||||
tr_tracker_info* trackers = nullptr;
|
||||
int trackerCount = 0;
|
||||
inf->announce_list = std::make_shared<tr_announce_list>();
|
||||
|
||||
// tr_tracker_info* trackers = nullptr;
|
||||
// int trackerCount = 0;
|
||||
auto url = std::string_view{};
|
||||
|
||||
/* Announce-list */
|
||||
tr_variant* tiers = nullptr;
|
||||
if (tr_variantDictFindList(meta, TR_KEY_announce_list, &tiers))
|
||||
{
|
||||
int const numTiers = tr_variantListSize(tiers);
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < numTiers; i++)
|
||||
for (size_t i = 0, in = tr_variantListSize(tiers); i < in; ++i)
|
||||
{
|
||||
n += tr_variantListSize(tr_variantListChild(tiers, i));
|
||||
}
|
||||
|
||||
trackers = tr_new0(tr_tracker_info, n);
|
||||
|
||||
int validTiers = 0;
|
||||
for (int i = 0; i < numTiers; ++i)
|
||||
{
|
||||
tr_variant* tier = tr_variantListChild(tiers, i);
|
||||
int const tierSize = tr_variantListSize(tier);
|
||||
bool anyAdded = false;
|
||||
|
||||
for (int j = 0; j < tierSize; j++)
|
||||
tr_variant* tier_list = tr_variantListChild(tiers, i);
|
||||
if (tier_list == nullptr)
|
||||
{
|
||||
if (tr_variantGetStrView(tr_variantListChild(tier, j), &url))
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = 0, jn = tr_variantListSize(tier_list); j < jn; ++j)
|
||||
{
|
||||
if (!tr_variantGetStrView(tr_variantListChild(tier_list, j), &url))
|
||||
{
|
||||
url = tr_strvStrip(url);
|
||||
|
||||
if (tr_urlIsValidTracker(url))
|
||||
{
|
||||
tr_tracker_info* t = trackers + trackerCount;
|
||||
t->tier = validTiers;
|
||||
t->announce = tr_strvDup(url);
|
||||
t->scrape = tr_convertAnnounceToScrape(url);
|
||||
t->id = trackerCount;
|
||||
|
||||
anyAdded = true;
|
||||
++trackerCount;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyAdded)
|
||||
{
|
||||
++validTiers;
|
||||
inf->announce_list->add(i, url);
|
||||
}
|
||||
}
|
||||
|
||||
/* did we use any of the tiers? */
|
||||
if (trackerCount == 0)
|
||||
{
|
||||
tr_free(trackers);
|
||||
trackers = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Regular announce value */
|
||||
if (trackerCount == 0 && tr_variantDictFindStrView(meta, TR_KEY_announce, &url))
|
||||
if (std::empty(*inf->announce_list) && tr_variantDictFindStrView(meta, TR_KEY_announce, &url))
|
||||
{
|
||||
url = tr_strvStrip(url);
|
||||
|
||||
if (tr_urlIsValidTracker(url))
|
||||
{
|
||||
trackers = tr_new0(tr_tracker_info, 1);
|
||||
trackers[trackerCount].tier = 0;
|
||||
trackers[trackerCount].announce = tr_strvDup(url);
|
||||
trackers[trackerCount].scrape = tr_convertAnnounceToScrape(url);
|
||||
trackers[trackerCount].id = 0;
|
||||
trackerCount++;
|
||||
}
|
||||
inf->announce_list->add(0, url);
|
||||
}
|
||||
|
||||
inf->trackers = trackers;
|
||||
inf->trackerCount = trackerCount;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -630,33 +552,39 @@ std::optional<tr_metainfo_parsed> tr_metainfoParse(tr_session const* session, tr
|
|||
|
||||
void tr_metainfoFree(tr_info* inf)
|
||||
{
|
||||
for (unsigned int i = 0; i < inf->webseedCount; i++)
|
||||
if (inf->webseeds != nullptr)
|
||||
{
|
||||
tr_free(inf->webseeds[i]);
|
||||
for (unsigned int i = 0; i < inf->webseedCount; i++)
|
||||
{
|
||||
tr_free(inf->webseeds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (tr_file_index_t ff = 0; ff < inf->fileCount; ff++)
|
||||
if (inf->files != nullptr)
|
||||
{
|
||||
tr_free(inf->files[ff].name);
|
||||
for (tr_file_index_t ff = 0; ff < inf->fileCount; ff++)
|
||||
{
|
||||
tr_free(inf->files[ff].name);
|
||||
}
|
||||
}
|
||||
|
||||
tr_free(inf->webseeds);
|
||||
tr_free(inf->files);
|
||||
tr_free(inf->comment);
|
||||
tr_free(inf->creator);
|
||||
tr_free(inf->files);
|
||||
tr_free(inf->name);
|
||||
tr_free(inf->source);
|
||||
tr_free(inf->torrent);
|
||||
tr_free(inf->name);
|
||||
tr_free(inf->webseeds);
|
||||
|
||||
for (unsigned int i = 0; i < inf->trackerCount; i++)
|
||||
{
|
||||
tr_free(inf->trackers[i].announce);
|
||||
tr_free(inf->trackers[i].scrape);
|
||||
}
|
||||
inf->comment = nullptr;
|
||||
inf->creator = nullptr;
|
||||
inf->files = nullptr;
|
||||
inf->name = nullptr;
|
||||
inf->source = nullptr;
|
||||
inf->torrent = nullptr;
|
||||
inf->webseeds = nullptr;
|
||||
|
||||
tr_free(inf->trackers);
|
||||
|
||||
memset(inf, '\0', sizeof(tr_info));
|
||||
inf->announce_list.reset();
|
||||
}
|
||||
|
||||
void tr_metainfoRemoveSaved(tr_session const* session, tr_info const* inf)
|
||||
|
|
|
@ -413,16 +413,16 @@ static void addWebseeds(tr_info const* info, tr_variant* webseeds)
|
|||
}
|
||||
}
|
||||
|
||||
static void addTrackers(tr_info const* info, tr_variant* trackers)
|
||||
static void addTrackers(tr_torrent const* tor, tr_variant* trackers)
|
||||
{
|
||||
for (unsigned int i = 0; i < info->trackerCount; ++i)
|
||||
for (size_t i = 0, n = tor->trackerCount(); i < n; ++i)
|
||||
{
|
||||
tr_tracker_info const* t = &info->trackers[i];
|
||||
auto const info = tor->tracker(i);
|
||||
tr_variant* d = tr_variantListAddDict(trackers, 4);
|
||||
tr_variantDictAddStrView(d, TR_KEY_announce, t->announce);
|
||||
tr_variantDictAddInt(d, TR_KEY_id, t->id);
|
||||
tr_variantDictAddStrView(d, TR_KEY_scrape, t->scrape != nullptr ? t->scrape : ""sv);
|
||||
tr_variantDictAddInt(d, TR_KEY_tier, t->tier);
|
||||
tr_variantDictAddQuark(d, TR_KEY_announce, info.announce_interned);
|
||||
tr_variantDictAddInt(d, TR_KEY_id, info.id);
|
||||
tr_variantDictAddQuark(d, TR_KEY_scrape, info.scrape_interned);
|
||||
tr_variantDictAddInt(d, TR_KEY_tier, info.tier);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,8 +781,8 @@ static void initField(
|
|||
break;
|
||||
|
||||
case TR_KEY_trackers:
|
||||
tr_variantInitList(initme, inf->trackerCount);
|
||||
addTrackers(inf, initme);
|
||||
tr_variantInitList(initme, tor->trackerCount());
|
||||
addTrackers(tor, initme);
|
||||
break;
|
||||
|
||||
case TR_KEY_trackerStats:
|
||||
|
@ -1056,175 +1056,79 @@ static char const* setFileDLs(tr_torrent* tor, bool wanted, tr_variant* list)
|
|||
return errmsg;
|
||||
}
|
||||
|
||||
static bool hasAnnounceUrl(tr_tracker_info const* t, int n, std::string_view url)
|
||||
{
|
||||
return std::any_of(t, t + n, [&url](auto const& row) { return row.announce == url; });
|
||||
}
|
||||
|
||||
static int copyTrackers(tr_tracker_info* tgt, tr_tracker_info const* src, int n)
|
||||
{
|
||||
int maxTier = -1;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
tgt[i].tier = src[i].tier;
|
||||
tgt[i].announce = tr_strdup(src[i].announce);
|
||||
maxTier = std::max(maxTier, src[i].tier);
|
||||
}
|
||||
|
||||
return maxTier;
|
||||
}
|
||||
|
||||
static void freeTrackers(tr_tracker_info* trackers, int n)
|
||||
{
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
tr_free(trackers[i].announce);
|
||||
}
|
||||
|
||||
tr_free(trackers);
|
||||
}
|
||||
|
||||
static char const* addTrackerUrls(tr_torrent* tor, tr_variant* urls)
|
||||
{
|
||||
char const* errmsg = nullptr;
|
||||
auto const old_size = tor->trackerCount();
|
||||
|
||||
/* make a working copy of the existing announce list */
|
||||
auto const* const inf = tr_torrentInfo(tor);
|
||||
int n = inf->trackerCount;
|
||||
auto* const trackers = tr_new0(tr_tracker_info, n + tr_variantListSize(urls));
|
||||
int tier = copyTrackers(trackers, inf->trackers, n);
|
||||
|
||||
/* and add the new ones */
|
||||
auto i = int{ 0 };
|
||||
auto changed = bool{ false };
|
||||
tr_variant const* val = nullptr;
|
||||
while ((val = tr_variantListChild(urls, i)) != nullptr)
|
||||
for (size_t i = 0, n = tr_variantListSize(urls); i < n; ++i)
|
||||
{
|
||||
auto announce = std::string_view{};
|
||||
|
||||
if (tr_variantGetStrView(val, &announce) && tr_urlIsValidTracker(announce) && !hasAnnounceUrl(trackers, n, announce))
|
||||
{
|
||||
trackers[n].tier = ++tier; /* add a new tier */
|
||||
trackers[n].announce = tr_strvDup(announce);
|
||||
++n;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
{
|
||||
errmsg = "invalid argument";
|
||||
}
|
||||
else if (!tr_torrentSetAnnounceList(tor, trackers, n))
|
||||
{
|
||||
errmsg = "error setting announce list";
|
||||
}
|
||||
|
||||
freeTrackers(trackers, n);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
static char const* replaceTrackers(tr_torrent* tor, tr_variant* urls)
|
||||
{
|
||||
char const* errmsg = nullptr;
|
||||
|
||||
/* make a working copy of the existing announce list */
|
||||
auto const* const inf = tr_torrentInfo(tor);
|
||||
int const n = inf->trackerCount;
|
||||
auto* const trackers = tr_new0(tr_tracker_info, n);
|
||||
copyTrackers(trackers, inf->trackers, n);
|
||||
|
||||
/* make the substitutions... */
|
||||
bool changed = false;
|
||||
for (size_t i = 0, url_count = tr_variantListSize(urls); i + 1 < url_count; i += 2)
|
||||
{
|
||||
auto pos = int64_t{};
|
||||
auto newval = std::string_view{};
|
||||
|
||||
if (tr_variantGetInt(tr_variantListChild(urls, i), &pos) &&
|
||||
tr_variantGetStrView(tr_variantListChild(urls, i + 1), &newval) && tr_urlIsValidTracker(newval) && pos < n &&
|
||||
pos >= 0)
|
||||
{
|
||||
tr_free(trackers[pos].announce);
|
||||
trackers[pos].announce = tr_strvDup(newval);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
{
|
||||
errmsg = "invalid argument";
|
||||
}
|
||||
else if (!tr_torrentSetAnnounceList(tor, trackers, n))
|
||||
{
|
||||
errmsg = "error setting announce list";
|
||||
}
|
||||
|
||||
freeTrackers(trackers, n);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
static char const* removeTrackers(tr_torrent* tor, tr_variant* ids)
|
||||
{
|
||||
/* make a working copy of the existing announce list */
|
||||
tr_info const* inf = tr_torrentInfo(tor);
|
||||
int n = inf->trackerCount;
|
||||
int* tids = tr_new0(int, n);
|
||||
tr_tracker_info* trackers = tr_new0(tr_tracker_info, n);
|
||||
copyTrackers(trackers, inf->trackers, n);
|
||||
|
||||
/* remove the ones specified in the urls list */
|
||||
int i = 0;
|
||||
int t = 0;
|
||||
tr_variant const* val = nullptr;
|
||||
while ((val = tr_variantListChild(ids, i)) != nullptr)
|
||||
{
|
||||
auto pos = int64_t{};
|
||||
|
||||
if (tr_variantGetInt(val, &pos) && 0 <= pos && pos < n)
|
||||
{
|
||||
tids[t++] = (int)pos;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
/* sort trackerIds and remove from largest to smallest so there is no need to recalculate array indicies */
|
||||
std::sort(tids, tids + t);
|
||||
|
||||
bool changed = false;
|
||||
int dup = -1;
|
||||
while (t-- != 0)
|
||||
{
|
||||
/* check for duplicates */
|
||||
if (tids[t] == dup)
|
||||
auto announce = std::string_view();
|
||||
auto* const val = tr_variantListChild(urls, i);
|
||||
if (val == nullptr || !tr_variantGetStrView(val, &announce))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tr_removeElementFromArray(trackers, tids[t], sizeof(tr_tracker_info), n);
|
||||
--n;
|
||||
|
||||
dup = tids[t];
|
||||
changed = true;
|
||||
tor->info.announce_list->add(tor->info.announce_list->nextTier(), announce);
|
||||
}
|
||||
|
||||
if (tor->trackerCount() == old_size)
|
||||
{
|
||||
return "error setting announce list";
|
||||
}
|
||||
|
||||
tor->info.announce_list->save(tor->info.torrent);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static char const* replaceTrackers(tr_torrent* tor, tr_variant* urls)
|
||||
{
|
||||
auto changed = bool{ false };
|
||||
|
||||
for (size_t i = 0, url_count = tr_variantListSize(urls); i + 1 < url_count; i += 2)
|
||||
{
|
||||
auto id = int64_t{};
|
||||
auto newval = std::string_view{};
|
||||
|
||||
if (tr_variantGetInt(tr_variantListChild(urls, i), &id) &&
|
||||
tr_variantGetStrView(tr_variantListChild(urls, i + 1), &newval))
|
||||
{
|
||||
changed |= tor->info.announce_list->replace(id, newval);
|
||||
}
|
||||
}
|
||||
|
||||
char const* errmsg = nullptr;
|
||||
if (!changed)
|
||||
{
|
||||
errmsg = "invalid argument";
|
||||
}
|
||||
else if (!tr_torrentSetAnnounceList(tor, trackers, n))
|
||||
{
|
||||
errmsg = "error setting announce list";
|
||||
return "error setting announce list";
|
||||
}
|
||||
|
||||
freeTrackers(trackers, n);
|
||||
tr_free(tids);
|
||||
return errmsg;
|
||||
tor->info.announce_list->save(tor->info.torrent);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static char const* removeTrackers(tr_torrent* tor, tr_variant* ids)
|
||||
{
|
||||
auto const old_size = tor->trackerCount();
|
||||
|
||||
for (size_t i = 0, n = tr_variantListSize(ids); i < n; ++i)
|
||||
{
|
||||
auto id = int64_t{};
|
||||
auto* const val = tr_variantListChild(ids, i);
|
||||
if (val == nullptr || !tr_variantGetInt(val, &id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tor->info.announce_list->remove(id);
|
||||
}
|
||||
|
||||
if (tor->trackerCount() == old_size)
|
||||
{
|
||||
return "error setting announce list";
|
||||
}
|
||||
|
||||
tor->info.announce_list->save(tor->info.torrent);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static char const* torrentSet(
|
||||
|
|
|
@ -399,10 +399,10 @@ char* tr_torrentInfoGetMagnetLink(tr_info const* inf)
|
|||
tr_http_escape(s, name, true);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < inf->trackerCount; ++i)
|
||||
for (size_t i = 0, n = std::size(*inf->announce_list); i < n; ++i)
|
||||
{
|
||||
evbuffer_add_printf(s, "%s", "&tr=");
|
||||
tr_http_escape(s, inf->trackers[i].announce, true);
|
||||
tr_http_escape(s, inf->announce_list->at(i).announce.full, true);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < inf->webseedCount; i++)
|
||||
|
|
|
@ -764,7 +764,7 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
|
|||
tr_error_clear(&error);
|
||||
}
|
||||
|
||||
tor->tiers = tr_announcerAddTorrent(tor, onTrackerResponse, nullptr);
|
||||
tor->announcer_tiers = tr_announcerAddTorrent(tor, onTrackerResponse, nullptr);
|
||||
|
||||
if (isNewTorrent)
|
||||
{
|
||||
|
@ -2138,114 +2138,40 @@ bool tr_torrent::checkPiece(tr_piece_index_t piece)
|
|||
****
|
||||
***/
|
||||
|
||||
static int compareTrackerByTier(void const* va, void const* vb)
|
||||
{
|
||||
auto const* const a = static_cast<tr_tracker_info const*>(va);
|
||||
auto const* const b = static_cast<tr_tracker_info const*>(vb);
|
||||
|
||||
/* sort by tier */
|
||||
if (a->tier != b->tier)
|
||||
{
|
||||
return a->tier - b->tier;
|
||||
}
|
||||
|
||||
/* get the effects of a stable sort by comparing the two elements' addresses */
|
||||
return a - b;
|
||||
}
|
||||
|
||||
bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_in, int trackerCount)
|
||||
bool tr_torrentSetAnnounceList(tr_torrent* tor, char const* const* announce_urls, tr_tracker_tier_t const* tiers, size_t n)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
auto const lock = tor->unique_lock();
|
||||
|
||||
auto metainfo = tr_variant{};
|
||||
auto ok = bool{ true };
|
||||
|
||||
/* ensure the trackers' tiers are in ascending order */
|
||||
auto* trackers = static_cast<tr_tracker_info*>(tr_memdup(trackers_in, sizeof(tr_tracker_info) * trackerCount));
|
||||
qsort(trackers, trackerCount, sizeof(tr_tracker_info), compareTrackerByTier);
|
||||
|
||||
/* look for bad URLs */
|
||||
for (int i = 0; ok && i < trackerCount; ++i)
|
||||
auto announce_list = tr_announce_list();
|
||||
if (!announce_list.set(announce_urls, tiers, n) || !announce_list.save(tor->info.torrent))
|
||||
{
|
||||
if (!tr_urlIsValidTracker(trackers[i].announce))
|
||||
return false;
|
||||
}
|
||||
|
||||
std::swap(*tor->info.announce_list, announce_list);
|
||||
tr_torrentMarkEdited(tor);
|
||||
|
||||
/* if we had a tracker-related error on this torrent,
|
||||
* and that tracker's been removed,
|
||||
* then clear the error */
|
||||
if (tor->error == TR_STAT_TRACKER_WARNING || tor->error == TR_STAT_TRACKER_ERROR)
|
||||
{
|
||||
auto const error_url = tor->error_announce_url;
|
||||
|
||||
if (std::any_of(
|
||||
std::begin(*tor->info.announce_list),
|
||||
std::end(*tor->info.announce_list),
|
||||
[error_url](auto const& tracker) { return tracker.announce_interned == error_url; }))
|
||||
{
|
||||
ok = false;
|
||||
tr_torrentClearError(tor);
|
||||
}
|
||||
}
|
||||
|
||||
/* save to the .torrent file */
|
||||
if (ok && tr_variantFromFile(&metainfo, TR_VARIANT_PARSE_BENC, tor->info.torrent, nullptr))
|
||||
{
|
||||
/* remove the old fields */
|
||||
tr_variantDictRemove(&metainfo, TR_KEY_announce);
|
||||
tr_variantDictRemove(&metainfo, TR_KEY_announce_list);
|
||||
/* tell the announcer to reload this torrent's tracker list */
|
||||
tr_announcerResetTorrent(tor->session->announcer, tor);
|
||||
|
||||
/* add the new fields */
|
||||
if (trackerCount > 0)
|
||||
{
|
||||
tr_variantDictAddStr(&metainfo, TR_KEY_announce, trackers[0].announce);
|
||||
}
|
||||
|
||||
if (trackerCount > 1)
|
||||
{
|
||||
int prevTier = -1;
|
||||
tr_variant* tier = nullptr;
|
||||
tr_variant* announceList = tr_variantDictAddList(&metainfo, TR_KEY_announce_list, 0);
|
||||
|
||||
for (int i = 0; i < trackerCount; ++i)
|
||||
{
|
||||
if (prevTier != trackers[i].tier)
|
||||
{
|
||||
prevTier = trackers[i].tier;
|
||||
tier = tr_variantListAddList(announceList, 0);
|
||||
}
|
||||
|
||||
tr_variantListAddStr(tier, trackers[i].announce);
|
||||
}
|
||||
}
|
||||
|
||||
/* try to parse it back again, to make sure it's good */
|
||||
auto parsed = tr_metainfoParse(tor->session, &metainfo, nullptr);
|
||||
if (parsed)
|
||||
{
|
||||
/* it's good, so keep these new trackers and free the old ones */
|
||||
std::swap(tor->info.trackers, parsed->info.trackers);
|
||||
std::swap(tor->info.trackerCount, parsed->info.trackerCount);
|
||||
tr_torrentMarkEdited(tor);
|
||||
tr_variantToFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_variantFree(&metainfo);
|
||||
|
||||
/* if we had a tracker-related error on this torrent,
|
||||
* and that tracker's been removed,
|
||||
* then clear the error */
|
||||
if (tor->error == TR_STAT_TRACKER_WARNING || tor->error == TR_STAT_TRACKER_ERROR)
|
||||
{
|
||||
bool clear = true;
|
||||
|
||||
for (int i = 0; clear && i < trackerCount; ++i)
|
||||
{
|
||||
if (strcmp(trackers[i].announce, tr_quark_get_string(tor->error_announce_url)) == 0)
|
||||
{
|
||||
clear = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (clear)
|
||||
{
|
||||
tr_torrentClearError(tor);
|
||||
}
|
||||
}
|
||||
|
||||
/* tell the announcer to reload this torrent's tracker list */
|
||||
tr_announcerResetTorrent(tor->session->announcer, tor);
|
||||
}
|
||||
|
||||
tr_free(trackers);
|
||||
return ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "bandwidth.h"
|
||||
#include "bitfield.h"
|
||||
#include "block-info.h"
|
||||
|
@ -36,7 +37,7 @@ struct tr_magnet_info;
|
|||
struct tr_metainfo_parsed;
|
||||
struct tr_session;
|
||||
struct tr_torrent;
|
||||
struct tr_torrent_tiers;
|
||||
struct tr_announcer_tiers;
|
||||
|
||||
/**
|
||||
*** Package-visible ctor API
|
||||
|
@ -327,6 +328,23 @@ public:
|
|||
|
||||
std::optional<tr_found_file_t> findFile(std::string& filename, tr_file_index_t i) const;
|
||||
|
||||
/// TRACKERS
|
||||
|
||||
size_t trackerCount() const
|
||||
{
|
||||
return std::size(*info.announce_list);
|
||||
}
|
||||
|
||||
auto const& tracker(size_t i) const
|
||||
{
|
||||
return info.announce_list->at(i);
|
||||
}
|
||||
|
||||
auto tiers() const
|
||||
{
|
||||
return info.announce_list->tiers();
|
||||
}
|
||||
|
||||
/// WEBSEEDS
|
||||
|
||||
auto webseedCount() const
|
||||
|
@ -398,7 +416,7 @@ public:
|
|||
|
||||
tr_session* session = nullptr;
|
||||
|
||||
struct tr_torrent_tiers* tiers = nullptr;
|
||||
tr_announcer_tiers* announcer_tiers = nullptr;
|
||||
|
||||
// Changed to non-owning pointer temporarily till tr_torrent becomes C++-constructible and destructible
|
||||
// TODO: change tr_bandwidth* to owning pointer to the bandwidth, or remove * and own the value
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
****
|
||||
***/
|
||||
|
||||
#include <memory>
|
||||
#include <stdbool.h> /* bool */
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdint.h> /* uintN_t */
|
||||
|
@ -34,6 +35,9 @@ using tr_piece_index_t = uint32_t;
|
|||
using tr_block_index_t = uint32_t;
|
||||
using tr_port = uint16_t;
|
||||
using tr_tracker_tier_t = uint32_t;
|
||||
using tr_tracker_id_t = uint32_t;
|
||||
|
||||
#include "announce-list.h"
|
||||
|
||||
struct tr_block_span_t
|
||||
{
|
||||
|
@ -1215,15 +1219,6 @@ static inline char* tr_torrentGetMagnetLink(tr_torrent const* tor)
|
|||
***
|
||||
**/
|
||||
|
||||
/** @brief a part of tr_info that represents a single tracker */
|
||||
struct tr_tracker_info
|
||||
{
|
||||
int tier;
|
||||
char* announce;
|
||||
char* scrape;
|
||||
uint32_t id; /* unique identifier used to match to a tr_tracker_stat */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Modify a torrent's tracker list.
|
||||
*
|
||||
|
@ -1231,13 +1226,11 @@ struct tr_tracker_info
|
|||
* and the metainfo file in tr_sessionGetConfigDir()'s torrent subdirectory.
|
||||
*
|
||||
* @param torrent The torrent whose tracker list is to be modified
|
||||
* @param trackers An array of trackers, sorted by tier from first to last.
|
||||
* NOTE: only the `tier' and `announce' fields are used.
|
||||
* libtransmission derives `scrape' from `announce'
|
||||
* and reassigns 'id'.
|
||||
* @param trackerCount size of the `trackers' array
|
||||
* @param urls Array of n announce url strings
|
||||
* @param tiers Array of n tier numbers for grouping 'urls' into tiers
|
||||
* @param n the number of urls/tiers
|
||||
*/
|
||||
bool tr_torrentSetAnnounceList(tr_torrent* torrent, tr_tracker_info const* trackers, int trackerCount);
|
||||
bool tr_torrentSetAnnounceList(tr_torrent* torrent, char const* const* announce_urls, tr_tracker_tier_t const* tiers, size_t n);
|
||||
|
||||
/**
|
||||
***
|
||||
|
@ -1522,6 +1515,7 @@ struct tr_file_priv
|
|||
time_t mtime;
|
||||
bool is_renamed; // true if we're using a different path from the one in the metainfo; ie, if the user has renamed it */
|
||||
};
|
||||
|
||||
/** @brief a part of tr_info that represents a single file of the torrent's content */
|
||||
struct tr_file
|
||||
{
|
||||
|
@ -1557,13 +1551,12 @@ struct tr_info
|
|||
// Use tr_torrentFile() and tr_torrentFileCount() instead.
|
||||
tr_file* files;
|
||||
|
||||
/* these trackers are sorted by tier */
|
||||
tr_tracker_info* trackers;
|
||||
// TODO(ckerr) aggregate this directly, rather than using a shared_ptr, when tr_info is private
|
||||
std::shared_ptr<tr_announce_list> announce_list;
|
||||
|
||||
/* Torrent info */
|
||||
time_t dateCreated;
|
||||
|
||||
unsigned int trackerCount;
|
||||
unsigned int webseedCount;
|
||||
tr_file_index_t fileCount;
|
||||
uint32_t pieceSize;
|
||||
|
|
|
@ -119,7 +119,6 @@ enum tr_variant_parse_opts
|
|||
TR_VARIANT_PARSE_INPLACE = (1 << 2)
|
||||
};
|
||||
|
||||
/* TR_VARIANT_FMT_JSON_LEAN and TR_VARIANT_FMT_JSON are equivalent here. */
|
||||
bool tr_variantFromFile(tr_variant* setme, tr_variant_parse_opts opts, char const* filename, struct tr_error** error = nullptr);
|
||||
|
||||
bool tr_variantFromBuf(
|
||||
|
|
|
@ -156,21 +156,22 @@ OSStatus GeneratePreviewForURL(void* thisInterface, QLPreviewRequestRef preview,
|
|||
[lists addObject:listSection];
|
||||
}
|
||||
|
||||
if (inf.trackerCount > 0)
|
||||
if (!std::empty(*inf.announce_list))
|
||||
{
|
||||
NSMutableString* listSection = [NSMutableString string];
|
||||
[listSection appendString:@"<table>"];
|
||||
|
||||
NSString* headerTitleString = inf.trackerCount == 1 ?
|
||||
auto const n = std::size(*inf.announce_list);
|
||||
NSString* headerTitleString = n == 1 ?
|
||||
NSLocalizedStringFromTableInBundle(@"1 Tracker", nil, bundle, "quicklook tracker header") :
|
||||
[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ Trackers", nil, bundle, "quicklook tracker header"),
|
||||
[NSString formattedUInteger:inf.trackerCount]];
|
||||
[NSString formattedUInteger:n]];
|
||||
[listSection appendFormat:@"<tr><th>%@</th></tr>", headerTitleString];
|
||||
|
||||
#warning handle tiers?
|
||||
for (int i = 0; i < inf.trackerCount; ++i)
|
||||
for (auto const tracker : *inf.announce_list)
|
||||
{
|
||||
[listSection appendFormat:@"<tr><td>%s<td></tr>", inf.trackers[i].announce];
|
||||
[listSection appendFormat:@"<tr><td>%s<td></tr>", tr_quark_get_string(tracker.announce_interned)];
|
||||
}
|
||||
|
||||
[listSection appendString:@"</table>"];
|
||||
|
|
|
@ -763,61 +763,63 @@ bool trashDataFile(char const* filename, tr_error** error)
|
|||
|
||||
- (NSArray*)allTrackersFlat
|
||||
{
|
||||
NSMutableArray* allTrackers = [NSMutableArray arrayWithCapacity:fInfo->trackerCount];
|
||||
auto const n = tr_torrentTrackerCount(fHandle);
|
||||
NSMutableArray* allTrackers = [NSMutableArray arrayWithCapacity:n];
|
||||
|
||||
for (NSInteger i = 0; i < fInfo->trackerCount; i++)
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
[allTrackers addObject:@(fInfo->trackers[i].announce)];
|
||||
[allTrackers addObject:@(tr_torrentTracker(fHandle, i).announce)];
|
||||
}
|
||||
|
||||
return allTrackers;
|
||||
}
|
||||
|
||||
- (BOOL)addTrackerToNewTier:(NSString*)tracker
|
||||
- (BOOL)addTrackerToNewTier:(NSString*)new_tracker
|
||||
{
|
||||
tracker = [tracker stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
|
||||
|
||||
if ([tracker rangeOfString:@"://"].location == NSNotFound)
|
||||
new_tracker = [new_tracker stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
|
||||
if ([new_tracker rangeOfString:@"://"].location == NSNotFound)
|
||||
{
|
||||
tracker = [@"http://" stringByAppendingString:tracker];
|
||||
new_tracker = [@"http://" stringByAppendingString:new_tracker];
|
||||
}
|
||||
|
||||
//recreate the tracker structure
|
||||
int const oldTrackerCount = fInfo->trackerCount;
|
||||
tr_tracker_info* trackerStructs = tr_new(tr_tracker_info, oldTrackerCount + 1);
|
||||
for (int i = 0; i < oldTrackerCount; ++i)
|
||||
auto urls = std::vector<char const*>{};
|
||||
auto tiers = std::vector<tr_tracker_tier_t>{};
|
||||
|
||||
for (size_t i = 0, n = tr_torrentTrackerCount(fHandle); i < n; ++i)
|
||||
{
|
||||
trackerStructs[i] = fInfo->trackers[i];
|
||||
auto const tracker = tr_torrentTracker(fHandle, i);
|
||||
urls.push_back(tracker.announce);
|
||||
tiers.push_back(tracker.tier);
|
||||
}
|
||||
|
||||
trackerStructs[oldTrackerCount].announce = (char*)tracker.UTF8String;
|
||||
trackerStructs[oldTrackerCount].tier = trackerStructs[oldTrackerCount - 1].tier + 1;
|
||||
trackerStructs[oldTrackerCount].id = oldTrackerCount;
|
||||
urls.push_back(new_tracker.UTF8String);
|
||||
tiers.push_back(std::empty(tiers) ? 0 : tiers.back() + 1);
|
||||
|
||||
BOOL const success = tr_torrentSetAnnounceList(fHandle, trackerStructs, oldTrackerCount + 1);
|
||||
tr_free(trackerStructs);
|
||||
BOOL const success = tr_torrentSetAnnounceList(fHandle, std::data(urls), std::data(tiers), std::size(urls));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (void)removeTrackers:(NSSet*)trackers
|
||||
{
|
||||
//recreate the tracker structure
|
||||
tr_tracker_info* trackerStructs = tr_new(tr_tracker_info, fInfo->trackerCount);
|
||||
auto urls = std::vector<char const*>{};
|
||||
auto tiers = std::vector<tr_tracker_tier_t>{};
|
||||
|
||||
NSUInteger newCount = 0;
|
||||
for (NSUInteger i = 0; i < fInfo->trackerCount; i++)
|
||||
for (size_t i = 0, n = tr_torrentTrackerCount(fHandle); i < n; ++i)
|
||||
{
|
||||
if (![trackers containsObject:@(fInfo->trackers[i].announce)])
|
||||
auto const tracker = tr_torrentTracker(fHandle, i);
|
||||
|
||||
if ([trackers containsObject:@(tracker.announce)])
|
||||
{
|
||||
trackerStructs[newCount++] = fInfo->trackers[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
urls.push_back(tracker.announce);
|
||||
tiers.push_back(tracker.tier);
|
||||
}
|
||||
|
||||
BOOL const success = tr_torrentSetAnnounceList(fHandle, trackerStructs, newCount);
|
||||
BOOL const success = tr_torrentSetAnnounceList(fHandle, std::data(urls), std::data(tiers), std::size(urls));
|
||||
NSAssert(success, @"Removing tracker addresses failed");
|
||||
|
||||
tr_free(trackerStructs);
|
||||
}
|
||||
|
||||
- (NSString*)comment
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
add_executable(libtransmission-test
|
||||
announce-list-test.cc
|
||||
bitfield-test.cc
|
||||
block-info-test.cc
|
||||
blocklist-test.cc
|
||||
|
|
404
tests/libtransmission/announce-list-test.cc
Normal file
404
tests/libtransmission/announce-list-test.cc
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "metainfo.h"
|
||||
#include "utils.h"
|
||||
#include "variant.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using AnnounceListTest = ::testing::Test;
|
||||
using namespace std::literals;
|
||||
|
||||
TEST_F(AnnounceListTest, canAdd)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
||||
auto constexpr Announce = "https://example.org/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_EQ(1, announce_list.add(Tier, Announce));
|
||||
auto const tracker = announce_list.at(0);
|
||||
EXPECT_EQ(Announce, tracker.announce.full);
|
||||
EXPECT_EQ("https://example.org/scrape"sv, tracker.scrape.full);
|
||||
EXPECT_EQ(Tier, tracker.tier);
|
||||
EXPECT_EQ("example.org:443"sv, tr_quark_get_string_view(tracker.host));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, groupsSiblingsIntoSameTier)
|
||||
{
|
||||
auto constexpr Tier1 = tr_tracker_tier_t{ 1 };
|
||||
auto constexpr Tier2 = tr_tracker_tier_t{ 2 };
|
||||
auto constexpr Tier3 = tr_tracker_tier_t{ 3 };
|
||||
auto constexpr Announce1 = "https://example.org/announce"sv;
|
||||
auto constexpr Announce2 = "http://example.org/announce"sv;
|
||||
auto constexpr Announce3 = "udp://example.org:999/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier1, Announce1));
|
||||
EXPECT_TRUE(announce_list.add(Tier2, Announce2));
|
||||
EXPECT_TRUE(announce_list.add(Tier3, Announce3));
|
||||
|
||||
EXPECT_EQ(3, std::size(announce_list));
|
||||
EXPECT_EQ(Tier1, announce_list.at(0).tier);
|
||||
EXPECT_EQ(Tier1, announce_list.at(1).tier);
|
||||
EXPECT_EQ(Tier1, announce_list.at(2).tier);
|
||||
EXPECT_EQ(Announce1, announce_list.at(1).announce.full);
|
||||
EXPECT_EQ(Announce2, announce_list.at(0).announce.full);
|
||||
EXPECT_EQ(Announce3, announce_list.at(2).announce.full);
|
||||
EXPECT_EQ("example.org:443"sv, tr_quark_get_string_view(announce_list.at(1).host));
|
||||
EXPECT_EQ("example.org:80"sv, tr_quark_get_string_view(announce_list.at(0).host));
|
||||
EXPECT_EQ("example.org:999"sv, tr_quark_get_string_view(announce_list.at(2).host));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canAddWithoutScrape)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
||||
auto constexpr Announce = "https://example.org/foo"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
||||
auto const tracker = announce_list.at(0);
|
||||
EXPECT_EQ(Announce, tracker.announce.full);
|
||||
EXPECT_EQ(TR_KEY_NONE, tracker.scrape_interned);
|
||||
EXPECT_EQ(Tier, tracker.tier);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canAddUdp)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
||||
auto constexpr Announce = "udp://example.org/"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
||||
auto const tracker = announce_list.at(0);
|
||||
EXPECT_EQ(Announce, tracker.announce.full);
|
||||
EXPECT_EQ("udp://example.org/"sv, tracker.scrape.full);
|
||||
EXPECT_EQ(Tier, tracker.tier);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotAddDuplicateAnnounce)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
||||
auto constexpr Announce = "https://example.org/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
||||
EXPECT_EQ(1, announce_list.size());
|
||||
EXPECT_FALSE(announce_list.add(Tier, Announce));
|
||||
EXPECT_EQ(1, announce_list.size());
|
||||
|
||||
auto constexpr Announce2 = "https://example.org:443/announce"sv;
|
||||
EXPECT_FALSE(announce_list.add(Tier, Announce2));
|
||||
EXPECT_EQ(1, announce_list.size());
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotAddInvalidUrl)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
||||
auto constexpr Announce = "telnet://example.org/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_FALSE(announce_list.add(Tier, Announce));
|
||||
EXPECT_EQ(0, announce_list.size());
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canSet)
|
||||
{
|
||||
auto constexpr Urls = std::array<char const*, 3>{
|
||||
"https://www.example.com/a/announce",
|
||||
"https://www.example.com/b/announce",
|
||||
"https://www.example.com/c/announce",
|
||||
};
|
||||
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 1, 2, 3 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_EQ(3, announce_list.set(std::data(Urls), std::data(Tiers), 3));
|
||||
EXPECT_EQ(3, announce_list.size());
|
||||
EXPECT_EQ(Tiers[0], announce_list.at(0).tier);
|
||||
EXPECT_EQ(Tiers[1], announce_list.at(1).tier);
|
||||
EXPECT_EQ(Tiers[2], announce_list.at(2).tier);
|
||||
EXPECT_EQ(Urls[0], announce_list.at(0).announce.full);
|
||||
EXPECT_EQ(Urls[1], announce_list.at(1).announce.full);
|
||||
EXPECT_EQ(Urls[2], announce_list.at(2).announce.full);
|
||||
auto const expected_tiers = std::set<tr_tracker_tier_t>(std::begin(Tiers), std::end(Tiers));
|
||||
auto const tiers = announce_list.tiers();
|
||||
EXPECT_EQ(expected_tiers, tiers);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canSetUnsortedWithBackupsInTiers)
|
||||
{
|
||||
auto constexpr Urls = std::array<char const*, 6>{
|
||||
"https://www.backup-a.com/announce", "https://www.backup-b.com/announce", "https://www.backup-c.com/announce",
|
||||
"https://www.primary-a.com/announce", "https://www.primary-b.com/announce", "https://www.primary-c.com/announce",
|
||||
};
|
||||
auto constexpr Tiers = std::array<tr_tracker_tier_t, 6>{ 0, 1, 2, 0, 1, 2 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_EQ(6, announce_list.set(std::data(Urls), std::data(Tiers), 6));
|
||||
EXPECT_EQ(6, announce_list.size());
|
||||
EXPECT_EQ(0, announce_list.at(0).tier);
|
||||
EXPECT_EQ(0, announce_list.at(1).tier);
|
||||
EXPECT_EQ(1, announce_list.at(2).tier);
|
||||
EXPECT_EQ(1, announce_list.at(3).tier);
|
||||
EXPECT_EQ(2, announce_list.at(4).tier);
|
||||
EXPECT_EQ(2, announce_list.at(5).tier);
|
||||
EXPECT_EQ(Urls[0], announce_list.at(0).announce.full);
|
||||
EXPECT_EQ(Urls[3], announce_list.at(1).announce.full);
|
||||
EXPECT_EQ(Urls[1], announce_list.at(2).announce.full);
|
||||
EXPECT_EQ(Urls[4], announce_list.at(3).announce.full);
|
||||
EXPECT_EQ(Urls[2], announce_list.at(4).announce.full);
|
||||
EXPECT_EQ(Urls[5], announce_list.at(5).announce.full);
|
||||
|
||||
// confirm that each has a unique id
|
||||
auto ids = std::set<tr_tracker_id_t>{};
|
||||
for (size_t i = 0, n = std::size(announce_list); i < n; ++i)
|
||||
{
|
||||
ids.insert(announce_list.at(i).id);
|
||||
}
|
||||
EXPECT_EQ(std::size(announce_list), std::size(ids));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canSetExceptDuplicate)
|
||||
{
|
||||
auto constexpr Urls = std::array<char const*, 3>{
|
||||
"https://www.example.com/a/announce",
|
||||
"https://www.example.com/b/announce",
|
||||
"https://www.example.com/b/announce",
|
||||
};
|
||||
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 3, 2, 1 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_EQ(2, announce_list.set(std::data(Urls), std::data(Tiers), 3));
|
||||
EXPECT_EQ(2, announce_list.size());
|
||||
EXPECT_EQ(Tiers[0], announce_list.at(1).tier);
|
||||
EXPECT_EQ(Tiers[1], announce_list.at(0).tier);
|
||||
EXPECT_EQ(Urls[0], announce_list.at(1).announce.full);
|
||||
EXPECT_EQ(Urls[1], announce_list.at(0).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canSetExceptInvalid)
|
||||
{
|
||||
auto constexpr Urls = std::array<char const*, 3>{
|
||||
"https://www.example.com/a/announce",
|
||||
"telnet://www.example.com/b/announce",
|
||||
"https://www.example.com/c/announce",
|
||||
};
|
||||
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 1, 2, 3 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_EQ(2, announce_list.set(std::data(Urls), std::data(Tiers), 3));
|
||||
EXPECT_EQ(2, announce_list.size());
|
||||
EXPECT_EQ(Tiers[0], announce_list.at(0).tier);
|
||||
EXPECT_EQ(Tiers[2], announce_list.at(1).tier);
|
||||
EXPECT_EQ(Urls[0], announce_list.at(0).announce.full);
|
||||
EXPECT_EQ(Urls[2], announce_list.at(1).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canRemoveById)
|
||||
{
|
||||
auto constexpr Announce = "https://www.example.com/announce"sv;
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.add(Tier, Announce);
|
||||
EXPECT_EQ(1, std::size(announce_list));
|
||||
auto const id = announce_list.at(0).id;
|
||||
|
||||
EXPECT_TRUE(announce_list.remove(id));
|
||||
EXPECT_EQ(0, std::size(announce_list));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotRemoveByInvalidId)
|
||||
{
|
||||
auto constexpr Announce = "https://www.example.com/announce"sv;
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.add(Tier, Announce);
|
||||
EXPECT_EQ(1, std::size(announce_list));
|
||||
auto const id = announce_list.at(0).id;
|
||||
|
||||
EXPECT_FALSE(announce_list.remove(id + 1));
|
||||
EXPECT_EQ(1, std::size(announce_list));
|
||||
EXPECT_EQ(Announce, announce_list.at(0).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canRemoveByAnnounce)
|
||||
{
|
||||
auto constexpr Announce = "https://www.example.com/announce"sv;
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.add(Tier, Announce);
|
||||
EXPECT_EQ(1, std::size(announce_list));
|
||||
|
||||
EXPECT_TRUE(announce_list.remove(Announce));
|
||||
EXPECT_EQ(0, std::size(announce_list));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotRemoveByInvalidAnnounce)
|
||||
{
|
||||
auto constexpr Announce = "https://www.example.com/announce"sv;
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
announce_list.add(Tier, Announce);
|
||||
EXPECT_EQ(1, std::size(announce_list));
|
||||
|
||||
EXPECT_FALSE(announce_list.remove("https://www.not-example.com/announce"sv));
|
||||
EXPECT_EQ(1, std::size(announce_list));
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canReplace)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
auto constexpr Announce1 = "https://www.example.com/1/announce"sv;
|
||||
auto constexpr Announce2 = "https://www.example.com/2/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce1));
|
||||
EXPECT_TRUE(announce_list.replace(announce_list.at(0).id, Announce2));
|
||||
EXPECT_EQ(Announce2, announce_list.at(0).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotReplaceInvalidId)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
auto constexpr Announce1 = "https://www.example.com/1/announce"sv;
|
||||
auto constexpr Announce2 = "https://www.example.com/2/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce1));
|
||||
EXPECT_FALSE(announce_list.replace(announce_list.at(0).id + 1, Announce2));
|
||||
EXPECT_EQ(Announce1, announce_list.at(0).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotReplaceWithInvalidAnnounce)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
auto constexpr Announce1 = "https://www.example.com/1/announce"sv;
|
||||
auto constexpr Announce2 = "telnet://www.example.com/2/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce1));
|
||||
EXPECT_FALSE(announce_list.replace(announce_list.at(0).id, Announce2));
|
||||
EXPECT_EQ(Announce1, announce_list.at(0).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, canNotReplaceWithDuplicate)
|
||||
{
|
||||
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
||||
auto constexpr Announce = "https://www.example.com/announce"sv;
|
||||
|
||||
auto announce_list = tr_announce_list{};
|
||||
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
||||
EXPECT_FALSE(announce_list.replace(announce_list.at(0).id, Announce));
|
||||
EXPECT_EQ(Announce, announce_list.at(0).announce.full);
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, announceToScrape)
|
||||
{
|
||||
struct ScrapeTest
|
||||
{
|
||||
std::string_view announce;
|
||||
std::string_view expected_scrape;
|
||||
};
|
||||
|
||||
auto constexpr Tests = std::array<ScrapeTest, 3>{ {
|
||||
{ "https://www.example.com/announce"sv, "https://www.example.com/scrape"sv },
|
||||
{ "https://www.example.com/foo"sv, ""sv },
|
||||
{ "udp://www.example.com:999/"sv, "udp://www.example.com:999/"sv },
|
||||
} };
|
||||
|
||||
for (auto const test : Tests)
|
||||
{
|
||||
auto const scrape = tr_announce_list::announceToScrape(tr_quark_new(test.announce));
|
||||
EXPECT_EQ(tr_quark_new(test.expected_scrape), scrape);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AnnounceListTest, save)
|
||||
{
|
||||
auto constexpr Urls = std::array<char const*, 3>{
|
||||
"https://www.example.com/a/announce",
|
||||
"https://www.example.com/b/announce",
|
||||
"https://www.example.com/c/announce",
|
||||
};
|
||||
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 0, 1, 2 };
|
||||
|
||||
// first, set up a scratch .torrent
|
||||
auto constexpr* const OriginalFile = LIBTRANSMISSION_TEST_ASSETS_DIR "/Android-x86 8.1 r6 iso.torrent";
|
||||
auto original_content = std::vector<char>{};
|
||||
auto const test_file = tr_strvJoin(::testing::TempDir(), "transmission-announce-list-test.torrent"sv);
|
||||
tr_error* error = nullptr;
|
||||
EXPECT_TRUE(tr_loadFile(original_content, OriginalFile, &error));
|
||||
EXPECT_EQ(nullptr, error);
|
||||
EXPECT_TRUE(tr_saveFile(test_file.c_str(), { std::data(original_content), std::size(original_content) }, &error));
|
||||
EXPECT_EQ(nullptr, error);
|
||||
|
||||
// make an announce_list for it
|
||||
auto announce_list = tr_announce_list();
|
||||
EXPECT_TRUE(announce_list.add(Tiers[0], Urls[0]));
|
||||
EXPECT_TRUE(announce_list.add(Tiers[1], Urls[1]));
|
||||
EXPECT_TRUE(announce_list.add(Tiers[2], Urls[2]));
|
||||
|
||||
// try saving to a nonexistent .torrent file
|
||||
EXPECT_FALSE(announce_list.save("/this/path/does/not/exist", &error));
|
||||
EXPECT_NE(nullptr, error);
|
||||
EXPECT_NE(0, error->code);
|
||||
tr_error_clear(&error);
|
||||
|
||||
// now save to a real .torrent fi le
|
||||
EXPECT_TRUE(announce_list.save(test_file.c_str(), &error));
|
||||
EXPECT_EQ(nullptr, error);
|
||||
|
||||
// load the original
|
||||
auto metainfo = tr_variant{};
|
||||
EXPECT_TRUE(tr_variantFromBuf(
|
||||
&metainfo,
|
||||
TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE,
|
||||
{ std::data(original_content), std::size(original_content) },
|
||||
nullptr,
|
||||
nullptr));
|
||||
auto original = tr_metainfoParse(nullptr, &metainfo, nullptr);
|
||||
EXPECT_TRUE(original);
|
||||
tr_variantFree(&metainfo);
|
||||
|
||||
// load the scratch that we saved to
|
||||
metainfo = tr_variant{};
|
||||
EXPECT_TRUE(tr_variantFromFile(&metainfo, TR_VARIANT_PARSE_BENC, test_file.c_str(), nullptr));
|
||||
auto saved = tr_metainfoParse(nullptr, &metainfo, nullptr);
|
||||
EXPECT_TRUE(saved);
|
||||
tr_variantFree(&metainfo);
|
||||
|
||||
// test that non-announce parts of the metainfo are the same
|
||||
EXPECT_STREQ(original->info.name, saved->info.name);
|
||||
EXPECT_EQ(original->info.fileCount, saved->info.fileCount);
|
||||
EXPECT_EQ(original->info.dateCreated, saved->info.dateCreated);
|
||||
EXPECT_EQ(original->pieces, saved->pieces);
|
||||
|
||||
// test that the saved version has the updated announce list
|
||||
EXPECT_EQ(std::size(announce_list), std::size(*saved->info.announce_list));
|
||||
EXPECT_TRUE(std::equal(
|
||||
std::begin(announce_list),
|
||||
std::end(announce_list),
|
||||
std::begin(*saved->info.announce_list),
|
||||
std::end(*saved->info.announce_list)));
|
||||
|
||||
// cleanup
|
||||
std::remove(test_file.c_str());
|
||||
}
|
|
@ -46,15 +46,15 @@ TEST(MagnetMetainfo, magnetParse)
|
|||
auto mm = tr_magnet_metainfo{};
|
||||
|
||||
EXPECT_TRUE(mm.parseMagnet(uri));
|
||||
EXPECT_EQ(2, std::size(mm.trackers));
|
||||
auto it = std::begin(mm.trackers);
|
||||
EXPECT_EQ(0, it->first);
|
||||
EXPECT_EQ("http://tracker.openbittorrent.com/announce"sv, tr_quark_get_string_view(it->second.announce_url));
|
||||
EXPECT_EQ("http://tracker.openbittorrent.com/scrape"sv, tr_quark_get_string_view(it->second.scrape_url));
|
||||
EXPECT_EQ(2, std::size(mm.announce_list));
|
||||
auto it = std::begin(mm.announce_list);
|
||||
EXPECT_EQ(0, it->tier);
|
||||
EXPECT_EQ("http://tracker.openbittorrent.com/announce"sv, it->announce.full);
|
||||
EXPECT_EQ("http://tracker.openbittorrent.com/scrape"sv, it->scrape.full);
|
||||
++it;
|
||||
EXPECT_EQ(1, it->first);
|
||||
EXPECT_EQ("http://tracker.opentracker.org/announce", tr_quark_get_string_view(it->second.announce_url));
|
||||
EXPECT_EQ("http://tracker.opentracker.org/scrape", tr_quark_get_string_view(it->second.scrape_url));
|
||||
EXPECT_EQ(1, it->tier);
|
||||
EXPECT_EQ("http://tracker.opentracker.org/announce", it->announce.full);
|
||||
EXPECT_EQ("http://tracker.opentracker.org/scrape", it->scrape.full);
|
||||
EXPECT_EQ(1, std::size(mm.webseed_urls));
|
||||
EXPECT_EQ("http://server.webseed.org/path/to/file"sv, mm.webseed_urls.front());
|
||||
EXPECT_EQ("Display Name"sv, mm.name);
|
||||
|
|
|
@ -82,7 +82,7 @@ protected:
|
|||
EXPECT_EQ(tr_file_index_t{ 1 }, inf.fileCount);
|
||||
EXPECT_EQ(isPrivate, inf.isPrivate);
|
||||
EXPECT_FALSE(inf.isFolder);
|
||||
EXPECT_EQ(trackerCount, inf.trackerCount);
|
||||
EXPECT_EQ(trackerCount, std::size(*inf.announce_list));
|
||||
|
||||
// cleanup
|
||||
tr_ctorFree(ctor);
|
||||
|
@ -168,7 +168,7 @@ protected:
|
|||
EXPECT_EQ(payload_count, inf.fileCount);
|
||||
EXPECT_EQ(is_private, inf.isPrivate);
|
||||
EXPECT_EQ(builder->isFolder, inf.isFolder);
|
||||
EXPECT_EQ(tracker_count, inf.trackerCount);
|
||||
EXPECT_EQ(tracker_count, std::size(*inf.announce_list));
|
||||
|
||||
// cleanup
|
||||
tr_ctorFree(ctor);
|
||||
|
|
|
@ -39,14 +39,15 @@ TEST(Metainfo, magnetLink)
|
|||
auto const parse_result = tr_torrentParse(ctor, &inf);
|
||||
EXPECT_EQ(TR_PARSE_OK, parse_result);
|
||||
EXPECT_EQ(0, inf.fileCount); // because it's a magnet link
|
||||
EXPECT_EQ(2, inf.trackerCount);
|
||||
if (inf.trackerCount >= 1)
|
||||
auto const n = std::size(*inf.announce_list);
|
||||
EXPECT_EQ(2, n);
|
||||
if (n >= 1)
|
||||
{
|
||||
EXPECT_STREQ("http://tracker.publicbt.com/announce", inf.trackers[0].announce);
|
||||
EXPECT_EQ("http://tracker.publicbt.com/announce"sv, inf.announce_list->at(0).announce.full);
|
||||
}
|
||||
if (inf.trackerCount >= 2)
|
||||
if (n >= 2)
|
||||
{
|
||||
EXPECT_STREQ("udp://tracker.publicbt.com:80", inf.trackers[1].announce);
|
||||
EXPECT_EQ("udp://tracker.publicbt.com:80"sv, inf.announce_list->at(1).announce.full);
|
||||
}
|
||||
EXPECT_EQ(1, inf.webseedCount);
|
||||
if (inf.webseedCount >= 1)
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#define MY_NAME "transmission-show"
|
||||
#define TIMEOUT_SECS 30
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
static tr_option options[] = {
|
||||
{ 'm', "magnet", "Give a magnet link for the specified torrent", "m", false, nullptr },
|
||||
{ 's', "scrape", "Ask the torrent's trackers how many peers are in the torrent's swarm", "s", false, nullptr },
|
||||
|
@ -115,7 +117,6 @@ static void showInfo(tr_info const* inf)
|
|||
{
|
||||
char buf[128];
|
||||
tr_file** files;
|
||||
int prevTier = -1;
|
||||
|
||||
/**
|
||||
*** General Info
|
||||
|
@ -132,7 +133,7 @@ static void showInfo(tr_info const* inf)
|
|||
printf(" Comment: %s\n", inf->comment);
|
||||
}
|
||||
|
||||
printf(" Piece Count: %d\n", inf->pieceCount);
|
||||
printf(" Piece Count: %zu\n", size_t(inf->pieceCount));
|
||||
printf(" Piece Size: %s\n", tr_formatter_mem_B(buf, inf->pieceSize, sizeof(buf)));
|
||||
printf(" Total Size: %s\n", tr_formatter_size_B(buf, inf->totalSize, sizeof(buf)));
|
||||
printf(" Privacy: %s\n", inf->isPrivate ? "Private torrent" : "Public torrent");
|
||||
|
@ -142,16 +143,18 @@ static void showInfo(tr_info const* inf)
|
|||
**/
|
||||
|
||||
printf("\nTRACKERS\n");
|
||||
|
||||
for (unsigned int i = 0; i < inf->trackerCount; ++i)
|
||||
auto current_tier = std::optional<tr_tracker_tier_t>{};
|
||||
auto print_tier = size_t{ 1 };
|
||||
for (auto const& tracker : *inf->announce_list)
|
||||
{
|
||||
if (prevTier != inf->trackers[i].tier)
|
||||
if (!current_tier || current_tier != tracker.tier)
|
||||
{
|
||||
prevTier = inf->trackers[i].tier;
|
||||
printf("\n Tier #%d\n", prevTier + 1);
|
||||
current_tier = tracker.tier;
|
||||
printf("\n Tier #%zu\n", print_tier);
|
||||
++print_tier;
|
||||
}
|
||||
|
||||
printf(" %s\n", inf->trackers[i].announce);
|
||||
printf(" %" TR_PRIsv "\n", TR_PRIsv_ARG(tracker.announce.full));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,33 +218,28 @@ static CURL* tr_curl_easy_init(struct evbuffer* writebuf)
|
|||
|
||||
static void doScrape(tr_info const* inf)
|
||||
{
|
||||
for (unsigned int i = 0; i < inf->trackerCount; ++i)
|
||||
for (auto const& tracker : *inf->announce_list)
|
||||
{
|
||||
CURL* curl;
|
||||
CURLcode res;
|
||||
struct evbuffer* buf;
|
||||
char const* scrape = inf->trackers[i].scrape;
|
||||
char* url;
|
||||
char escaped[SHA_DIGEST_LENGTH * 3 + 1];
|
||||
|
||||
if (scrape == nullptr)
|
||||
if (tracker.scrape_interned == TR_KEY_NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char escaped[SHA_DIGEST_LENGTH * 3 + 1];
|
||||
tr_http_escape_sha1(escaped, inf->hash);
|
||||
auto const scrape = tracker.scrape.full;
|
||||
auto const url = tr_strvJoin(scrape, (tr_strvContains(scrape, '?') ? "&"sv : "?"sv), "info_hash="sv, escaped);
|
||||
|
||||
url = tr_strdup_printf("%s%cinfo_hash=%s", scrape, strchr(scrape, '?') != nullptr ? '&' : '?', escaped);
|
||||
|
||||
printf("%s ... ", url);
|
||||
printf("%" TR_PRIsv " ... ", TR_PRIsv_ARG(url));
|
||||
fflush(stdout);
|
||||
|
||||
buf = evbuffer_new();
|
||||
curl = tr_curl_easy_init(buf);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
auto* const buf = evbuffer_new();
|
||||
auto* const curl = tr_curl_easy_init(buf);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, TIMEOUT_SECS);
|
||||
|
||||
if ((res = curl_easy_perform(curl)) != CURLE_OK)
|
||||
auto const res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
{
|
||||
printf("error: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
@ -305,7 +303,6 @@ static void doScrape(tr_info const* inf)
|
|||
|
||||
curl_easy_cleanup(curl);
|
||||
evbuffer_free(buf);
|
||||
tr_free(url);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +324,7 @@ int tr_main(int argc, char* argv[])
|
|||
|
||||
if (showVersion)
|
||||
{
|
||||
fprintf(stderr, MY_NAME " " LONG_VERSION_STRING "\n");
|
||||
fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue