diff --git a/libtransmission/announce-list.cc b/libtransmission/announce-list.cc index e32ba1388..bfb71a975 100644 --- a/libtransmission/announce-list.cc +++ b/libtransmission/announce-list.cc @@ -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{}; diff --git a/libtransmission/announce-list.h b/libtransmission/announce-list.h index ffb994e98..032686c5c 100644 --- a/libtransmission/announce-list.h +++ b/libtransmission/announce-list.h @@ -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 announceToScrape(std::string_view announce); static tr_quark announceToScrape(tr_quark announce); diff --git a/libtransmission/announcer-http.cc b/libtransmission/announcer-http.cc index 5d146255f..5307c2fd1 100644 --- a/libtransmission/announcer-http.cc +++ b/libtransmission/announcer-http.cc @@ -6,12 +6,12 @@ #include /* USHRT_MAX */ #include /* fprintf() */ #include /* strchr(), memcmp(), memcpy() */ -#include #include +#include +#include #include #include -#include #include /* for HTTP_OK */ #include @@ -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{}; 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{}; 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( diff --git a/libtransmission/announcer.cc b/libtransmission/announcer.cc index 5e9ee9f31..4a22495d2 100644 --- a/libtransmission/announcer.cc +++ b/libtransmission/announcer.cc @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -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, diff --git a/libtransmission/magnet-metainfo.cc b/libtransmission/magnet-metainfo.cc index c4ca9d1d4..4ac94607d 100644 --- a/libtransmission/magnet-metainfo.cc +++ b/libtransmission/magnet-metainfo.cc @@ -5,7 +5,7 @@ #include #include -#include // isxdigit() +#include // back_inserter #include #include @@ -150,29 +150,26 @@ std::optional 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; diff --git a/libtransmission/magnet-metainfo.h b/libtransmission/magnet-metainfo.h index e41fde7a5..91deeb279 100644 --- a/libtransmission/magnet-metainfo.h +++ b/libtransmission/magnet-metainfo.h @@ -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 { diff --git a/libtransmission/resume.cc b/libtransmission/resume.cc index d38428989..c5112ab0d 100644 --- a/libtransmission/resume.cc +++ b/libtransmission/resume.cc @@ -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))); } diff --git a/libtransmission/rpcimpl.cc b/libtransmission/rpcimpl.cc index 6802cfce4..b7cd0069b 100644 --- a/libtransmission/rpcimpl.cc +++ b/libtransmission/rpcimpl.cc @@ -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; } diff --git a/libtransmission/session.cc b/libtransmission/session.cc index 81d35c3ae..024fd99cb 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -124,8 +124,14 @@ tr_peer_id_t tr_peerIdInit() std::optional 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{ 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 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{}; tr_loadFile(path.sv(), buf)) + if (auto buf = std::vector{}; 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; } diff --git a/libtransmission/torrent-ctor.cc b/libtransmission/torrent-ctor.cc index e173e91e4..95d6bf9cf 100644 --- a/libtransmission/torrent-ctor.cc +++ b/libtransmission/torrent-ctor.cc @@ -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); -} - /*** **** ***/ diff --git a/libtransmission/torrent-magnet.cc b/libtransmission/torrent-magnet.cc index 991cd67ea..42beed88b 100644 --- a/libtransmission/torrent-magnet.cc +++ b/libtransmission/torrent-magnet.cc @@ -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); diff --git a/libtransmission/torrent-metainfo.cc b/libtransmission/torrent-metainfo.cc index 3c4327947..ca871ad78 100644 --- a/libtransmission/torrent-metainfo.cc +++ b/libtransmission/torrent-metainfo.cc @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -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 diff --git a/libtransmission/torrent-metainfo.h b/libtransmission/torrent-metainfo.h index 0490eb6d1..6863c4476 100644 --- a/libtransmission/torrent-metainfo.h +++ b/libtransmission/torrent-metainfo.h @@ -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); } diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc index ff0813324..c7197b643 100644 --- a/libtransmission/torrent.cc +++ b/libtransmission/torrent.cc @@ -3,13 +3,12 @@ // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. -#include /* EINVAL */ +#include #include -#include /* EINVAL */ +#include // EINVAL #include /* INT_MAX */ #include #include /* signal() */ -#include /* memcmp */ #include #include #include @@ -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{ 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{}; auto folders = std::set{}; - 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::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::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); diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index 7277d88cb..25ea6692c 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -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); diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index 44a2620d1..d179d03b1 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -14,7 +14,6 @@ #include // getenv() #include /* strerror() */ #include // nanosleep() -#include // std::back_inserter #include #include #include diff --git a/libtransmission/web-utils.cc b/libtransmission/web-utils.cc index 45db112b4..693a9e41c 100644 --- a/libtransmission/web-utils.cc +++ b/libtransmission/web-utils.cc @@ -13,8 +13,6 @@ #include #include -#include - #define PSL_STATIC #include @@ -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 == '-' || diff --git a/libtransmission/web-utils.h b/libtransmission/web-utils.h index 47a30bb3e..60fda85ec 100644 --- a/libtransmission/web-utils.h +++ b/libtransmission/web-utils.h @@ -11,6 +11,8 @@ #include #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 +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); diff --git a/libtransmission/webseed.cc b/libtransmission/webseed.cc index 826a1266e..3492138ed 100644 --- a/libtransmission/webseed.cc +++ b/libtransmission/webseed.cc @@ -4,6 +4,7 @@ // License text can be found in the licenses/ folder. #include +#include #include #include #include @@ -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 +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(); diff --git a/tests/libtransmission/torrent-metainfo-test.cc b/tests/libtransmission/torrent-metainfo-test.cc index faea4a3c1..f78f218d6 100644 --- a/tests/libtransmission/torrent-metainfo-test.cc +++ b/tests/libtransmission/torrent-metainfo-test.cc @@ -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 diff --git a/tests/libtransmission/torrents-test.cc b/tests/libtransmission/torrents-test.cc index d00baed5a..e929ae6bf 100644 --- a/tests/libtransmission/torrents-test.cc +++ b/tests/libtransmission/torrents-test.cc @@ -3,6 +3,9 @@ // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. +#include +#include + #include "transmission.h" #include "torrent.h" @@ -11,9 +14,6 @@ #include "gtest/gtest.h" -#include -#include - using namespace std::literals; using TorrentsTest = ::testing::Test; diff --git a/utils/show.cc b/utils/show.cc index c1c54a84a..082cf2868 100644 --- a/utils/show.cc +++ b/utils/show.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include