diff --git a/cli/cli.cc b/cli/cli.cc index 5dcc59bb4..990fa1d65 100644 --- a/cli/cli.cc +++ b/cli/cli.cc @@ -142,7 +142,7 @@ static void onTorrentFileDownloaded( void* vctor) { auto* ctor = static_cast(vctor); - tr_ctorSetMetainfo(ctor, std::data(response), std::size(response)); + tr_ctorSetMetainfo(ctor, std::data(response), std::size(response), nullptr); waitingOnWeb = false; } @@ -301,11 +301,11 @@ int tr_main(int argc, char* argv[]) if (tr_sys_path_exists(torrentPath, nullptr)) { - tr_ctorSetMetainfoFromFile(ctor, torrentPath); + tr_ctorSetMetainfoFromFile(ctor, torrentPath, nullptr); } else if (memcmp(torrentPath, "magnet:?", 8) == 0) { - tr_ctorSetMetainfoFromMagnetLink(ctor, torrentPath); + tr_ctorSetMetainfoFromMagnetLink(ctor, torrentPath, nullptr); } else if (memcmp(torrentPath, "http", 4) == 0) { diff --git a/daemon/daemon.cc b/daemon/daemon.cc index 0e10f18e1..9c67b28a7 100644 --- a/daemon/daemon.cc +++ b/daemon/daemon.cc @@ -218,7 +218,7 @@ static char const* getConfigDir(int argc, char const* const* argv) return configDir; } -static tr_watchdir_status onFileAdded(tr_watchdir_t dir, char const* name, void* vsession) +static auto onFileAdded(tr_watchdir_t dir, char const* name, void* vsession) { auto const* session = static_cast(vsession); @@ -229,48 +229,44 @@ static tr_watchdir_status onFileAdded(tr_watchdir_t dir, char const* name, void* auto filename = tr_strvPath(tr_watchdir_get_path(dir), name); tr_ctor* ctor = tr_ctorNew(session); - int err = tr_ctorSetMetainfoFromFile(ctor, filename.c_str()); - - if (err == 0) + if (!tr_ctorSetMetainfoFromFile(ctor, filename.c_str(), nullptr)) { - if (tr_torrentNew(ctor, nullptr) == nullptr) - { - tr_logAddError("Unable to add .torrent file \"%s\"", name); - } - else - { - bool trash = false; - bool const test = tr_ctorGetDeleteSource(ctor, &trash); + tr_ctorFree(ctor); + return TR_WATCHDIR_RETRY; + } - tr_logAddInfo("Parsing .torrent file successful \"%s\"", name); - - if (test && trash) - { - tr_error* error = nullptr; - - tr_logAddInfo("Deleting input .torrent file \"%s\"", name); - - if (!tr_sys_path_remove(filename.c_str(), &error)) - { - tr_logAddError("Error deleting .torrent file: %s", error->message); - tr_error_free(error); - } - } - else - { - auto const new_filename = filename + ".added"; - tr_sys_path_rename(filename.c_str(), new_filename.c_str(), nullptr); - } - } + if (tr_torrentNew(ctor, nullptr) == nullptr) + { + tr_logAddError("Unable to add .torrent file \"%s\"", name); } else { - err = TR_PARSE_ERR; + bool trash = false; + bool const test = tr_ctorGetDeleteSource(ctor, &trash); + + tr_logAddInfo("Parsing .torrent file successful \"%s\"", name); + + if (test && trash) + { + tr_error* error = nullptr; + + tr_logAddInfo("Deleting input .torrent file \"%s\"", name); + + if (!tr_sys_path_remove(filename.c_str(), &error)) + { + tr_logAddError("Error deleting .torrent file: %s", error->message); + tr_error_free(error); + } + } + else + { + auto const new_filename = filename + ".added"; + tr_sys_path_rename(filename.c_str(), new_filename.c_str(), nullptr); + } } tr_ctorFree(ctor); - - return err == TR_PARSE_ERR ? TR_WATCHDIR_RETRY : TR_WATCHDIR_ACCEPT; + return TR_WATCHDIR_ACCEPT; } static void printMessage( diff --git a/gtk/MakeDialog.cc b/gtk/MakeDialog.cc index c7df0fff9..bcd6c55f3 100644 --- a/gtk/MakeDialog.cc +++ b/gtk/MakeDialog.cc @@ -168,7 +168,7 @@ MakeProgressDialog::~MakeProgressDialog() void MakeProgressDialog::addTorrent() { tr_ctor* ctor = tr_ctorNew(core_->get_session()); - tr_ctorSetMetainfoFromFile(ctor, target_.c_str()); + tr_ctorSetMetainfoFromFile(ctor, target_.c_str(), nullptr); tr_ctorSetDownloadDir(ctor, TR_FORCE, Glib::path_get_dirname(builder_.top).c_str()); core_->add_ctor(ctor); } diff --git a/gtk/OptionsDialog.cc b/gtk/OptionsDialog.cc index d75abec2e..c8d747c4c 100644 --- a/gtk/OptionsDialog.cc +++ b/gtk/OptionsDialog.cc @@ -193,7 +193,7 @@ void OptionsDialog::Impl::sourceChanged(Gtk::FileChooserButton* b) if (!filename.empty() && (filename_.empty() || !tr_sys_path_is_same(filename.c_str(), filename_.c_str(), nullptr))) { filename_ = filename; - tr_ctorSetMetainfoFromFile(ctor_.get(), filename_.c_str()); + tr_ctorSetMetainfoFromFile(ctor_.get(), filename_.c_str(), nullptr); new_file = true; } diff --git a/gtk/Session.cc b/gtk/Session.cc index 8f2bbdebf..cc21f2525 100644 --- a/gtk/Session.cc +++ b/gtk/Session.cc @@ -31,8 +31,10 @@ #include #include + #include #include +#include #include #include /* tr_free */ #include @@ -1040,44 +1042,36 @@ tr_torrent* Session::Impl::create_new_torrent(tr_ctor* ctor) int Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify) { - tr_info inf; - auto err = tr_torrentParse(ctor, &inf); - - switch (err) + auto const* metainfo = tr_ctorGetMetainfo(ctor); + if (metainfo == nullptr) { - case TR_PARSE_ERR: - break; + return TR_PARSE_ERR; + } - case TR_PARSE_DUPLICATE: + if (tr_torrentFindFromMetainfo(get_session(), metainfo) != nullptr) + { /* don't complain about .torrent files in the watch directory * that have already been added... that gets annoying and we * don't want to be nagging users to clean up their watch dirs */ if (tr_ctorGetSourceFile(ctor) == nullptr || !adding_from_watch_dir_) { - signal_add_error.emit(static_cast(err), inf.name); + signal_add_error.emit(static_cast(ERR_ADD_TORRENT_DUP), metainfo->name().c_str()); } - tr_metainfoFree(&inf); tr_ctorFree(ctor); - break; - - default: - if (do_prompt) - { - signal_add_prompt.emit(ctor); - } - else - { - ScopedModelSortBlocker disable_sort(*gtr_get_ptr(sorted_model_)); - add_torrent(create_new_torrent(ctor), do_notify); - tr_ctorFree(ctor); - } - - tr_metainfoFree(&inf); - break; + return TR_PARSE_DUPLICATE; } - return err; + if (!do_prompt) + { + ScopedModelSortBlocker disable_sort(*gtr_get_ptr(sorted_model_)); + add_torrent(create_new_torrent(ctor), do_notify); + tr_ctorFree(ctor); + return 0; + } + + signal_add_prompt.emit(ctor); + return 0; } namespace @@ -1136,7 +1130,7 @@ void Session::Impl::add_file_async_callback( { g_message(_("Couldn't read \"%s\""), file->get_parse_name().c_str()); } - else if (tr_ctorSetMetainfo(ctor, contents, length) == 0) + else if (tr_ctorSetMetainfo(ctor, contents, length, nullptr)) { add_ctor(ctor, do_prompt, do_notify); } @@ -1175,7 +1169,7 @@ bool Session::Impl::add_file(Glib::RefPtr const& file, bool do_start, if ((tried = !str.empty() && Glib::file_test(str, Glib::FILE_TEST_EXISTS))) { - loaded = !tr_ctorSetMetainfoFromFile(ctor, str.c_str()); + loaded = tr_ctorSetMetainfoFromFile(ctor, str.c_str(), nullptr); } } @@ -1186,7 +1180,7 @@ bool Session::Impl::add_file(Glib::RefPtr const& file, bool do_start, auto const str = file->get_parse_name(); auto const magnet = gtr_sprintf("magnet:%s", str.substr(str.find('?'))); tried = true; - loaded = !tr_ctorSetMetainfoFromMagnetLink(ctor, magnet.c_str()); + loaded = tr_ctorSetMetainfoFromMagnetLink(ctor, magnet.c_str(), nullptr); } /* hashcodes that we can turn into magnet links... */ @@ -1197,7 +1191,7 @@ bool Session::Impl::add_file(Glib::RefPtr const& file, bool do_start, if (gtr_is_hex_hashcode(str)) { auto const magnet = gtr_sprintf("magnet:?xt=urn:btih:%s", str); - loaded = !tr_ctorSetMetainfoFromMagnetLink(ctor, magnet.c_str()); + loaded = tr_ctorSetMetainfoFromMagnetLink(ctor, magnet.c_str(), nullptr); } } diff --git a/libtransmission/metainfo.h b/libtransmission/metainfo.h index 2654ebc4a..25a4adf97 100644 --- a/libtransmission/metainfo.h +++ b/libtransmission/metainfo.h @@ -22,6 +22,7 @@ #include "transmission.h" #include "tr-macros.h" +#include "torrent.h" struct tr_error; struct tr_variant; diff --git a/libtransmission/rpcimpl.cc b/libtransmission/rpcimpl.cc index d25bf2cbf..99adacd6c 100644 --- a/libtransmission/rpcimpl.cc +++ b/libtransmission/rpcimpl.cc @@ -1569,7 +1569,7 @@ static void gotMetadataFromURL( if (response_code == 200 || response_code == 221) /* http or ftp success.. */ { - tr_ctorSetMetainfo(data->ctor, std::data(response), std::size(response)); + tr_ctorSetMetainfo(data->ctor, std::data(response), std::size(response), nullptr); addTorrentImpl(data->data, data->ctor); } else @@ -1709,20 +1709,20 @@ static char const* torrentAdd(tr_session* session, tr_variant* args_in, tr_varia if (std::empty(filename)) { std::string const metainfo = tr_base64_decode_str(metainfo_base64); - tr_ctorSetMetainfo(ctor, std::data(metainfo), std::size(metainfo)); + tr_ctorSetMetainfo(ctor, std::data(metainfo), std::size(metainfo), nullptr); } else { // these two tr_ctorSet*() functions require zero-terminated strings - auto const filename_str = std::string{ filename }; + auto const filename_sz = std::string{ filename }; if (tr_strvStartsWith(filename, "magnet:?"sv)) { - tr_ctorSetMetainfoFromMagnetLink(ctor, filename_str.c_str()); + tr_ctorSetMetainfoFromMagnetLink(ctor, filename_sz.c_str(), nullptr); } else { - tr_ctorSetMetainfoFromFile(ctor, filename_str.c_str()); + tr_ctorSetMetainfoFromFile(ctor, filename_sz.c_str(), nullptr); } } diff --git a/libtransmission/session.cc b/libtransmission/session.cc index 511d973a1..a0edc4c16 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -2042,7 +2042,7 @@ static void sessionLoadTorrents(void* vdata) if (tr_str_has_suffix(name, ".torrent")) { tr_buildBuf(path, dirname_sv, "/", name); - tr_ctorSetMetainfoFromFile(data->ctor, path.c_str()); + tr_ctorSetMetainfoFromFile(data->ctor, path.c_str(), nullptr); if (tr_torrent* const tor = tr_torrentNew(data->ctor, nullptr); tor != nullptr) { diff --git a/libtransmission/torrent-ctor.cc b/libtransmission/torrent-ctor.cc index 131254c3b..66c490453 100644 --- a/libtransmission/torrent-ctor.cc +++ b/libtransmission/torrent-ctor.cc @@ -6,21 +6,21 @@ * */ -#include /* EINVAL */ +#include // EINVAL #include #include -#include #include #include "transmission.h" #include "error.h" -#include "file.h" +#include "error-types.h" #include "magnet-metainfo.h" #include "session.h" -#include "torrent.h" /* tr_ctorGetSave() */ +#include "torrent-metainfo.h" +#include "torrent.h" #include "tr-assert.h" -#include "utils.h" /* tr_new0 */ +#include "utils.h" #include "variant.h" using namespace std::literals; @@ -40,14 +40,14 @@ struct tr_ctor bool saveInOurTorrentsDir = false; std::optional delete_source; + tr_torrent_metainfo metainfo = {}; + tr_priority_t priority = TR_PRI_NORMAL; - bool isSet_metainfo = false; - tr_variant metainfo = {}; - std::string source_file; struct optional_args optional_args[2]; std::string incomplete_dir; + std::string torrent_filename; std::vector wanted; std::vector unwanted; @@ -67,103 +67,46 @@ struct tr_ctor **** ***/ -static void setSourceFile(tr_ctor* ctor, char const* source_file) +bool tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename, tr_error** error) { - ctor->source_file.assign(source_file ? source_file : ""); -} - -static void clearMetainfo(tr_ctor* ctor) -{ - if (ctor->isSet_metainfo) + if (filename == nullptr) { - ctor->isSet_metainfo = false; - tr_variantFree(&ctor->metainfo); + tr_error_set_literal(error, EINVAL, "no filename specified"); + return false; } - setSourceFile(ctor, nullptr); + if (!tr_loadFile(ctor->contents, filename, error)) + { + return false; + } + + ctor->metainfo.clear(); + auto const contents_sv = std::string_view{ std::data(ctor->contents), std::size(ctor->contents) }; + return ctor->metainfo.parseBenc(contents_sv, error); } -static int parseMetainfoContents(tr_ctor* ctor) +bool tr_ctorSetMetainfo(tr_ctor* ctor, char const* metainfo, size_t len, tr_error** error) { - auto& contents = ctor->contents; - auto sv = std::string_view{ std::data(contents), std::size(contents) }; - ctor->isSet_metainfo = tr_variantFromBuf(&ctor->metainfo, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, sv); - return ctor->isSet_metainfo ? 0 : EILSEQ; -} - -int tr_ctorSetMetainfo(tr_ctor* ctor, char const* metainfo, size_t len) -{ - clearMetainfo(ctor); - + ctor->metainfo.clear(); ctor->contents.assign(metainfo, metainfo + len); + auto const contents_sv = std::string_view{ std::data(ctor->contents), std::size(ctor->contents) }; + return ctor->metainfo.parseBenc(contents_sv, error); +} - return parseMetainfoContents(ctor); +bool tr_ctorSetMetainfoFromMagnetLink(tr_ctor* ctor, char const* magnet_link, tr_error** error) +{ + ctor->metainfo.clear(); + return ctor->metainfo.parseMagnet(magnet_link ? magnet_link : "", error); +} + +std::string_view tr_ctorGetContents(tr_ctor const* ctor) +{ + return std::string_view{ std::data(ctor->contents), std::size(ctor->contents) }; } char const* tr_ctorGetSourceFile(tr_ctor const* ctor) { - return ctor->source_file.c_str(); -} - -int tr_ctorSetMetainfoFromMagnetLink(tr_ctor* ctor, char const* magnet_link) -{ - auto mm = tr_magnet_metainfo{}; - if (!mm.parseMagnet(magnet_link ? magnet_link : "")) - { - return -1; - } - - auto tmp = tr_variant{}; - mm.toVariant(&tmp); - auto len = size_t{}; - char* const str = tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC, &len); - auto const err = tr_ctorSetMetainfo(ctor, str, len); - tr_free(str); - tr_variantFree(&tmp); - - return err; -} - -int tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename) -{ - clearMetainfo(ctor); - - if (!tr_loadFile(ctor->contents, filename, nullptr) || std::empty(ctor->contents)) - { - return EILSEQ; - } - - if (int const err = parseMetainfoContents(ctor); err != 0) - { - clearMetainfo(ctor); - return err; - } - - setSourceFile(ctor, filename); - - /* if no `name' field was set, then set it from the filename */ - if (tr_variant* info = nullptr; tr_variantDictFindDict(&ctor->metainfo, TR_KEY_info, &info)) - { - auto name = std::string_view{}; - - if (!tr_variantDictFindStrView(info, TR_KEY_name_utf_8, &name) && !tr_variantDictFindStrView(info, TR_KEY_name, &name)) - { - name = ""sv; - } - - if (std::empty(name)) - { - char* base = tr_sys_path_basename(filename, nullptr); - - if (base != nullptr) - { - tr_variantDictAddStr(info, TR_KEY_name, base); - tr_free(base); - } - } - } - - return 0; + return ctor->metainfo.parsedTorrentFile().c_str(); } bool tr_ctorSaveContents(tr_ctor const* ctor, std::string_view filename, tr_error** error) @@ -353,19 +296,9 @@ bool tr_ctorGetIncompleteDir(tr_ctor const* ctor, char const** setme) return true; } -bool tr_ctorGetMetainfo(tr_ctor const* ctor, tr_variant const** setme) +tr_torrent_metainfo const* tr_ctorGetMetainfo(tr_ctor const* ctor) { - if (!ctor->isSet_metainfo) - { - return false; - } - - if (setme != nullptr) - { - *setme = &ctor->metainfo; - } - - return true; + return std::empty(ctor->metainfo.files()) ? nullptr : &ctor->metainfo; } tr_session* tr_ctorGetSession(tr_ctor const* ctor) @@ -403,13 +336,10 @@ tr_ctor* tr_ctorNew(tr_session const* session) { auto* const ctor = new tr_ctor{ session }; - if (session != nullptr) - { - tr_ctorSetDeleteSource(ctor, tr_sessionGetDeleteSource(session)); - tr_ctorSetPaused(ctor, TR_FALLBACK, tr_sessionGetPaused(session)); - tr_ctorSetPeerLimit(ctor, TR_FALLBACK, session->peerLimitPerTorrent); - tr_ctorSetDownloadDir(ctor, TR_FALLBACK, tr_sessionGetDownloadDir(session)); - } + tr_ctorSetDeleteSource(ctor, tr_sessionGetDeleteSource(session)); + tr_ctorSetPaused(ctor, TR_FALLBACK, tr_sessionGetPaused(session)); + tr_ctorSetPeerLimit(ctor, TR_FALLBACK, session->peerLimitPerTorrent); + tr_ctorSetDownloadDir(ctor, TR_FALLBACK, tr_sessionGetDownloadDir(session)); tr_ctorSetSave(ctor, true); return ctor; @@ -417,6 +347,5 @@ tr_ctor* tr_ctorNew(tr_session const* session) void tr_ctorFree(tr_ctor* ctor) { - clearMetainfo(ctor); delete ctor; } diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc index 062e3a6fb..b0e00844b 100644 --- a/libtransmission/torrent.cc +++ b/libtransmission/torrent.cc @@ -776,43 +776,26 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor) } } -tr_parse_result tr_torrentParse(tr_ctor const* ctor, tr_info* setmeInfo) -{ - tr_variant const* metainfo = nullptr; - if (!tr_ctorGetMetainfo(ctor, &metainfo)) - { - return TR_PARSE_ERR; - } - - auto parsed = tr_metainfoParse(tr_ctorGetSession(ctor), metainfo, nullptr); - if (!parsed) - { - return TR_PARSE_ERR; - } - - if (setmeInfo != nullptr) - { - *setmeInfo = parsed->info; - parsed->info = {}; - } - - return TR_PARSE_OK; -} - tr_torrent* tr_torrentNew(tr_ctor const* ctor, tr_torrent** setme_duplicate_of) { TR_ASSERT(ctor != nullptr); auto* const session = tr_ctorGetSession(ctor); TR_ASSERT(tr_isSession(session)); - tr_variant const* metainfo = nullptr; - tr_ctorGetMetainfo(ctor, &metainfo); - auto parsed = tr_metainfoParse(session, metainfo, nullptr); + // is the metainfo valid + auto top = tr_variant{}; + if (!tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC, tr_ctorGetContents(ctor), nullptr, nullptr)) + { + return nullptr; + } + auto parsed = tr_metainfoParse(session, &top, nullptr); + tr_variantFree(&top); if (!parsed) { return nullptr; } + // is it a duplicate auto* const duplicate_of = session->getTorrent(parsed->info.hash); if (duplicate_of != nullptr) { @@ -824,7 +807,8 @@ tr_torrent* tr_torrentNew(tr_ctor const* ctor, tr_torrent** setme_duplicate_of) return nullptr; } - auto* tor = new tr_torrent{ parsed->info }; + // add it + auto* const tor = new tr_torrent{ parsed->info }; tor->swapMetainfo(*parsed); torrentInit(tor, ctor); return tor; diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index 65f70e0e9..7aa0f845c 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -59,7 +59,7 @@ void tr_ctorInitTorrentWanted(tr_ctor const* ctor, tr_torrent* tor); bool tr_ctorSaveContents(tr_ctor const* ctor, std::string_view filename, tr_error** error); -bool tr_ctorGetMetainfo(tr_ctor const* ctor, tr_variant const** setme); +std::string_view tr_ctorGetContents(tr_ctor const* ctor); tr_session* tr_ctorGetSession(tr_ctor const* ctor); @@ -769,3 +769,6 @@ char* tr_torrentBuildPartial(tr_torrent const*, tr_file_index_t fileNo); void tr_torrentGotNewInfoDict(tr_torrent* tor); tr_peer_id_t const& tr_torrentGetPeerId(tr_torrent* tor); + +/** @brief free a metainfo */ +void tr_metainfoFree(tr_info* inf); diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index df610f42e..3c685ac94 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -816,30 +816,15 @@ char const* tr_blocklistGetURL(tr_session const*); /** @} */ -/** @addtogroup tr_ctor Torrent Constructors - @{ - - Instantiating a tr_torrent had gotten more complicated as features were - added. At one point there were four functions to check metainfo and five - to create a tr_torrent object. - - To remedy this, a Torrent Constructor (struct tr_ctor) has been introduced: - - Simplifies the API to two functions: tr_torrentParse() and tr_torrentNew() - - You can set the fields you want; the system sets defaults for the rest. - - You can specify whether or not your fields should supercede resume's. - - We can add new features to tr_ctor without breaking tr_torrentNew()'s API. - - All the tr_ctor{Get,Set}* () functions with a return value return - an error number, or zero if no error occurred. - - You must call one of the SetMetainfo() functions before creating - a torrent with a tr_ctor. The other functions are optional. - - You can reuse a single tr_ctor to create a batch of torrents -- - just call one of the SetMetainfo() functions between each - tr_torrentNew() call. - - Every call to tr_ctorSetMetainfo* () frees the previous metainfo. +/** + * Instantiating tr_torrents and wrangling .torrent file metadata + * + * 1. Torrent metadata is handled in the tr_torrent_metadata class. + * + * 2. Torrents should be instantiated using a torrent builder (tr_ctor). + * Calling one of the tr_ctorSetMetainfo*() functions is required. + * Other settings, e.g. torrent priority, are optional. + * When ready, pass the builder object to tr_torrentNew(). */ enum tr_ctorMode @@ -849,11 +834,8 @@ enum tr_ctorMode }; /** @brief Create a torrent constructor object used to instantiate a tr_torrent - @param session_or_nullptr the tr_session. - This is required if you're going to call tr_torrentNew(), - but you can use nullptr for tr_torrentParse(). - @see tr_torrentNew(), tr_torrentParse() */ -tr_ctor* tr_ctorNew(tr_session const* session_or_nullptr); + @param session the tr_session. */ +tr_ctor* tr_ctorNew(tr_session const* session); /** @brief Free a torrent constructor object */ void tr_ctorFree(tr_ctor* ctor); @@ -863,13 +845,15 @@ void tr_ctorFree(tr_ctor* ctor); void tr_ctorSetDeleteSource(tr_ctor* ctor, bool doDelete); /** @brief Set the constructor's metainfo from a magnet link */ -int tr_ctorSetMetainfoFromMagnetLink(tr_ctor* ctor, char const* magnet); +bool tr_ctorSetMetainfoFromMagnetLink(tr_ctor* ctor, char const* magnet, tr_error** error); /** @brief Set the constructor's metainfo from a raw benc already in memory */ -int tr_ctorSetMetainfo(tr_ctor* ctor, char const* metainfo, size_t len); +bool tr_ctorSetMetainfo(tr_ctor* ctor, char const* metainfo, size_t len, tr_error** error); /** @brief Set the constructor's metainfo from a local .torrent file */ -int tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename); +bool tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename, tr_error** error); + +tr_torrent_metainfo const* tr_ctorGetMetainfo(tr_ctor const* ctor); /** @brief Set how many peers this torrent can connect to. (Default: 50) */ void tr_ctorSetPeerLimit(tr_ctor* ctor, tr_ctorMode mode, uint16_t limit); @@ -915,6 +899,7 @@ bool tr_ctorGetDeleteSource(tr_ctor const* ctor, bool* setmeDoDelete); or nullptr if tr_ctorSetMetainfoFromFile() wasn't used */ char const* tr_ctorGetSourceFile(tr_ctor const* ctor); +// TODO(ckerr) remove enum tr_parse_result { TR_PARSE_OK, @@ -922,32 +907,6 @@ enum tr_parse_result TR_PARSE_DUPLICATE }; -/** - * @brief Parses the specified metainfo - * - * @return TR_PARSE_ERR if parsing failed; - * TR_PARSE_OK if parsing succeeded and it's not a duplicate; - * TR_PARSE_DUPLICATE if parsing succeeded but it's a duplicate. - * - * @param setme_info_or_nullptr If parsing is successful and setme_info is non-nullptr, - * the parsed metainfo is stored there and sould be freed - * by calling tr_metainfoFree() when no longer needed. - * - * Notes: - * - * 1. tr_torrentParse() won't be able to check for duplicates -- and therefore - * won't return TR_PARSE_DUPLICATE -- unless ctor's "download-dir" and - * session variable is set. - * - * 2. setme_info->torrent's value can't be set unless ctor's session variable - * is set. - */ -tr_parse_result tr_torrentParse(tr_ctor const* ctor, tr_info* setme_info_or_nullptr); - -/** @brief free a metainfo - @see tr_torrentParse */ -void tr_metainfoFree(tr_info* inf); - /** * Instantiate a single torrent. * diff --git a/macosx/Torrent.mm b/macosx/Torrent.mm index a8e8f70e4..984970c97 100644 --- a/macosx/Torrent.mm +++ b/macosx/Torrent.mm @@ -1849,18 +1849,19 @@ bool trashDataFile(char const* filename, tr_error** error) tr_ctorSetIncompleteDir(ctor, incompleteFolder.UTF8String); } - tr_parse_result result = TR_PARSE_ERR; + bool loaded = false; + if (path) { - result = static_cast(tr_ctorSetMetainfoFromFile(ctor, path.UTF8String)); + loaded = tr_ctorSetMetainfoFromFile(ctor, path.UTF8String, nullptr); } - if (result != TR_PARSE_OK && magnetAddress) + if (!loaded && magnetAddress) { - result = static_cast(tr_ctorSetMetainfoFromMagnetLink(ctor, magnetAddress.UTF8String)); + loaded = tr_ctorSetMetainfoFromMagnetLink(ctor, magnetAddress.UTF8String, nullptr); } - if (result == TR_PARSE_OK) + if (loaded) { fHandle = tr_torrentNew(ctor, NULL); } diff --git a/tests/libtransmission/metainfo-test.cc b/tests/libtransmission/metainfo-test.cc index 3b865bc88..84b03c73c 100644 --- a/tests/libtransmission/metainfo-test.cc +++ b/tests/libtransmission/metainfo-test.cc @@ -13,113 +13,29 @@ #include "torrent.h" #include "utils.h" -#include "gtest/gtest.h" +#include "test-fixtures.h" #include #include #include #include -using namespace std::literals; - -TEST(Metainfo, magnetLink) -{ - // background info @ http://wiki.theory.org/BitTorrent_Magnet-URI_Webseeding - char const constexpr* const MagnetLink = - "magnet:?" - "xt=urn:btih:14ffe5dd23188fd5cb53a1d47f1289db70abf31e" - "&dn=ubuntu_12_04_1_desktop_32_bit" - "&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce" - "&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80" - "&ws=http%3A%2F%2Ftransmissionbt.com"; - - auto* ctor = tr_ctorNew(nullptr); - tr_ctorSetMetainfoFromMagnetLink(ctor, MagnetLink); - auto inf = tr_info{}; - 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 - auto const n = std::size(*inf.announce_list); - EXPECT_EQ(2, n); - if (n >= 1) - { - EXPECT_EQ("http://tracker.publicbt.com/announce"sv, inf.announce_list->at(0).announce.full); - } - if (n >= 2) - { - EXPECT_EQ("udp://tracker.publicbt.com:80"sv, inf.announce_list->at(1).announce.full); - } - EXPECT_EQ(1, inf.webseedCount); - if (inf.webseedCount >= 1) - { - EXPECT_STREQ("http://transmissionbt.com", inf.webseeds[0]); - } - - auto* const link = tr_torrentInfoGetMagnetLink(&inf); - EXPECT_STREQ(MagnetLink, link); - tr_free(link); - - /* cleanup */ - tr_metainfoFree(&inf); - tr_ctorFree(ctor); -} - #define BEFORE_PATH \ "d10:created by25:Transmission/2.82 (14160)13:creation datei1402280218e8:encoding5:UTF-84:infod5:filesld6:lengthi2e4:pathl" #define AFTER_PATH \ "eed6:lengthi2e4:pathl5:b.txteee4:name3:foo12:piece lengthi32768e6:pieces20:ÞÉ`âM‘‹Šs¡Å;˺¬.åÂà7:privatei0eee" -// FIXME: split these into parameterized tests? -TEST(Metainfo, bucket) +using namespace std::literals; + +namespace libtransmission { - struct LocalTest - { - int expected_benc_err; - int expected_parse_result; - std::string_view benc; - }; - auto const tests = std::array{ - LocalTest{ 0, TR_PARSE_OK, BEFORE_PATH "5:a.txt" AFTER_PATH }, +namespace test +{ - /* allow empty components, but not =all= empty components, see bug #5517 */ - { 0, TR_PARSE_OK, BEFORE_PATH "0:5:a.txt" AFTER_PATH }, - { 0, TR_PARSE_ERR, BEFORE_PATH "0:0:" AFTER_PATH }, +using MetainfoTest = SessionTest; - /* allow path separators in a filename (replaced with '_') */ - { 0, TR_PARSE_OK, BEFORE_PATH "7:a/a.txt" AFTER_PATH }, - - /* allow "." components (skipped) */ - { 0, TR_PARSE_OK, BEFORE_PATH "1:.5:a.txt" AFTER_PATH }, - { 0, TR_PARSE_OK, BEFORE_PATH "5:a.txt1:." AFTER_PATH }, - - /* allow ".." components (replaced with "__") */ - { 0, TR_PARSE_OK, BEFORE_PATH "2:..5:a.txt" AFTER_PATH }, - { 0, TR_PARSE_OK, BEFORE_PATH "5:a.txt2:.." AFTER_PATH }, - - /* fail on empty string */ - { EILSEQ, TR_PARSE_ERR, "" }, - }; - - tr_logSetLevel(TR_LOG_SILENT); - - for (auto const& test : tests) - { - auto* ctor = tr_ctorNew(nullptr); - int const err = tr_ctorSetMetainfo(ctor, std::data(test.benc), std::size(test.benc)); - EXPECT_EQ(test.expected_benc_err, err); - - if (err == 0) - { - tr_parse_result const parse_result = tr_torrentParse(ctor, nullptr); - EXPECT_EQ(test.expected_parse_result, parse_result); - } - - tr_ctorFree(ctor); - } -} - -TEST(Metainfo, sanitize) +TEST_F(MetainfoTest, sanitize) { struct LocalTest { @@ -177,23 +93,22 @@ TEST(Metainfo, sanitize) } } -TEST(Metainfo, AndroidTorrent) +TEST_F(MetainfoTest, AndroidTorrent) { auto const filename = tr_strvJoin(LIBTRANSMISSION_TEST_ASSETS_DIR, "/Android-x86 8.1 r6 iso.torrent"sv); - auto* ctor = tr_ctorNew(nullptr); - auto const err = tr_ctorSetMetainfoFromFile(ctor, filename.c_str()); - EXPECT_EQ(0, err); + auto* ctor = tr_ctorNew(session_); + EXPECT_TRUE(tr_ctorSetMetainfoFromFile(ctor, filename.c_str(), nullptr)); tr_ctorFree(ctor); } -TEST(Metainfo, ctorSaveContents) +TEST_F(MetainfoTest, ctorSaveContents) { auto const src_filename = tr_strvJoin(LIBTRANSMISSION_TEST_ASSETS_DIR, "/Android-x86 8.1 r6 iso.torrent"sv); auto const tgt_filename = tr_strvJoin(::testing::TempDir(), "save-contents-test.torrent"); // try saving without passing any metainfo. - auto* ctor = tr_ctorNew(nullptr); + auto* ctor = tr_ctorNew(session_); tr_error* error = nullptr; EXPECT_FALSE(tr_ctorSaveContents(ctor, tgt_filename.c_str(), &error)); EXPECT_NE(nullptr, error); @@ -204,7 +119,8 @@ TEST(Metainfo, ctorSaveContents) } // now try saving _with_ metainfo - EXPECT_EQ(0, tr_ctorSetMetainfoFromFile(ctor, src_filename.c_str())); + EXPECT_TRUE(tr_ctorSetMetainfoFromFile(ctor, src_filename.c_str(), &error)); + EXPECT_EQ(nullptr, error); EXPECT_TRUE(tr_ctorSaveContents(ctor, tgt_filename.c_str(), &error)); EXPECT_EQ(nullptr, error); @@ -221,3 +137,7 @@ TEST(Metainfo, ctorSaveContents) tr_error_clear(&error); tr_ctorFree(ctor); } + +} // namespace test + +} // namespace libtransmission diff --git a/tests/libtransmission/rename-test.cc b/tests/libtransmission/rename-test.cc index 8f840c099..8349dff87 100644 --- a/tests/libtransmission/rename-test.cc +++ b/tests/libtransmission/rename-test.cc @@ -73,7 +73,9 @@ protected: auto* metainfo = static_cast(tr_base64_decode_str(metainfo_base64, &metainfo_len)); EXPECT_NE(nullptr, metainfo); EXPECT_LT(size_t(0), metainfo_len); - tr_ctorSetMetainfo(ctor, metainfo, metainfo_len); + tr_error* error = nullptr; + EXPECT_TRUE(tr_ctorSetMetainfo(ctor, metainfo, metainfo_len, &error)); + EXPECT_EQ(nullptr, error); tr_ctorSetPaused(ctor, TR_FORCE, true); // create the torrent diff --git a/tests/libtransmission/test-fixtures.h b/tests/libtransmission/test-fixtures.h index c35e3c24f..5f55ee14f 100644 --- a/tests/libtransmission/test-fixtures.h +++ b/tests/libtransmission/test-fixtures.h @@ -379,7 +379,9 @@ protected: EXPECT_NE(nullptr, metainfo); EXPECT_LT(size_t{ 0 }, metainfo_len); auto* ctor = tr_ctorNew(session_); - tr_ctorSetMetainfo(ctor, static_cast(metainfo), metainfo_len); + tr_error* error = nullptr; + EXPECT_TRUE(tr_ctorSetMetainfo(ctor, static_cast(metainfo), metainfo_len, &error)); + EXPECT_EQ(nullptr, error); tr_ctorSetPaused(ctor, TR_FORCE, true); tr_free(metainfo); diff --git a/tests/libtransmission/torrent-metainfo-test.cc b/tests/libtransmission/torrent-metainfo-test.cc index c9cd4eae8..336587037 100644 --- a/tests/libtransmission/torrent-metainfo-test.cc +++ b/tests/libtransmission/torrent-metainfo-test.cc @@ -19,11 +19,18 @@ #include "torrent.h" #include "utils.h" -#include "gtest/gtest.h" +#include "test-fixtures.h" using namespace std::literals; -TEST(TorrentMetainfo, magnetLink) +namespace libtransmission +{ +namespace test +{ + +using TorrentMetainfoTest = SessionTest; + +TEST_F(TorrentMetainfoTest, magnetLink) { // background info @ http://wiki.theory.org/BitTorrent_Magnet-URI_Webseeding char const constexpr* const MagnetLink = @@ -47,7 +54,7 @@ TEST(TorrentMetainfo, magnetLink) "eed6:lengthi2e4:pathl5:b.txteee4:name3:foo12:piece lengthi32768e6:pieces20:ÞÉ`âM‘‹Šs¡Å;˺¬.åÂà7:privatei0eee" // FIXME: split these into parameterized tests? -TEST(TorrentMetainfo, bucket) +TEST_F(TorrentMetainfoTest, bucket) { struct LocalTest { @@ -81,7 +88,7 @@ TEST(TorrentMetainfo, bucket) } } -TEST(TorrentMetainfo, sanitize) +TEST_F(TorrentMetainfoTest, sanitize) { struct LocalTest { @@ -139,23 +146,24 @@ TEST(TorrentMetainfo, sanitize) } } -TEST(TorrentMetainfo, AndroidTorrent) +TEST_F(TorrentMetainfoTest, AndroidTorrent) { auto const filename = tr_strvJoin(LIBTRANSMISSION_TEST_ASSETS_DIR, "/Android-x86 8.1 r6 iso.torrent"sv); - auto* ctor = tr_ctorNew(nullptr); - auto const err = tr_ctorSetMetainfoFromFile(ctor, filename.c_str()); - EXPECT_EQ(0, err); + auto* ctor = tr_ctorNew(session_); + tr_error* error = nullptr; + EXPECT_TRUE(tr_ctorSetMetainfoFromFile(ctor, filename.c_str(), &error)); + EXPECT_EQ(nullptr, error); tr_ctorFree(ctor); } -TEST(TorrentMetainfo, ctorSaveContents) +TEST_F(TorrentMetainfoTest, ctorSaveContents) { auto const src_filename = tr_strvJoin(LIBTRANSMISSION_TEST_ASSETS_DIR, "/Android-x86 8.1 r6 iso.torrent"sv); auto const tgt_filename = tr_strvJoin(::testing::TempDir(), "save-contents-test.torrent"); // try saving without passing any metainfo. - auto* ctor = tr_ctorNew(nullptr); + auto* ctor = tr_ctorNew(session_); tr_error* error = nullptr; EXPECT_FALSE(tr_ctorSaveContents(ctor, tgt_filename.c_str(), &error)); EXPECT_NE(nullptr, error); @@ -166,7 +174,8 @@ TEST(TorrentMetainfo, ctorSaveContents) } // now try saving _with_ metainfo - EXPECT_EQ(0, tr_ctorSetMetainfoFromFile(ctor, src_filename.c_str())); + EXPECT_TRUE(tr_ctorSetMetainfoFromFile(ctor, src_filename.c_str(), &error)); + EXPECT_EQ(nullptr, error); EXPECT_TRUE(tr_ctorSaveContents(ctor, tgt_filename.c_str(), &error)); EXPECT_EQ(nullptr, error); @@ -183,3 +192,6 @@ TEST(TorrentMetainfo, ctorSaveContents) tr_error_clear(&error); tr_ctorFree(ctor); } + +} // namespace test +} // namespace libtransmission