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)
|
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)
|
tr_session* tr_ctorGetSession(tr_ctor const* ctor)
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
#include "transmission.h"
|
#include "transmission.h"
|
||||||
|
|
||||||
#include "crypto-utils.h" /* tr_sha1() */
|
#include "crypto-utils.h" /* tr_sha1() */
|
||||||
|
#include "error.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "magnet-metainfo.h"
|
#include "magnet-metainfo.h"
|
||||||
#include "metainfo.h"
|
#include "metainfo.h"
|
||||||
#include "resume.h"
|
#include "resume.h"
|
||||||
#include "torrent-magnet.h"
|
#include "torrent-magnet.h"
|
||||||
|
#include "torrent-metainfo.h"
|
||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
#include "tr-assert.h"
|
#include "tr-assert.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
@ -227,7 +229,80 @@ static int getPieceLength(struct tr_incomplete_metadata const* m, int piece)
|
||||||
METADATA_PIECE_SIZE;
|
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 });
|
auto const sha1 = tr_sha1(std::string_view{ m->metadata, m->metadata_size });
|
||||||
bool const checksum_passed = sha1 && *sha1 == tor->infoHash();
|
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
|
// checksum passed; now try to parse it as benc
|
||||||
auto infoDict = tr_variant{};
|
auto info_dict_v = tr_variant{};
|
||||||
auto const metadata_sv = std::string_view{ m->metadata, m->metadata_size };
|
auto const info_dict_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 (!tr_variantFromBuf(&info_dict_v, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, info_dict_sv, nullptr, error))
|
||||||
if (!metainfoParsed)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// yay we have bencoded metainfo... merge it into our .torrent file
|
// yay we have an info dict. Let's make a .torrent file
|
||||||
auto success = bool{ false };
|
auto top_v = tr_variant{};
|
||||||
tr_variant newMetainfo;
|
tr_buildMetainfoExceptInfoDict(tor->info, &top_v);
|
||||||
auto const path = tor->torrentFile();
|
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
|
return false;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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);
|
incompleteMetadataFree(tor->incompleteMetadata);
|
||||||
tor->incompleteMetadata = nullptr;
|
tor->incompleteMetadata = nullptr;
|
||||||
|
@ -308,7 +378,9 @@ static void onHaveAllMetainfo(tr_torrent* tor, tr_incomplete_metadata* m)
|
||||||
}
|
}
|
||||||
|
|
||||||
m->piecesNeededCount = n;
|
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 <cstddef> // size_t
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
#include "transmission.h"
|
||||||
|
|
||||||
struct tr_torrent;
|
struct tr_torrent;
|
||||||
|
|
||||||
// defined by BEP #9
|
// 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));
|
TR_ASSERT(tr_isSession(session));
|
||||||
|
|
||||||
// is the metainfo valid?
|
// is the metainfo valid?
|
||||||
auto top = tr_variant{};
|
auto const* metainfo = tr_ctorGetMetainfo(ctor);
|
||||||
if (!tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC, tr_ctorGetContents(ctor), nullptr, nullptr))
|
if (metainfo == nullptr)
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto parsed = tr_metainfoParse(session, &top, nullptr);
|
|
||||||
tr_variantFree(&top);
|
|
||||||
if (!parsed)
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is it a duplicate?
|
// 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)
|
if (setme_duplicate_of != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -820,6 +814,26 @@ tr_torrent* tr_torrentNew(tr_ctor const* ctor, tr_torrent** setme_duplicate_of)
|
||||||
return nullptr;
|
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
|
// add it
|
||||||
auto* const tor = new tr_torrent{ parsed->info };
|
auto* const tor = new tr_torrent{ parsed->info };
|
||||||
tor->swapMetainfo(*parsed);
|
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);
|
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_;
|
return is_private_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAnnounceList(tr_announce_list const& list);
|
||||||
|
|
||||||
|
tr_announce_list const& announceList() const
|
||||||
|
{
|
||||||
|
return *announce_list;
|
||||||
|
}
|
||||||
|
|
||||||
tr_sha1_digest_t hash_;
|
tr_sha1_digest_t hash_;
|
||||||
std::shared_ptr<tr_announce_list> announce_list;
|
std::shared_ptr<tr_announce_list> announce_list;
|
||||||
std::vector<tr_file> files;
|
std::vector<tr_file> files;
|
||||||
|
|
Loading…
Reference in New Issue