fix: magnet link regression (#2390)
This commit is contained in:
parent
43b9d5c147
commit
a9284c0a6b
|
@ -298,7 +298,7 @@ bool tr_ctorGetIncompleteDir(tr_ctor const* ctor, char const** setme)
|
|||
|
||||
tr_torrent_metainfo const* tr_ctorGetMetainfo(tr_ctor const* ctor)
|
||||
{
|
||||
return !std::empty(ctor->metainfo) ? &ctor->metainfo : nullptr;
|
||||
return !std::empty(ctor->metainfo.infoHashString()) ? &ctor->metainfo : nullptr;
|
||||
}
|
||||
|
||||
tr_session* tr_ctorGetSession(tr_ctor const* ctor)
|
||||
|
|
|
@ -17,12 +17,14 @@
|
|||
#include "transmission.h"
|
||||
|
||||
#include "crypto-utils.h" /* tr_sha1() */
|
||||
#include "error.h"
|
||||
#include "file.h"
|
||||
#include "log.h"
|
||||
#include "magnet-metainfo.h"
|
||||
#include "metainfo.h"
|
||||
#include "resume.h"
|
||||
#include "torrent-magnet.h"
|
||||
#include "torrent-metainfo.h"
|
||||
#include "torrent.h"
|
||||
#include "tr-assert.h"
|
||||
#include "utils.h"
|
||||
|
@ -227,7 +229,80 @@ static int getPieceLength(struct tr_incomplete_metadata const* m, int piece)
|
|||
METADATA_PIECE_SIZE;
|
||||
}
|
||||
|
||||
static bool useNewMetainfo(tr_torrent* tor, tr_incomplete_metadata* m)
|
||||
static void tr_buildMetainfoExceptInfoDict(tr_info const& tm, tr_variant* top)
|
||||
{
|
||||
tr_variantInitDict(top, 6);
|
||||
|
||||
if (auto const& val = tm.comment(); !std::empty(val))
|
||||
{
|
||||
tr_variantDictAddStr(top, TR_KEY_comment, val);
|
||||
}
|
||||
|
||||
if (auto const& val = tm.source(); !std::empty(val))
|
||||
{
|
||||
tr_variantDictAddStr(top, TR_KEY_source, val);
|
||||
}
|
||||
|
||||
if (auto const& val = tm.creator(); !std::empty(val))
|
||||
{
|
||||
tr_variantDictAddStr(top, TR_KEY_created_by, val);
|
||||
}
|
||||
|
||||
if (auto const val = tm.dateCreated(); val != 0)
|
||||
{
|
||||
tr_variantDictAddInt(top, TR_KEY_creation_date, val);
|
||||
}
|
||||
|
||||
if (auto const& announce_list = tm.announceList(); !std::empty(announce_list))
|
||||
{
|
||||
auto const n = std::size(announce_list);
|
||||
if (n == 1)
|
||||
{
|
||||
tr_variantDictAddStr(top, TR_KEY_announce, announce_list.at(0).announce_str.sv());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* const announce_list_variant = tr_variantDictAddList(top, TR_KEY_announce_list, n);
|
||||
tr_variant* tier_variant = nullptr;
|
||||
auto current_tier = std::optional<tr_tracker_tier_t>{};
|
||||
for (auto const& tracker : announce_list)
|
||||
{
|
||||
if (!current_tier || *current_tier != tracker.tier)
|
||||
{
|
||||
tier_variant = tr_variantListAddList(announce_list_variant, n);
|
||||
}
|
||||
|
||||
tr_variantListAddStr(tier_variant, tracker.announce_str.sv());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const n_webseeds = tm.webseedCount(); n_webseeds > 0)
|
||||
{
|
||||
auto* const webseeds_variant = tr_variantDictAddList(top, TR_KEY_url_list, n_webseeds);
|
||||
for (size_t i = 0; i < n_webseeds; ++i)
|
||||
{
|
||||
tr_variantListAddStr(webseeds_variant, tm.webseed(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (tm.fileCount() == 0)
|
||||
{
|
||||
// local transmission extensions.
|
||||
// these temporary placeholders are used for magnets until we have the info dict.
|
||||
auto* const magnet_info = tr_variantDictAddDict(top, TR_KEY_magnet_info, 2);
|
||||
tr_variantDictAddStr(
|
||||
magnet_info,
|
||||
TR_KEY_info_hash,
|
||||
std::string_view{ reinterpret_cast<char const*>(std::data(tm.infoHash())), std::size(tm.infoHash()) });
|
||||
if (auto const& val = tm.name(); !std::empty(val))
|
||||
{
|
||||
tr_variantDictAddStr(magnet_info, TR_KEY_display_name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool useNewMetainfo(tr_torrent* tor, tr_incomplete_metadata* m, tr_error** error)
|
||||
{
|
||||
auto const sha1 = tr_sha1(std::string_view{ m->metadata, m->metadata_size });
|
||||
bool const checksum_passed = sha1 && *sha1 == tor->infoHash();
|
||||
|
@ -237,58 +312,53 @@ static bool useNewMetainfo(tr_torrent* tor, tr_incomplete_metadata* m)
|
|||
}
|
||||
|
||||
// checksum passed; now try to parse it as benc
|
||||
auto infoDict = tr_variant{};
|
||||
auto const metadata_sv = std::string_view{ m->metadata, m->metadata_size };
|
||||
auto const metainfoParsed = tr_variantFromBuf(&infoDict, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, metadata_sv);
|
||||
if (!metainfoParsed)
|
||||
auto info_dict_v = tr_variant{};
|
||||
auto const info_dict_sv = std::string_view{ m->metadata, m->metadata_size };
|
||||
if (!tr_variantFromBuf(&info_dict_v, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, info_dict_sv, nullptr, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// yay we have bencoded metainfo... merge it into our .torrent file
|
||||
auto success = bool{ false };
|
||||
tr_variant newMetainfo;
|
||||
auto const path = tor->torrentFile();
|
||||
// yay we have an info dict. Let's make a .torrent file
|
||||
auto top_v = tr_variant{};
|
||||
tr_buildMetainfoExceptInfoDict(tor->info, &top_v);
|
||||
tr_variantMergeDicts(tr_variantDictAddDict(&top_v, TR_KEY_info, 0), &info_dict_v);
|
||||
auto const benc = tr_variantToStr(&top_v, TR_VARIANT_FMT_BENC);
|
||||
|
||||
if (tr_variantFromFile(&newMetainfo, TR_VARIANT_PARSE_BENC, path, nullptr))
|
||||
// does this synthetic .torrent file parse?
|
||||
auto parsed = tr_metainfoParse(tor->session, &top_v, error);
|
||||
tr_variantFree(&top_v);
|
||||
tr_variantFree(&info_dict_v);
|
||||
if (!parsed)
|
||||
{
|
||||
// remove any old .torrent and .resume files
|
||||
tr_sys_path_remove(path.c_str(), nullptr);
|
||||
tr_torrentRemoveResume(tor);
|
||||
|
||||
dbgmsg(tor, "Saving completed metadata to \"%s\"", path.c_str());
|
||||
tr_variantMergeDicts(tr_variantDictAddDict(&newMetainfo, TR_KEY_info, 0), &infoDict);
|
||||
|
||||
auto info = tr_metainfoParse(tor->session, &newMetainfo, nullptr);
|
||||
success = !!info;
|
||||
if (info && tr_block_info::bestBlockSize(info->info.pieceSize()) == 0)
|
||||
{
|
||||
tor->setLocalError(_("Magnet torrent's metadata is not usable"));
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
// tor should keep this metainfo
|
||||
tor->swapMetainfo(*info);
|
||||
|
||||
// save the new .torrent file
|
||||
tr_variantToFile(&newMetainfo, TR_VARIANT_FMT_BENC, tor->torrentFile());
|
||||
tr_torrentGotNewInfoDict(tor);
|
||||
tor->setDirty();
|
||||
}
|
||||
|
||||
tr_variantFree(&newMetainfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
tr_variantFree(&infoDict);
|
||||
// save it
|
||||
auto const& torrent_dir = tor->session->torrent_dir;
|
||||
auto const filename = tr_magnet_metainfo::makeFilename(
|
||||
torrent_dir,
|
||||
tor->name(),
|
||||
tor->infoHashString(),
|
||||
tr_magnet_metainfo::BasenameFormat::Hash,
|
||||
".torrent"sv);
|
||||
if (!tr_saveFile(filename, benc, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return success;
|
||||
// tor should keep this metainfo
|
||||
tor->swapMetainfo(*parsed);
|
||||
tr_torrentGotNewInfoDict(tor);
|
||||
tor->setDirty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void onHaveAllMetainfo(tr_torrent* tor, tr_incomplete_metadata* m)
|
||||
{
|
||||
if (useNewMetainfo(tor, m))
|
||||
tr_error* error = nullptr;
|
||||
if (useNewMetainfo(tor, m, &error))
|
||||
{
|
||||
incompleteMetadataFree(tor->incompleteMetadata);
|
||||
tor->incompleteMetadata = nullptr;
|
||||
|
@ -308,7 +378,9 @@ static void onHaveAllMetainfo(tr_torrent* tor, tr_incomplete_metadata* m)
|
|||
}
|
||||
|
||||
m->piecesNeededCount = n;
|
||||
dbgmsg(tor, "metadata error; trying again. %d pieces left", n);
|
||||
char const* const msg = error != nullptr && error->message != nullptr ? error->message : "unknown error";
|
||||
dbgmsg(tor, "metadata error: %s. (trying again; %d pieces left)", msg, n);
|
||||
tr_error_clear(&error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <cstddef> // size_t
|
||||
#include <ctime>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
struct tr_torrent;
|
||||
|
||||
// defined by BEP #9
|
||||
|
|
|
@ -797,20 +797,14 @@ tr_torrent* tr_torrentNew(tr_ctor const* ctor, tr_torrent** setme_duplicate_of)
|
|||
TR_ASSERT(tr_isSession(session));
|
||||
|
||||
// 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)
|
||||
auto const* metainfo = tr_ctorGetMetainfo(ctor);
|
||||
if (metainfo == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// is it a duplicate?
|
||||
if (auto* const duplicate_of = session->getTorrent(parsed->info.infoHash()); duplicate_of != nullptr)
|
||||
if (auto* const duplicate_of = session->getTorrent(metainfo->infoHash()); duplicate_of != nullptr)
|
||||
{
|
||||
if (setme_duplicate_of != nullptr)
|
||||
{
|
||||
|
@ -820,6 +814,26 @@ tr_torrent* tr_torrentNew(tr_ctor const* ctor, tr_torrent** setme_duplicate_of)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// build a variant to parse
|
||||
auto top_variant = tr_variant{};
|
||||
if (std::empty(*metainfo))
|
||||
{
|
||||
metainfo->toVariant(&top_variant);
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_variantFromBuf(&top_variant, TR_VARIANT_PARSE_BENC, tr_ctorGetContents(ctor), nullptr, nullptr);
|
||||
}
|
||||
|
||||
// parse the metainfo
|
||||
tr_error* error = nullptr;
|
||||
auto parsed = tr_metainfoParse(session, &top_variant, &error);
|
||||
tr_variantFree(&top_variant);
|
||||
if (!parsed)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// add it
|
||||
auto* const tor = new tr_torrent{ parsed->info };
|
||||
tor->swapMetainfo(*parsed);
|
||||
|
@ -3144,3 +3158,8 @@ void tr_torrent::setFileSubpath(tr_file_index_t i, std::string_view subpath)
|
|||
{
|
||||
this->info.setFileSubpath(i, subpath);
|
||||
}
|
||||
|
||||
void tr_info::setAnnounceList(tr_announce_list const& list)
|
||||
{
|
||||
this->announce_list = std::make_shared<tr_announce_list>(list);
|
||||
}
|
||||
|
|
|
@ -1608,6 +1608,13 @@ struct tr_info
|
|||
return is_private_;
|
||||
}
|
||||
|
||||
void setAnnounceList(tr_announce_list const& list);
|
||||
|
||||
tr_announce_list const& announceList() const
|
||||
{
|
||||
return *announce_list;
|
||||
}
|
||||
|
||||
tr_sha1_digest_t hash_;
|
||||
std::shared_ptr<tr_announce_list> announce_list;
|
||||
std::vector<tr_file> files;
|
||||
|
|
Loading…
Reference in New Issue