mirror of
https://github.com/transmission/transmission
synced 2025-02-22 06:00:41 +00:00
refactor: use tr_torrent_metainfo in tr ctor (#2345)
This commit is contained in:
parent
a505447454
commit
a515c1d94b
17 changed files with 194 additions and 391 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "transmission.h"
|
||||
|
||||
#include "tr-macros.h"
|
||||
#include "torrent.h"
|
||||
|
||||
struct tr_error;
|
||||
struct tr_variant;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:ÞÉ`âMs¡Å;˺¬.åÂà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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:ÞÉ`âMs¡Å;˺¬.åÂà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
|
||||
|
|
Loading…
Reference in a new issue