refactor: strbuf for metainfo files (#2833)
* refactor: tr_ctorSaveContents takes a string_view filename * refactor: remove tr_ctorSaveMagnetContents * refactor: announce_list::save takes a std::string_view * refactor: magnet() takes an OutputIt arg Generate the magnet link URL into an output iterator * refactor: remove deprecated calls to tr_http_escape * refactor: tr_torrent.torrentFile takes an OutputIterator * refactor: tr_torrent.torrentFile returns a tr_pathbuf * refactor: tr_torrent_metainfo.makeFilename returns a tr_pathbuf * refactor: use tr_urlbuf in announcer-http
This commit is contained in:
parent
3ddf76b560
commit
2996e223dd
|
@ -223,7 +223,7 @@ bool tr_announce_list::canAdd(tr_url_parsed_t const& announce)
|
|||
return std::none_of(std::begin(trackers_), std::end(trackers_), is_same);
|
||||
}
|
||||
|
||||
bool tr_announce_list::save(std::string const& torrent_file, tr_error** error) const
|
||||
bool tr_announce_list::save(std::string_view torrent_file, tr_error** error) const
|
||||
{
|
||||
// load the torrent file
|
||||
auto metainfo = tr_variant{};
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
bool parse(std::string_view text);
|
||||
[[nodiscard]] std::string toString() const;
|
||||
|
||||
bool save(std::string const& torrent_file, tr_error** error = nullptr) const;
|
||||
bool save(std::string_view torrent_file, tr_error** error = nullptr) const;
|
||||
|
||||
static std::optional<std::string> announceToScrape(std::string_view announce);
|
||||
static tr_quark announceToScrape(tr_quark announce);
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
#include <climits> /* USHRT_MAX */
|
||||
#include <cstdio> /* fprintf() */
|
||||
#include <cstring> /* strchr(), memcmp(), memcpy() */
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/http.h> /* for HTTP_OK */
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
@ -46,58 +46,56 @@ static char const* get_event_string(tr_announce_request const* req)
|
|||
return req->partial_seed && (req->event != TR_ANNOUNCE_EVENT_STOPPED) ? "paused" : tr_announce_event_get_string(req->event);
|
||||
}
|
||||
|
||||
static std::string announce_url_new(tr_session const* session, tr_announce_request const* req)
|
||||
static tr_urlbuf announce_url_new(tr_session const* session, tr_announce_request const* req)
|
||||
{
|
||||
auto const announce_sv = req->announce_url.sv();
|
||||
auto url = tr_urlbuf{};
|
||||
auto out = std::back_inserter(url);
|
||||
|
||||
auto escaped_info_hash = std::array<char, SHA_DIGEST_LENGTH * 3 + 1>{};
|
||||
tr_http_escape_sha1(std::data(escaped_info_hash), req->info_hash);
|
||||
|
||||
auto* const buf = evbuffer_new();
|
||||
evbuffer_expand(buf, 1024);
|
||||
evbuffer_add_printf(
|
||||
buf,
|
||||
"%" TR_PRIsv
|
||||
"%c"
|
||||
"info_hash=%s"
|
||||
"&peer_id=%" TR_PRIsv
|
||||
"&port=%d"
|
||||
"&uploaded=%" PRIu64 //
|
||||
"&downloaded=%" PRIu64 //
|
||||
"&left=%" PRIu64
|
||||
"&numwant=%d"
|
||||
"&key=%x"
|
||||
fmt::format_to(
|
||||
out,
|
||||
"{url}"
|
||||
"{sep}info_hash={info_hash}"
|
||||
"&peer_id={peer_id}"
|
||||
"&port={port}"
|
||||
"&uploaded={uploaded}"
|
||||
"&downloaded={downloaded}"
|
||||
"&left={left}"
|
||||
"&numwant={numwant}"
|
||||
"&key={key}"
|
||||
"&compact=1"
|
||||
"&supportcrypto=1",
|
||||
TR_PRIsv_ARG(announce_sv),
|
||||
announce_sv.find('?') == std::string_view::npos ? '?' : '&',
|
||||
std::data(escaped_info_hash),
|
||||
TR_PRIsv_ARG(req->peer_id),
|
||||
req->port,
|
||||
req->up,
|
||||
req->down,
|
||||
req->leftUntilComplete,
|
||||
req->numwant,
|
||||
req->key);
|
||||
fmt::arg("url", req->announce_url),
|
||||
fmt::arg("sep", tr_strvContains(req->announce_url.sv(), '?') ? '&' : '?'),
|
||||
fmt::arg("info_hash", std::data(escaped_info_hash)),
|
||||
fmt::arg("peer_id", std::string_view{ std::data(req->peer_id), std::size(req->peer_id) }),
|
||||
fmt::arg("port", req->port),
|
||||
fmt::arg("uploaded", req->up),
|
||||
fmt::arg("downloaded", req->down),
|
||||
fmt::arg("left", req->leftUntilComplete),
|
||||
fmt::arg("numwant", req->numwant),
|
||||
fmt::arg("key", req->key));
|
||||
|
||||
if (session->encryptionMode == TR_ENCRYPTION_REQUIRED)
|
||||
{
|
||||
evbuffer_add_printf(buf, "&requirecrypto=1");
|
||||
fmt::format_to(out, "&requirecrypto=1");
|
||||
}
|
||||
|
||||
if (req->corrupt != 0)
|
||||
{
|
||||
evbuffer_add_printf(buf, "&corrupt=%" PRIu64, req->corrupt);
|
||||
fmt::format_to(out, "&corrupt={}", req->corrupt);
|
||||
}
|
||||
|
||||
if (char const* str = get_event_string(req); !tr_str_is_empty(str))
|
||||
{
|
||||
evbuffer_add_printf(buf, "&event=%s", str);
|
||||
fmt::format_to(out, "&event={}", str);
|
||||
}
|
||||
|
||||
if (!std::empty(req->tracker_id))
|
||||
{
|
||||
evbuffer_add_printf(buf, "&trackerid=%" TR_PRIsv, TR_PRIsv_ARG(req->tracker_id));
|
||||
fmt::format_to(out, "&trackerid={}", req->tracker_id);
|
||||
}
|
||||
|
||||
/* There are two incompatible techniques for announcing an IPv6 address.
|
||||
|
@ -113,11 +111,11 @@ static std::string announce_url_new(tr_session const* session, tr_announce_reque
|
|||
{
|
||||
auto ipv6_readable = std::array<char, INET6_ADDRSTRLEN>{};
|
||||
evutil_inet_ntop(AF_INET6, ipv6, std::data(ipv6_readable), std::size(ipv6_readable));
|
||||
evbuffer_add_printf(buf, "&ipv6=");
|
||||
tr_http_escape(buf, std::data(ipv6_readable), true);
|
||||
fmt::format_to(out, "&ipv6=");
|
||||
tr_http_escape(out, std::data(ipv6_readable), true);
|
||||
}
|
||||
|
||||
return evbuffer_free_to_str(buf);
|
||||
return url;
|
||||
}
|
||||
|
||||
static void verboseLog(std::string_view description, tr_direction direction, std::string_view message)
|
||||
|
@ -343,7 +341,7 @@ void tr_tracker_http_announce(
|
|||
auto const url = announce_url_new(session, request);
|
||||
tr_logAddTrace(fmt::format("Sending announce to libcurl: '{}'", url), request->log_name);
|
||||
|
||||
auto options = tr_web::FetchOptions{ url, onAnnounceDone, d };
|
||||
auto options = tr_web::FetchOptions{ url.sv(), onAnnounceDone, d };
|
||||
options.timeout_secs = 90L;
|
||||
options.sndbuf = 1024;
|
||||
options.rcvbuf = 3072;
|
||||
|
@ -498,23 +496,22 @@ static void onScrapeDone(tr_web::FetchResponse const& web_response)
|
|||
delete data;
|
||||
}
|
||||
|
||||
static std::string scrape_url_new(tr_scrape_request const* req)
|
||||
static auto scrape_url_new(tr_scrape_request const* req)
|
||||
{
|
||||
auto const sv = req->scrape_url.sv();
|
||||
char delimiter = tr_strvContains(sv, '?') ? '&' : '?';
|
||||
|
||||
auto* const buf = evbuffer_new();
|
||||
evbuffer_add(buf, std::data(sv), std::size(sv));
|
||||
auto scrape_url = tr_pathbuf{ sv };
|
||||
|
||||
char delimiter = sv.find('?') == std::string_view::npos ? '?' : '&';
|
||||
for (int i = 0; i < req->info_hash_count; ++i)
|
||||
{
|
||||
char str[SHA_DIGEST_LENGTH * 3 + 1];
|
||||
tr_http_escape_sha1(str, req->info_hash[i]);
|
||||
evbuffer_add_printf(buf, "%cinfo_hash=%s", delimiter, str);
|
||||
scrape_url.append(delimiter, "info_hash=", str);
|
||||
delimiter = '&';
|
||||
}
|
||||
|
||||
return evbuffer_free_to_str(buf);
|
||||
return scrape_url;
|
||||
}
|
||||
|
||||
void tr_tracker_http_scrape(
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
@ -1297,7 +1296,7 @@ static void checkMultiscrapeMax(tr_announcer* announcer, tr_scrape_response cons
|
|||
{
|
||||
// don't log the full URL, since that might have a personal announce id
|
||||
// (note: we know 'parsed' will be successful since this url has a scrape_info)
|
||||
auto const parsed = *tr_urlParse(url.sv());
|
||||
auto const parsed = *tr_urlParse(url);
|
||||
auto clean_url = std::string{};
|
||||
tr_buildBuf(clean_url, parsed.scheme, "://"sv, parsed.host, ":"sv, parsed.portstr);
|
||||
tr_logAddDebug(fmt::format("Reducing multiscrape max to {}", n), clean_url);
|
||||
|
@ -1325,8 +1324,6 @@ static void on_scrape_done(tr_scrape_response const* response, void* vsession)
|
|||
continue;
|
||||
}
|
||||
|
||||
auto const scrape_url_sv = response->scrape_url.sv();
|
||||
|
||||
tr_logAddTraceTier(
|
||||
tier,
|
||||
fmt::format(
|
||||
|
@ -1340,7 +1337,7 @@ static void on_scrape_done(tr_scrape_response const* response, void* vsession)
|
|||
"downloaders:{} "
|
||||
"min_request_interval:{} "
|
||||
"err:{} ",
|
||||
scrape_url_sv,
|
||||
response->scrape_url.sv(),
|
||||
response->did_connect,
|
||||
response->did_timeout,
|
||||
row.seeders,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <cctype> // isxdigit()
|
||||
#include <iterator> // back_inserter
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
@ -150,29 +150,26 @@ std::optional<tr_sha1_digest_t> parseHash(std::string_view sv)
|
|||
****
|
||||
***/
|
||||
|
||||
std::string tr_magnet_metainfo::magnet() const
|
||||
tr_urlbuf tr_magnet_metainfo::magnet() const
|
||||
{
|
||||
auto s = std::string{};
|
||||
|
||||
s += "magnet:?xt=urn:btih:"sv;
|
||||
s += infoHashString();
|
||||
auto s = tr_urlbuf{ "magnet:?xt=urn:btih:"sv, infoHashString() };
|
||||
|
||||
if (!std::empty(name_))
|
||||
{
|
||||
s += "&dn="sv;
|
||||
tr_http_escape(s, name_, true);
|
||||
tr_http_escape(std::back_inserter(s), name_, true);
|
||||
}
|
||||
|
||||
for (auto const& tracker : this->announceList())
|
||||
{
|
||||
s += "&tr="sv;
|
||||
tr_http_escape(s, tracker.announce.full, true);
|
||||
tr_http_escape(std::back_inserter(s), tracker.announce.full, true);
|
||||
}
|
||||
|
||||
for (auto const& webseed : webseed_urls_)
|
||||
{
|
||||
s += "&ws="sv;
|
||||
tr_http_escape(s, webseed, true);
|
||||
tr_http_escape(std::back_inserter(s), webseed, true);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "transmission.h"
|
||||
|
||||
#include "announce-list.h"
|
||||
#include "tr-strbuf.h" // tr_urlbuf
|
||||
|
||||
struct tr_error;
|
||||
struct tr_variant;
|
||||
|
@ -21,7 +22,7 @@ class tr_magnet_metainfo
|
|||
public:
|
||||
bool parseMagnet(std::string_view magnet_link, tr_error** error = nullptr);
|
||||
|
||||
std::string magnet() const;
|
||||
[[nodiscard]] tr_urlbuf magnet() const;
|
||||
|
||||
auto const& infoHash() const
|
||||
{
|
||||
|
|
|
@ -958,7 +958,8 @@ void save(tr_torrent* tor)
|
|||
saveLabels(&top, tor);
|
||||
saveGroup(&top, tor);
|
||||
|
||||
if (auto const err = tr_variantToFile(&top, TR_VARIANT_FMT_BENC, tor->resumeFile()); err != 0)
|
||||
auto const resume_file = tor->resumeFile();
|
||||
if (auto const err = tr_variantToFile(&top, TR_VARIANT_FMT_BENC, resume_file); err != 0)
|
||||
{
|
||||
tor->setLocalError(tr_strvJoin("Unable to save resume file: ", tr_strerror(err)));
|
||||
}
|
||||
|
|
|
@ -794,7 +794,7 @@ static void initField(tr_torrent const* const tor, tr_stat const* const st, tr_v
|
|||
}
|
||||
|
||||
case TR_KEY_torrentFile:
|
||||
tr_variantInitStrView(initme, tor->torrentFile());
|
||||
tr_variantInitStr(initme, tor->torrentFile());
|
||||
break;
|
||||
|
||||
case TR_KEY_totalSize:
|
||||
|
@ -1084,6 +1084,7 @@ static char const* addTrackerUrls(tr_torrent* tor, tr_variant* urls)
|
|||
}
|
||||
|
||||
tor->announceList().save(tor->torrentFile());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1109,6 +1110,7 @@ static char const* replaceTrackers(tr_torrent* tor, tr_variant* urls)
|
|||
}
|
||||
|
||||
tor->announceList().save(tor->torrentFile());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1134,6 +1136,7 @@ static char const* removeTrackers(tr_torrent* tor, tr_variant* ids)
|
|||
}
|
||||
|
||||
tor->announceList().save(tor->torrentFile());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,8 +124,14 @@ tr_peer_id_t tr_peerIdInit()
|
|||
|
||||
std::optional<std::string> tr_session::WebMediator::cookieFile() const
|
||||
{
|
||||
auto const str = tr_strvPath(session_->config_dir, "cookies.txt");
|
||||
return tr_sys_path_exists(str.c_str()) ? std::optional<std::string>{ str } : std::nullopt;
|
||||
auto const path = tr_pathbuf{ session_->config_dir, "/cookies.txt" };
|
||||
|
||||
if (!tr_sys_path_exists(path))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::string{ path };
|
||||
}
|
||||
|
||||
std::optional<std::string> tr_session::WebMediator::userAgent() const
|
||||
|
@ -489,7 +495,7 @@ bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const
|
|||
auto fileSettings = tr_variant{};
|
||||
auto const filename = tr_pathbuf{ config_dir, "/settings.json"sv };
|
||||
auto success = bool{};
|
||||
if (tr_error* error = nullptr; tr_variantFromFile(&fileSettings, TR_VARIANT_PARSE_JSON, filename.sv(), &error))
|
||||
if (tr_error* error = nullptr; tr_variantFromFile(&fileSettings, TR_VARIANT_PARSE_JSON, filename, &error))
|
||||
{
|
||||
tr_variantMergeDicts(dict, &fileSettings);
|
||||
tr_variantFree(&fileSettings);
|
||||
|
@ -518,7 +524,7 @@ void tr_sessionSaveSettings(tr_session* session, char const* config_dir, tr_vari
|
|||
{
|
||||
tr_variant fileSettings;
|
||||
|
||||
if (tr_variantFromFile(&fileSettings, TR_VARIANT_PARSE_JSON, filename.sv(), nullptr))
|
||||
if (tr_variantFromFile(&fileSettings, TR_VARIANT_PARSE_JSON, filename, nullptr))
|
||||
{
|
||||
tr_variantMergeDicts(&settings, &fileSettings);
|
||||
tr_variantFree(&fileSettings);
|
||||
|
@ -538,7 +544,7 @@ void tr_sessionSaveSettings(tr_session* session, char const* config_dir, tr_vari
|
|||
}
|
||||
|
||||
/* save the result */
|
||||
tr_variantToFile(&settings, TR_VARIANT_FMT_JSON, filename.sv());
|
||||
tr_variantToFile(&settings, TR_VARIANT_FMT_JSON, filename);
|
||||
|
||||
/* cleanup */
|
||||
tr_variantFree(&settings);
|
||||
|
@ -2033,9 +2039,9 @@ static void sessionLoadTorrents(struct sessionLoadTorrentsData* const data)
|
|||
auto const path = tr_pathbuf{ dirname_sv, "/"sv, name };
|
||||
|
||||
// is a magnet link?
|
||||
if (!tr_ctorSetMetainfoFromFile(data->ctor, std::string{ path.sv() }, nullptr))
|
||||
if (!tr_ctorSetMetainfoFromFile(data->ctor, std::string{ path }, nullptr))
|
||||
{
|
||||
if (auto buf = std::vector<char>{}; tr_loadFile(path.sv(), buf))
|
||||
if (auto buf = std::vector<char>{}; tr_loadFile(path, buf))
|
||||
{
|
||||
tr_ctorSetMetainfoFromMagnetLink(
|
||||
data->ctor,
|
||||
|
@ -2876,7 +2882,7 @@ static void bandwidthGroupRead(tr_session* session, std::string_view config_dir)
|
|||
{
|
||||
auto const filename = tr_pathbuf{ config_dir, "/"sv, BandwidthGroupsFilename };
|
||||
auto groups_dict = tr_variant{};
|
||||
if (!tr_variantFromFile(&groups_dict, TR_VARIANT_PARSE_JSON, filename.sv(), nullptr) || !tr_variantIsList(&groups_dict))
|
||||
if (!tr_variantFromFile(&groups_dict, TR_VARIANT_PARSE_JSON, filename, nullptr) || !tr_variantIsList(&groups_dict))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -2887,7 +2893,7 @@ static void bandwidthGroupRead(tr_session* session, std::string_view config_dir)
|
|||
while (tr_variantDictChild(&groups_dict, idx++, &key, &dict))
|
||||
{
|
||||
auto name = tr_interned_string(key);
|
||||
auto& group = session->getBandwidthGroup(name.sv());
|
||||
auto& group = session->getBandwidthGroup(name);
|
||||
|
||||
auto limits = tr_bandwidth_limits{};
|
||||
tr_variantDictFindBool(dict, TR_KEY_uploadLimited, &limits.up_limited);
|
||||
|
@ -2935,7 +2941,7 @@ static int bandwidthGroupWrite(tr_session const* session, std::string_view confi
|
|||
}
|
||||
|
||||
auto const filename = tr_pathbuf{ config_dir, "/"sv, BandwidthGroupsFilename };
|
||||
auto const ret = tr_variantToFile(&groups_dict, TR_VARIANT_FMT_JSON, filename.sv());
|
||||
auto const ret = tr_variantToFile(&groups_dict, TR_VARIANT_FMT_JSON, filename);
|
||||
tr_variantFree(&groups_dict);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ char const* tr_ctorGetSourceFile(tr_ctor const* ctor)
|
|||
return ctor->torrent_filename.c_str();
|
||||
}
|
||||
|
||||
bool tr_ctorSaveContents(tr_ctor const* ctor, std::string const& filename, tr_error** error)
|
||||
bool tr_ctorSaveContents(tr_ctor const* ctor, std::string_view filename, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(ctor != nullptr);
|
||||
TR_ASSERT(!std::empty(filename));
|
||||
|
@ -127,20 +127,6 @@ bool tr_ctorSaveContents(tr_ctor const* ctor, std::string const& filename, tr_er
|
|||
return tr_saveFile(filename, ctor->contents, error);
|
||||
}
|
||||
|
||||
bool tr_ctorSaveMagnetContents(tr_torrent* tor, std::string const& filename, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(tor != nullptr);
|
||||
TR_ASSERT(!std::empty(filename));
|
||||
|
||||
auto const magnet = tor->magnet();
|
||||
if (std::empty(magnet))
|
||||
{
|
||||
tr_error_set(error, EINVAL, "torrent has no magnetlink to save"sv);
|
||||
return false;
|
||||
}
|
||||
return tr_saveFile(filename, magnet, error);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
|
@ -123,7 +123,7 @@ void* tr_torrentGetMetadataPiece(tr_torrent const* tor, int piece, size_t* len)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
auto const fd = tr_sys_file_open(tor->torrentFile().c_str(), TR_SYS_FILE_READ, 0);
|
||||
auto const fd = tr_sys_file_open(tor->torrentFile(), TR_SYS_FILE_READ, 0);
|
||||
if (fd == TR_BAD_SYS_FILE)
|
||||
{
|
||||
return nullptr;
|
||||
|
@ -273,13 +273,13 @@ static bool useNewMetainfo(tr_torrent* tor, tr_incomplete_metadata const* m, tr_
|
|||
}
|
||||
|
||||
// save it
|
||||
if (auto const filename = tor->torrentFile(); !tr_saveFile(filename, benc, error))
|
||||
if (!tr_saveFile(tor->torrentFile(), benc, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove .magnet file
|
||||
tr_sys_path_remove(tor->magnetFile().c_str());
|
||||
tr_sys_path_remove(tor->magnetFile());
|
||||
|
||||
// tor should keep this metainfo
|
||||
tor->setMetainfo(metainfo);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -501,7 +500,7 @@ tr_sha1_digest_t const& tr_torrent_metainfo::pieceHash(tr_piece_index_t piece) c
|
|||
return this->pieces_[piece];
|
||||
}
|
||||
|
||||
std::string tr_torrent_metainfo::makeFilename(
|
||||
tr_pathbuf tr_torrent_metainfo::makeFilename(
|
||||
std::string_view dirname,
|
||||
std::string_view name,
|
||||
std::string_view info_hash_string,
|
||||
|
@ -510,8 +509,8 @@ std::string tr_torrent_metainfo::makeFilename(
|
|||
{
|
||||
// `${dirname}/${name}.${info_hash}${suffix}`
|
||||
// `${dirname}/${info_hash}${suffix}`
|
||||
return format == BasenameFormat::Hash ? tr_strvJoin(dirname, "/"sv, info_hash_string, suffix) :
|
||||
tr_strvJoin(dirname, "/"sv, name, "."sv, info_hash_string.substr(0, 16), suffix);
|
||||
return format == BasenameFormat::Hash ? tr_pathbuf{ dirname, "/"sv, info_hash_string, suffix } :
|
||||
tr_pathbuf{ dirname, "/"sv, name, "."sv, info_hash_string.substr(0, 16), suffix };
|
||||
}
|
||||
|
||||
bool tr_torrent_metainfo::migrateFile(
|
||||
|
@ -556,11 +555,8 @@ void tr_torrent_metainfo::removeFile(
|
|||
std::string_view info_hash_string,
|
||||
std::string_view suffix)
|
||||
{
|
||||
auto filename = makeFilename(dirname, name, info_hash_string, BasenameFormat::NameAndPartialHash, suffix);
|
||||
tr_sys_path_remove(filename.c_str());
|
||||
|
||||
filename = makeFilename(dirname, name, info_hash_string, BasenameFormat::Hash, suffix);
|
||||
tr_sys_path_remove(filename.c_str());
|
||||
tr_sys_path_remove(makeFilename(dirname, name, info_hash_string, BasenameFormat::NameAndPartialHash, suffix));
|
||||
tr_sys_path_remove(makeFilename(dirname, name, info_hash_string, BasenameFormat::Hash, suffix));
|
||||
}
|
||||
|
||||
std::string const& tr_torrent_metainfo::fileSubpath(tr_file_index_t i) const
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "block-info.h"
|
||||
#include "magnet-metainfo.h"
|
||||
#include "tr-strbuf.h"
|
||||
|
||||
struct tr_error;
|
||||
|
||||
|
@ -134,17 +135,17 @@ public:
|
|||
return pieces_offset_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string torrentFile(std::string_view torrent_dir) const
|
||||
[[nodiscard]] auto torrentFile(std::string_view torrent_dir) const
|
||||
{
|
||||
return makeFilename(torrent_dir, name(), infoHashString(), BasenameFormat::Hash, ".torrent");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string magnetFile(std::string_view torrent_dir) const
|
||||
[[nodiscard]] auto magnetFile(std::string_view torrent_dir) const
|
||||
{
|
||||
return makeFilename(torrent_dir, name(), infoHashString(), BasenameFormat::Hash, ".magnet");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string resumeFile(std::string_view resume_dir) const
|
||||
[[nodiscard]] auto resumeFile(std::string_view resume_dir) const
|
||||
{
|
||||
return makeFilename(resume_dir, name(), infoHashString(), BasenameFormat::Hash, ".resume");
|
||||
}
|
||||
|
@ -175,14 +176,14 @@ private:
|
|||
NameAndPartialHash
|
||||
};
|
||||
|
||||
static std::string makeFilename(
|
||||
[[nodiscard]] static tr_pathbuf makeFilename(
|
||||
std::string_view dirname,
|
||||
std::string_view name,
|
||||
std::string_view info_hash_string,
|
||||
BasenameFormat format,
|
||||
std::string_view suffix);
|
||||
|
||||
[[nodiscard]] std::string makeFilename(std::string_view dirname, BasenameFormat format, std::string_view suffix) const
|
||||
auto makeFilename(std::string_view dirname, BasenameFormat format, std::string_view suffix) const
|
||||
{
|
||||
return makeFilename(dirname, name(), infoHashString(), format, suffix);
|
||||
}
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <algorithm> /* EINVAL */
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cerrno> /* EINVAL */
|
||||
#include <cerrno> // EINVAL
|
||||
#include <climits> /* INT_MAX */
|
||||
#include <cmath>
|
||||
#include <csignal> /* signal() */
|
||||
#include <cstring> /* memcmp */
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
@ -779,29 +778,34 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
|
|||
[](auto mtime) { return mtime > 0; });
|
||||
}
|
||||
|
||||
// if we don't have a local .torrent or .magnet file already, assume the torrent is new
|
||||
auto const filename = tor->hasMetadata() ? tor->torrentFile() : tor->magnetFile();
|
||||
|
||||
// if we don't have a local .torrent or .magnet file already,
|
||||
// assume the torrent is new
|
||||
bool const is_new_torrent = !tr_sys_path_exists(filename.c_str());
|
||||
|
||||
if (is_new_torrent)
|
||||
{
|
||||
tr_error* error = nullptr;
|
||||
if (tor->hasMetadata())
|
||||
|
||||
if (tor->hasMetadata()) // torrent file
|
||||
{
|
||||
if (!tr_ctorSaveContents(ctor, filename, &error))
|
||||
{
|
||||
tor->setLocalError(
|
||||
tr_strvJoin("Unable to save torrent file: ", error->message, " ("sv, std::to_string(error->code), ")"sv));
|
||||
}
|
||||
tr_ctorSaveContents(ctor, filename, &error);
|
||||
}
|
||||
else
|
||||
else // magnet link
|
||||
{
|
||||
// magnet link
|
||||
if (!tr_ctorSaveMagnetContents(tor, filename, &error))
|
||||
{
|
||||
tor->setLocalError(
|
||||
tr_strvJoin("Unable to save magnet file: ", error->message, " ("sv, std::to_string(error->code), ")"sv));
|
||||
}
|
||||
auto const magnet_link = tor->magnet();
|
||||
tr_saveFile(filename, magnet_link, &error);
|
||||
}
|
||||
|
||||
if (error != nullptr)
|
||||
{
|
||||
tor->setLocalError(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
tr_error_clear(&error);
|
||||
}
|
||||
|
||||
tr_error_clear(&error);
|
||||
|
@ -1782,7 +1786,7 @@ static void torrentCallScript(tr_torrent const* tor, char const* script)
|
|||
tr_localtime_r(&now, &tm);
|
||||
strftime(ctime_str, sizeof(ctime_str), "%a %b %d %T %Y%n", &tm); /* ctime equiv */
|
||||
|
||||
auto torrent_dir = std::string{ tor->currentDir().sv() };
|
||||
auto torrent_dir = std::string{ tor->currentDir() };
|
||||
tr_sys_path_native_separators(std::data(torrent_dir));
|
||||
|
||||
auto const cmd = std::array<char const*, 2>{ script, nullptr };
|
||||
|
@ -2085,7 +2089,7 @@ bool tr_torrent::setTrackerList(std::string_view text)
|
|||
}
|
||||
|
||||
auto const has_metadata = this->hasMetadata();
|
||||
if (has_metadata && !announce_list.save(this->torrentFile()))
|
||||
if (has_metadata && !announce_list.save(torrentFile()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -2096,12 +2100,14 @@ bool tr_torrent::setTrackerList(std::string_view text)
|
|||
// magnet links
|
||||
if (!has_metadata)
|
||||
{
|
||||
auto const magnet_file = magnetFile();
|
||||
auto const magnet_link = this->magnet();
|
||||
tr_error* save_error = nullptr;
|
||||
if (!tr_ctorSaveMagnetContents(this, this->magnetFile(), &save_error))
|
||||
if (!tr_saveFile(magnet_file, magnet_link, &save_error))
|
||||
{
|
||||
this->setLocalError(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", this->magnetFile()),
|
||||
fmt::arg("path", magnet_file),
|
||||
fmt::arg("error", save_error->message),
|
||||
fmt::arg("error_code", save_error->code)));
|
||||
tr_error_clear(&save_error);
|
||||
|
@ -2242,7 +2248,7 @@ static void deleteLocalData(tr_torrent const* tor, tr_fileFunc func)
|
|||
{
|
||||
auto files = std::vector<std::string>{};
|
||||
auto folders = std::set<std::string>{};
|
||||
auto const top = std::string{ tor->currentDir().sv() };
|
||||
auto const top = std::string{ tor->currentDir() };
|
||||
|
||||
/* don't try to delete local data if the directory's gone missing */
|
||||
if (!tr_sys_path_exists(top.c_str()))
|
||||
|
@ -2674,7 +2680,7 @@ std::optional<tr_torrent::tr_found_file_t> tr_torrent::findFile(std::string& fil
|
|||
|
||||
if (!std::empty(this->downloadDir()))
|
||||
{
|
||||
auto const base = this->downloadDir().sv();
|
||||
auto const base = this->downloadDir();
|
||||
|
||||
tr_buildBuf(filename, base, "/"sv, subpath);
|
||||
if (tr_sys_path_get_info(filename.c_str(), 0, &file_info))
|
||||
|
@ -2691,7 +2697,7 @@ std::optional<tr_torrent::tr_found_file_t> tr_torrent::findFile(std::string& fil
|
|||
|
||||
if (!std::empty(this->incompleteDir()))
|
||||
{
|
||||
auto const base = this->incompleteDir().sv();
|
||||
auto const base = this->incompleteDir();
|
||||
|
||||
tr_buildBuf(filename, base, "/"sv, subpath);
|
||||
if (tr_sys_path_get_info(filename.c_str(), 0, &file_info))
|
||||
|
@ -2960,7 +2966,7 @@ static int renamePath(tr_torrent* tor, char const* oldpath, char const* newname)
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
auto const base = tor->isDone() || std::empty(tor->incompleteDir()) ? tor->downloadDir().sv() : tor->incompleteDir().sv();
|
||||
auto const base = tor->isDone() || std::empty(tor->incompleteDir()) ? tor->downloadDir() : tor->incompleteDir();
|
||||
|
||||
auto src = tr_strvPath(base, oldpath);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "session.h"
|
||||
#include "torrent-metainfo.h"
|
||||
#include "tr-macros.h"
|
||||
#include "tr-strbuf.h"
|
||||
|
||||
class tr_swarm;
|
||||
struct tr_error;
|
||||
|
@ -52,9 +53,7 @@ void tr_ctorInitTorrentPriorities(tr_ctor const* ctor, tr_torrent* tor);
|
|||
|
||||
void tr_ctorInitTorrentWanted(tr_ctor const* ctor, tr_torrent* tor);
|
||||
|
||||
bool tr_ctorSaveContents(tr_ctor const* ctor, std::string const& filename, tr_error** error);
|
||||
|
||||
bool tr_ctorSaveMagnetContents(tr_torrent* tor, std::string const& filename, tr_error** error);
|
||||
bool tr_ctorSaveContents(tr_ctor const* ctor, std::string_view filename, tr_error** error);
|
||||
|
||||
std::string_view tr_ctorGetContents(tr_ctor const* ctor);
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <cstdlib> // getenv()
|
||||
#include <cstring> /* strerror() */
|
||||
#include <ctime> // nanosleep()
|
||||
#include <iterator> // std::back_inserter
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <event2/buffer.h>
|
||||
|
||||
#define PSL_STATIC
|
||||
#include <libpsl.h>
|
||||
|
||||
|
@ -171,44 +169,6 @@ char const* tr_webGetResponseStr(long code)
|
|||
}
|
||||
}
|
||||
|
||||
void tr_http_escape(struct evbuffer* out, std::string_view str, bool escape_reserved)
|
||||
{
|
||||
auto constexpr ReservedChars = std::string_view{ "!*'();:@&=+$,/?%#[]" };
|
||||
auto constexpr UnescapedChars = std::string_view{ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~" };
|
||||
|
||||
for (auto const& ch : str)
|
||||
{
|
||||
if (tr_strvContains(UnescapedChars, ch) || (tr_strvContains(ReservedChars, ch) && !escape_reserved))
|
||||
{
|
||||
evbuffer_add_printf(out, "%c", ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
evbuffer_add_printf(out, "%%%02X", (unsigned)(ch & 0xFF));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tr_http_escape(std::string& appendme, std::string_view str, bool escape_reserved)
|
||||
{
|
||||
auto constexpr ReservedChars = std::string_view{ "!*'();:@&=+$,/?%#[]" };
|
||||
auto constexpr UnescapedChars = std::string_view{ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~" };
|
||||
|
||||
for (auto const& ch : str)
|
||||
{
|
||||
if (tr_strvContains(UnescapedChars, ch) || (!escape_reserved && tr_strvContains(ReservedChars, ch)))
|
||||
{
|
||||
appendme += ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[16];
|
||||
tr_snprintf(buf, sizeof(buf), "%%%02X", (unsigned)(ch & 0xFF));
|
||||
appendme += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_rfc2396_alnum(uint8_t ch)
|
||||
{
|
||||
return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ch == '.' || ch == '-' ||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <utility>
|
||||
|
||||
#include "tr-macros.h" // tr_sha1_digest_t
|
||||
#include "tr-strbuf.h" // tr_urlbuf
|
||||
#include "utils.h"
|
||||
|
||||
struct evbuffer;
|
||||
|
||||
|
@ -91,10 +93,24 @@ struct tr_url_query_view
|
|||
}
|
||||
};
|
||||
|
||||
void tr_http_escape(std::string& appendme, std::string_view str, bool escape_reserved);
|
||||
template<typename OutputIt>
|
||||
void tr_http_escape(OutputIt out, std::string_view str, bool escape_reserved)
|
||||
{
|
||||
auto constexpr ReservedChars = std::string_view{ "!*'();:@&=+$,/?%#[]" };
|
||||
auto constexpr UnescapedChars = std::string_view{ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~" };
|
||||
|
||||
// TODO: remove evbuffer version
|
||||
void tr_http_escape(struct evbuffer* out, std::string_view str, bool escape_reserved);
|
||||
for (auto const& ch : str)
|
||||
{
|
||||
if (tr_strvContains(UnescapedChars, ch) || (tr_strvContains(ReservedChars, ch) && !escape_reserved))
|
||||
{
|
||||
out = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::format_to(out, "%{:02X}", unsigned(ch & 0xFF));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tr_http_escape_sha1(char* out, uint8_t const* sha1_digest);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -456,16 +457,17 @@ void onPartialDataFetched(tr_web::FetchResponse const& web_response)
|
|||
on_idle(webseed);
|
||||
}
|
||||
|
||||
std::string make_url(tr_webseed* w, std::string_view name)
|
||||
template<typename OutputIt>
|
||||
void makeUrl(tr_webseed* w, std::string_view name, OutputIt out)
|
||||
{
|
||||
auto url = w->base_url;
|
||||
auto const url = w->base_url;
|
||||
|
||||
out = std::copy(std::begin(url), std::end(url), out);
|
||||
|
||||
if (tr_strvEndsWith(url, "/"sv) && !std::empty(name))
|
||||
{
|
||||
tr_http_escape(url, name, false);
|
||||
tr_http_escape(out, name, false);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
void task_request_next_chunk(tr_webseed_task* task)
|
||||
|
@ -485,8 +487,9 @@ void task_request_next_chunk(tr_webseed_task* task)
|
|||
|
||||
webseed->connection_limiter.taskStarted();
|
||||
|
||||
auto const url = make_url(webseed, tor->fileSubpath(file_index));
|
||||
auto options = tr_web::FetchOptions{ url, onPartialDataFetched, task };
|
||||
auto url = tr_urlbuf{};
|
||||
makeUrl(webseed, tor->fileSubpath(file_index), std::back_inserter(url));
|
||||
auto options = tr_web::FetchOptions{ url.sv(), onPartialDataFetched, task };
|
||||
options.range = tr_strvJoin(std::to_string(file_offset), "-"sv, std::to_string(file_offset + this_chunk - 1));
|
||||
options.speed_limit_tag = tor->uniqueId;
|
||||
options.buffer = task->content();
|
||||
|
|
|
@ -42,7 +42,7 @@ TEST_F(TorrentMetainfoTest, magnetLink)
|
|||
EXPECT_TRUE(metainfo.parseMagnet(MagnetLink));
|
||||
EXPECT_EQ(0, metainfo.fileCount()); // because it's a magnet link
|
||||
EXPECT_EQ(2, std::size(metainfo.announceList()));
|
||||
EXPECT_EQ(MagnetLink, metainfo.magnet());
|
||||
EXPECT_EQ(MagnetLink, metainfo.magnet().sv());
|
||||
}
|
||||
|
||||
#define BEFORE_PATH \
|
||||
|
@ -166,7 +166,7 @@ TEST_F(TorrentMetainfoTest, ctorSaveContents)
|
|||
// try saving without passing any metainfo.
|
||||
auto* ctor = tr_ctorNew(session_);
|
||||
tr_error* error = nullptr;
|
||||
EXPECT_FALSE(tr_ctorSaveContents(ctor, tgt_filename.c_str(), &error));
|
||||
EXPECT_FALSE(tr_ctorSaveContents(ctor, tgt_filename.sv(), &error));
|
||||
EXPECT_NE(nullptr, error);
|
||||
if (error != nullptr)
|
||||
{
|
||||
|
@ -177,7 +177,7 @@ TEST_F(TorrentMetainfoTest, ctorSaveContents)
|
|||
// now try saving _with_ metainfo
|
||||
EXPECT_TRUE(tr_ctorSetMetainfoFromFile(ctor, src_filename.c_str(), &error));
|
||||
EXPECT_EQ(nullptr, error) << *error;
|
||||
EXPECT_TRUE(tr_ctorSaveContents(ctor, tgt_filename.c_str(), &error));
|
||||
EXPECT_TRUE(tr_ctorSaveContents(ctor, tgt_filename.sv(), &error));
|
||||
EXPECT_EQ(nullptr, error) << *error;
|
||||
|
||||
// the saved contents should match the source file's contents
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "torrent.h"
|
||||
|
@ -11,9 +14,6 @@
|
|||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
using TorrentsTest = ::testing::Test;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
|
Loading…
Reference in New Issue