refactor: use tr_torrent_metainfo in tr ctor (#2345)

This commit is contained in:
Charles Kerr 2021-12-26 10:25:07 -06:00 committed by GitHub
parent a505447454
commit a515c1d94b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 194 additions and 391 deletions

View File

@ -142,7 +142,7 @@ static void onTorrentFileDownloaded(
void* vctor)
{
auto* ctor = static_cast<tr_ctor*>(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)
{

View File

@ -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<tr_session const*>(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(

View File

@ -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);
}

View File

@ -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;
}

View File

@ -31,8 +31,10 @@
#include <event2/buffer.h>
#include <libtransmission/transmission.h>
#include <libtransmission/log.h>
#include <libtransmission/rpcimpl.h>
#include <libtransmission/torrent-metainfo.h>
#include <libtransmission/tr-assert.h>
#include <libtransmission/utils.h> /* tr_free */
#include <libtransmission/variant.h>
@ -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<ErrorCode>(err), inf.name);
signal_add_error.emit(static_cast<ErrorCode>(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<Gio::File> 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<Gio::File> 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<Gio::File> 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);
}
}

View File

@ -22,6 +22,7 @@
#include "transmission.h"
#include "tr-macros.h"
#include "torrent.h"
struct tr_error;
struct tr_variant;

View File

@ -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);
}
}

View File

@ -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)
{

View File

@ -6,21 +6,21 @@
*
*/
#include <cerrno> /* EINVAL */
#include <cerrno> // EINVAL
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#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<bool> 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<tr_file_index_t> wanted;
std::vector<tr_file_index_t> 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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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.
*

View File

@ -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_parse_result>(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_parse_result>(tr_ctorSetMetainfoFromMagnetLink(ctor, magnetAddress.UTF8String));
loaded = tr_ctorSetMetainfoFromMagnetLink(ctor, magnetAddress.UTF8String, nullptr);
}
if (result == TR_PARSE_OK)
if (loaded)
{
fHandle = tr_torrentNew(ctor, NULL);
}

View File

@ -13,113 +13,29 @@
#include "torrent.h"
#include "utils.h"
#include "gtest/gtest.h"
#include "test-fixtures.h"
#include <array>
#include <cerrno>
#include <cstring>
#include <string_view>
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, 9>{
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

View File

@ -73,7 +73,9 @@ protected:
auto* metainfo = static_cast<char*>(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

View File

@ -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<char const*>(metainfo), metainfo_len);
tr_error* error = nullptr;
EXPECT_TRUE(tr_ctorSetMetainfo(ctor, static_cast<char const*>(metainfo), metainfo_len, &error));
EXPECT_EQ(nullptr, error);
tr_ctorSetPaused(ctor, TR_FORCE, true);
tr_free(metainfo);

View File

@ -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