2009-12-07 03:57:55 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2007-2014 Mnemosyne LLC
|
2006-07-16 19:39:23 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2006-07-16 19:39:23 +00:00
|
|
|
*
|
2009-12-07 03:57:55 +00:00
|
|
|
*/
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2021-10-06 22:24:04 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <array>
|
2021-11-12 16:42:51 +00:00
|
|
|
#include <cstring>
|
2021-11-04 01:36:32 +00:00
|
|
|
#include <iterator>
|
2021-10-14 15:36:49 +00:00
|
|
|
#include <string_view>
|
2021-11-12 16:42:51 +00:00
|
|
|
#include <vector>
|
2007-07-29 18:11:21 +00:00
|
|
|
|
2006-07-16 19:39:23 +00:00
|
|
|
#include "transmission.h"
|
2021-10-29 18:24:30 +00:00
|
|
|
|
2014-12-04 12:13:59 +00:00
|
|
|
#include "crypto-utils.h" /* tr_sha1 */
|
2021-11-12 16:42:51 +00:00
|
|
|
#include "error.h"
|
|
|
|
#include "error-types.h"
|
2014-07-08 00:08:43 +00:00
|
|
|
#include "file.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2007-07-09 20:10:42 +00:00
|
|
|
#include "metainfo.h"
|
2017-04-21 07:40:57 +00:00
|
|
|
#include "platform.h" /* tr_getTorrentDir() */
|
2014-07-08 00:08:43 +00:00
|
|
|
#include "session.h"
|
2021-11-28 03:17:47 +00:00
|
|
|
#include "torrent.h"
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2007-07-30 18:04:10 +00:00
|
|
|
#include "utils.h"
|
2012-12-14 04:34:42 +00:00
|
|
|
#include "variant.h"
|
2021-11-09 03:30:03 +00:00
|
|
|
#include "web-utils.h"
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2021-10-15 13:28:47 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2007-10-20 15:17:36 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2021-11-20 00:36:25 +00:00
|
|
|
std::string tr_buildTorrentFilename(
|
|
|
|
std::string_view dirname,
|
|
|
|
tr_info const* inf,
|
|
|
|
enum tr_metainfo_basename_format format,
|
|
|
|
std::string_view suffix)
|
2014-04-27 20:17:16 +00:00
|
|
|
{
|
2021-11-20 00:36:25 +00:00
|
|
|
return format == TR_METAINFO_BASENAME_NAME_AND_PARTIAL_HASH ?
|
|
|
|
tr_strvJoin(dirname, "/"sv, inf->name, "."sv, std::string_view{ inf->hashString, 16 }, suffix) :
|
|
|
|
tr_strvJoin(dirname, "/"sv, inf->hashString, suffix);
|
2014-04-27 20:17:16 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 00:36:25 +00:00
|
|
|
static std::string getTorrentFilename(tr_session const* session, tr_info const* inf, enum tr_metainfo_basename_format format)
|
2010-04-20 23:14:00 +00:00
|
|
|
{
|
2021-11-20 00:36:25 +00:00
|
|
|
return tr_buildTorrentFilename(tr_getTorrentDir(session), inf, format, ".torrent"sv);
|
2008-04-14 11:52:50 +00:00
|
|
|
}
|
|
|
|
|
2008-08-20 21:01:17 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
bool tr_metainfoAppendSanitizedPathComponent(std::string& out, std::string_view in, bool* is_adjusted)
|
2009-12-28 00:35:29 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
auto const original_out_len = std::size(out);
|
|
|
|
auto const original_in = in;
|
2019-06-23 13:23:22 +00:00
|
|
|
*is_adjusted = false;
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
// remove leading spaces
|
|
|
|
auto constexpr leading_test = [](auto ch)
|
|
|
|
{
|
|
|
|
return isspace(ch);
|
2019-06-23 13:23:22 +00:00
|
|
|
};
|
2021-11-04 01:36:32 +00:00
|
|
|
auto const it = std::find_if_not(std::begin(in), std::end(in), leading_test);
|
|
|
|
in.remove_prefix(std::distance(std::begin(in), it));
|
2019-06-23 13:23:22 +00:00
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
// remove trailing spaces and '.'
|
|
|
|
auto constexpr trailing_test = [](auto ch)
|
|
|
|
{
|
|
|
|
return isspace(ch) || ch == '.';
|
|
|
|
};
|
|
|
|
auto const rit = std::find_if_not(std::rbegin(in), std::rend(in), trailing_test);
|
|
|
|
in.remove_suffix(std::distance(std::rbegin(in), rit));
|
2019-06-23 13:23:22 +00:00
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
// munge banned characters
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
|
|
|
|
auto constexpr ensure_legal_char = [](auto ch)
|
2019-06-23 13:23:22 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
auto constexpr Banned = std::string_view{ "<>:\"/\\|?*" };
|
2021-11-10 00:13:47 +00:00
|
|
|
auto const banned = tr_strvContains(Banned, ch) || (unsigned char)ch < 0x20;
|
2021-11-04 01:36:32 +00:00
|
|
|
return banned ? '_' : ch;
|
|
|
|
};
|
|
|
|
auto const old_out_len = std::size(out);
|
|
|
|
std::transform(std::begin(in), std::end(in), std::back_inserter(out), ensure_legal_char);
|
2019-06-23 13:23:22 +00:00
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
// munge banned filenames
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
|
|
|
|
auto constexpr ReservedNames = std::array<std::string_view, 22>{
|
|
|
|
"CON"sv, "PRN"sv, "AUX"sv, "NUL"sv, "COM1"sv, "COM2"sv, "COM3"sv, "COM4"sv, "COM5"sv, "COM6"sv, "COM7"sv,
|
|
|
|
"COM8"sv, "COM9"sv, "LPT1"sv, "LPT2"sv, "LPT3"sv, "LPT4"sv, "LPT5"sv, "LPT6"sv, "LPT7"sv, "LPT8"sv, "LPT9"sv,
|
|
|
|
};
|
|
|
|
for (auto const& name : ReservedNames)
|
2019-06-23 13:23:22 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
size_t const name_len = std::size(name);
|
|
|
|
if (evutil_ascii_strncasecmp(out.c_str() + old_out_len, std::data(name), name_len) != 0 ||
|
|
|
|
(out[old_out_len + name_len] != '\0' && out[old_out_len + name_len] != '.'))
|
2019-06-23 13:23:22 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
out.insert(std::begin(out) + old_out_len + name_len, '_');
|
2019-06-23 13:23:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
*is_adjusted = original_in != std::string_view{ out.c_str() + original_out_len };
|
|
|
|
return std::size(out) > original_out_len;
|
2009-12-28 00:35:29 +00:00
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
static bool getfile(char** setme, bool* is_adjusted, std::string_view root, tr_variant* path, std::string& buf)
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
bool success = false;
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
*setme = nullptr;
|
2019-06-23 13:23:22 +00:00
|
|
|
*is_adjusted = false;
|
2014-04-27 20:17:16 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_variantIsList(path))
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
success = true;
|
2021-11-04 01:36:32 +00:00
|
|
|
|
|
|
|
buf = root;
|
2014-04-27 20:17:16 +00:00
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (int i = 0, n = tr_variantListSize(path); i < n; i++)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
auto raw = std::string_view{};
|
|
|
|
if (!tr_variantGetStrView(tr_variantListChild(path, i), &raw))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
success = false;
|
|
|
|
break;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
auto is_component_adjusted = bool{};
|
|
|
|
auto const pos = std::size(buf);
|
|
|
|
if (!tr_metainfoAppendSanitizedPathComponent(buf, raw, &is_component_adjusted))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2014-06-09 02:53:04 +00:00
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
buf.insert(std::begin(buf) + pos, TR_PATH_DELIMITER);
|
2019-06-23 13:23:22 +00:00
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
*is_adjusted |= is_component_adjusted;
|
2014-04-27 20:17:16 +00:00
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
if (success && std::size(buf) <= std::size(root))
|
2014-06-09 02:53:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
success = false;
|
2014-06-09 02:53:04 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (success)
|
2009-12-28 00:35:29 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
*setme = tr_utf8clean(buf);
|
|
|
|
*is_adjusted |= buf != *setme;
|
2009-12-28 00:35:29 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return success;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static char const* parseFiles(tr_info* inf, tr_variant* files, tr_variant const* length)
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-10-24 17:40:15 +00:00
|
|
|
int64_t len = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->totalSize = 0;
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2021-10-24 17:40:15 +00:00
|
|
|
bool is_root_adjusted = false;
|
2021-11-04 01:36:32 +00:00
|
|
|
auto root_name = std::string{};
|
|
|
|
if (!tr_metainfoAppendSanitizedPathComponent(root_name, inf->name, &is_root_adjusted))
|
2019-06-23 13:23:22 +00:00
|
|
|
{
|
|
|
|
return "path";
|
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
char const* errstr = nullptr;
|
2019-06-23 13:23:22 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_variantIsList(files)) /* multi-file mode */
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
auto buf = std::string{};
|
|
|
|
errstr = nullptr;
|
2008-08-21 16:12:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->isFolder = true;
|
|
|
|
inf->fileCount = tr_variantListSize(files);
|
|
|
|
inf->files = tr_new0(tr_file, inf->fileCount);
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (tr_file_index_t i = 0; i < inf->fileCount; i++)
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-10-24 17:40:15 +00:00
|
|
|
auto* const file = tr_variantListChild(files, i);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (!tr_variantIsDict(file))
|
2014-04-27 20:17:16 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
errstr = "files";
|
2017-04-19 12:04:45 +00:00
|
|
|
break;
|
2014-04-27 20:17:16 +00:00
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2021-10-24 17:40:15 +00:00
|
|
|
tr_variant* path = nullptr;
|
2021-08-15 09:41:48 +00:00
|
|
|
if (!tr_variantDictFindList(file, TR_KEY_path_utf_8, &path) && !tr_variantDictFindList(file, TR_KEY_path, &path))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
errstr = "path";
|
2020-11-01 21:47:57 +00:00
|
|
|
break;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2021-10-24 17:40:15 +00:00
|
|
|
bool is_file_adjusted = false;
|
2019-06-23 13:23:22 +00:00
|
|
|
if (!getfile(&inf->files[i].name, &is_file_adjusted, root_name, path, buf))
|
2014-04-27 20:17:16 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
errstr = "path";
|
2017-04-19 12:04:45 +00:00
|
|
|
break;
|
2014-04-27 20:17:16 +00:00
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!tr_variantDictFindInt(file, TR_KEY_length, &len))
|
2014-04-27 20:17:16 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
errstr = "length";
|
2017-04-19 12:04:45 +00:00
|
|
|
break;
|
2014-04-27 20:17:16 +00:00
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->files[i].length = len;
|
2021-11-28 03:17:47 +00:00
|
|
|
inf->files[i].priv.is_renamed = is_root_adjusted || is_file_adjusted;
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->totalSize += len;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else if (tr_variantGetInt(length, &len)) /* single-file mode */
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->isFolder = false;
|
|
|
|
inf->fileCount = 1;
|
|
|
|
inf->files = tr_new0(tr_file, 1);
|
2021-11-04 01:36:32 +00:00
|
|
|
inf->files[0].name = tr_strndup(root_name.c_str(), std::size(root_name));
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->files[0].length = len;
|
2021-11-28 03:17:47 +00:00
|
|
|
inf->files[0].priv.is_renamed = is_root_adjusted;
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->totalSize += len;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-11-04 01:36:32 +00:00
|
|
|
errstr = "length";
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
|
2021-11-04 01:36:32 +00:00
|
|
|
return errstr;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static char const* getannounce(tr_info* inf, tr_variant* meta)
|
2008-04-24 03:26:36 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
inf->announce_list = std::make_shared<tr_announce_list>();
|
|
|
|
|
|
|
|
// tr_tracker_info* trackers = nullptr;
|
|
|
|
// int trackerCount = 0;
|
2021-11-05 06:29:19 +00:00
|
|
|
auto url = std::string_view{};
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
/* Announce-list */
|
2021-11-05 06:29:19 +00:00
|
|
|
tr_variant* tiers = nullptr;
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_variantDictFindList(meta, TR_KEY_announce_list, &tiers))
|
2008-04-24 03:26:36 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
for (size_t i = 0, in = tr_variantListSize(tiers); i < in; ++i)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
tr_variant* tier_list = tr_variantListChild(tiers, i);
|
|
|
|
if (tier_list == nullptr)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-14 20:59:40 +00:00
|
|
|
for (size_t j = 0, jn = tr_variantListSize(tier_list); j < jn; ++j)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
if (!tr_variantGetStrView(tr_variantListChild(tier_list, j), &url))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
continue;
|
2008-04-24 03:26:36 +00:00
|
|
|
}
|
2009-08-10 20:04:08 +00:00
|
|
|
|
2021-12-14 20:59:40 +00:00
|
|
|
inf->announce_list->add(i, url);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-04-24 03:26:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* Regular announce value */
|
2021-12-14 20:59:40 +00:00
|
|
|
if (std::empty(*inf->announce_list) && tr_variantDictFindStrView(meta, TR_KEY_announce, &url))
|
2008-04-24 03:26:36 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
inf->announce_list->add(0, url);
|
2008-04-24 03:26:36 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
return nullptr;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
|
|
|
|
2011-09-06 16:45:48 +00:00
|
|
|
/**
|
|
|
|
* @brief Ensure that the URLs for multfile torrents end in a slash.
|
|
|
|
*
|
|
|
|
* See http://bittorrent.org/beps/bep_0019.html#metadata-extension
|
|
|
|
* for background on how the trailing slash is used for "url-list"
|
|
|
|
* fields.
|
|
|
|
*
|
|
|
|
* This function is to workaround some .torrent generators, such as
|
|
|
|
* mktorrent and very old versions of utorrent, that don't add the
|
|
|
|
* trailing slash for multifile torrents if omitted by the end user.
|
|
|
|
*/
|
2021-11-05 06:29:19 +00:00
|
|
|
static char* fix_webseed_url(tr_info const* inf, std::string_view url)
|
2011-09-06 16:45:48 +00:00
|
|
|
{
|
2021-11-10 00:13:47 +00:00
|
|
|
url = tr_strvStrip(url);
|
2012-10-14 18:10:17 +00:00
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
if (!tr_urlIsValid(url))
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2011-09-06 16:45:48 +00:00
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
if (inf->fileCount > 1 && !std::empty(url) && url.back() != '/')
|
2011-09-06 16:45:48 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
return tr_strdup_printf("%" TR_PRIsv "/", TR_PRIsv_ARG(url));
|
2011-09-06 16:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-11-10 00:13:47 +00:00
|
|
|
return tr_strvDup(url);
|
2011-09-06 16:45:48 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void geturllist(tr_info* inf, tr_variant* meta)
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-10-24 17:40:15 +00:00
|
|
|
tr_variant* urls = nullptr;
|
2021-11-05 06:29:19 +00:00
|
|
|
auto url = std::string_view{};
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_variantDictFindList(meta, TR_KEY_url_list, &urls))
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
int const n = tr_variantListSize(urls);
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->webseedCount = 0;
|
|
|
|
inf->webseeds = tr_new0(char*, n);
|
2008-05-29 00:38:31 +00:00
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (int i = 0; i < n; i++)
|
2011-05-05 03:10:51 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
if (tr_variantGetStrView(tr_variantListChild(urls, i), &url))
|
2011-05-05 03:10:51 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
char* const fixed_url = fix_webseed_url(inf, url);
|
2021-09-15 00:18:09 +00:00
|
|
|
if (fixed_url != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
inf->webseeds[inf->webseedCount++] = fixed_url;
|
|
|
|
}
|
2011-05-05 03:10:51 +00:00
|
|
|
}
|
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
2021-11-05 06:29:19 +00:00
|
|
|
else if (tr_variantDictFindStrView(meta, TR_KEY_url_list, &url)) /* handle single items in webseeds */
|
2010-08-22 18:40:18 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
char* const fixed_url = fix_webseed_url(inf, url);
|
2021-09-15 00:18:09 +00:00
|
|
|
if (fixed_url != nullptr)
|
2011-05-05 03:10:51 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->webseedCount = 1;
|
|
|
|
inf->webseeds = tr_new0(char*, 1);
|
|
|
|
inf->webseeds[0] = fixed_url;
|
2011-05-05 03:10:51 +00:00
|
|
|
}
|
2010-08-22 18:40:18 +00:00
|
|
|
}
|
2008-04-24 03:26:36 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
static char const* tr_metainfoParseImpl(
|
|
|
|
tr_session const* session,
|
|
|
|
tr_info* inf,
|
2021-11-12 16:42:51 +00:00
|
|
|
std::vector<tr_sha1_digest_t>* pieces,
|
|
|
|
uint64_t* infoDictLength,
|
2017-04-20 16:02:19 +00:00
|
|
|
tr_variant const* meta_in)
|
2007-12-21 22:18:40 +00:00
|
|
|
{
|
2021-10-24 17:40:15 +00:00
|
|
|
int64_t i = 0;
|
2021-11-05 06:29:19 +00:00
|
|
|
auto sv = std::string_view{};
|
2021-10-24 17:40:15 +00:00
|
|
|
tr_variant* const meta = const_cast<tr_variant*>(meta_in);
|
2017-04-19 12:04:45 +00:00
|
|
|
bool isMagnet = false;
|
|
|
|
|
|
|
|
/* info_hash: urlencoded 20-byte SHA1 hash of the value of the info key
|
|
|
|
* from the Metainfo file. Note that the value will be a bencoded
|
|
|
|
* dictionary, given the definition of the info key above. */
|
2021-10-24 17:40:15 +00:00
|
|
|
tr_variant* infoDict = nullptr;
|
2021-11-18 00:17:09 +00:00
|
|
|
if (bool b = tr_variantDictFindDict(meta, TR_KEY_info, &infoDict); !b)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
/* no info dictionary... is this a magnet link? */
|
2021-11-18 00:17:09 +00:00
|
|
|
if (tr_variant* d = nullptr; tr_variantDictFindDict(meta, TR_KEY_magnet_info, &d))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
isMagnet = true;
|
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
// get the info-hash
|
|
|
|
if (!tr_variantDictFindStrView(d, TR_KEY_info_hash, &sv))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "info_hash";
|
|
|
|
}
|
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
if (std::size(sv) != SHA_DIGEST_LENGTH)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "info_hash";
|
|
|
|
}
|
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
std::copy(std::begin(sv), std::end(sv), inf->hash);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_sha1_to_hex(inf->hashString, inf->hash);
|
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
// maybe get the display name
|
|
|
|
if (tr_variantDictFindStrView(d, TR_KEY_display_name, &sv))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_free(inf->name);
|
2021-11-10 00:13:47 +00:00
|
|
|
inf->name = tr_strvDup(sv);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
if (inf->name == nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
inf->name = tr_strdup(inf->hashString);
|
|
|
|
}
|
|
|
|
}
|
2021-11-05 06:29:19 +00:00
|
|
|
else // not a magnet link and has no info dict...
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "info";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2010-02-02 22:45:22 +00:00
|
|
|
{
|
2021-10-24 17:40:15 +00:00
|
|
|
size_t blen = 0;
|
2020-08-11 18:11:55 +00:00
|
|
|
char* bstr = tr_variantToStr(infoDict, TR_VARIANT_FMT_BENC, &blen);
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_sha1(inf->hash, bstr, (int)blen, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_sha1_to_hex(inf->hashString, inf->hash);
|
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
if (infoDictLength != nullptr)
|
2010-02-02 22:45:22 +00:00
|
|
|
{
|
2020-08-11 18:11:55 +00:00
|
|
|
*infoDictLength = blen;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tr_free(bstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* name */
|
|
|
|
if (!isMagnet)
|
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
if (!tr_variantDictFindStrView(infoDict, TR_KEY_name_utf_8, &sv) &&
|
|
|
|
!tr_variantDictFindStrView(infoDict, TR_KEY_name, &sv))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
sv = ""sv;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2010-08-03 03:16:21 +00:00
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
if (std::empty(sv))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "name";
|
2010-02-02 22:45:22 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
tr_free(inf->name);
|
2021-11-05 06:29:19 +00:00
|
|
|
inf->name = tr_utf8clean(sv);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* comment */
|
2021-11-05 06:29:19 +00:00
|
|
|
if (!tr_variantDictFindStrView(meta, TR_KEY_comment_utf_8, &sv) && !tr_variantDictFindStrView(meta, TR_KEY_comment, &sv))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
sv = ""sv;
|
2010-02-02 22:45:22 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
tr_free(inf->comment);
|
2021-11-05 06:29:19 +00:00
|
|
|
inf->comment = tr_utf8clean(sv);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
/* created by */
|
2021-11-05 06:29:19 +00:00
|
|
|
if (!tr_variantDictFindStrView(meta, TR_KEY_created_by_utf_8, &sv) &&
|
|
|
|
!tr_variantDictFindStrView(meta, TR_KEY_created_by, &sv))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
sv = ""sv;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2009-11-24 02:16:31 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_free(inf->creator);
|
2021-11-05 06:29:19 +00:00
|
|
|
inf->creator = tr_utf8clean(sv);
|
2009-11-24 02:16:31 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* creation date */
|
2021-11-05 06:29:19 +00:00
|
|
|
i = 0;
|
|
|
|
(void)!tr_variantDictFindInt(meta, TR_KEY_creation_date, &i);
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->dateCreated = i;
|
|
|
|
|
|
|
|
/* private */
|
2021-08-15 09:41:48 +00:00
|
|
|
if (!tr_variantDictFindInt(infoDict, TR_KEY_private, &i) && !tr_variantDictFindInt(meta, TR_KEY_private, &i))
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2020-11-01 21:47:57 +00:00
|
|
|
i = 0;
|
2010-02-02 22:45:22 +00:00
|
|
|
}
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
inf->isPrivate = i != 0;
|
|
|
|
|
2021-10-18 23:05:39 +00:00
|
|
|
/* source */
|
2021-11-05 06:29:19 +00:00
|
|
|
if (!tr_variantDictFindStrView(infoDict, TR_KEY_source, &sv) && !tr_variantDictFindStrView(meta, TR_KEY_source, &sv))
|
2021-10-18 23:05:39 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
sv = ""sv;
|
2021-10-18 23:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tr_free(inf->source);
|
2021-11-05 06:29:19 +00:00
|
|
|
inf->source = tr_utf8clean(sv);
|
2021-10-18 23:05:39 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* piece length */
|
|
|
|
if (!isMagnet)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!tr_variantDictFindInt(infoDict, TR_KEY_piece_length, &i) || (i < 1))
|
|
|
|
{
|
|
|
|
return "piece length";
|
|
|
|
}
|
|
|
|
|
|
|
|
inf->pieceSize = i;
|
2010-02-02 22:45:22 +00:00
|
|
|
}
|
2008-08-20 21:01:17 +00:00
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
/* pieces and files */
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!isMagnet)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
if (!tr_variantDictFindStrView(infoDict, TR_KEY_pieces, &sv))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "pieces";
|
|
|
|
}
|
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
if (std::size(sv) % SHA_DIGEST_LENGTH != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "pieces";
|
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:51 +00:00
|
|
|
auto const n_pieces = std::size(sv) / SHA_DIGEST_LENGTH;
|
|
|
|
inf->pieceCount = n_pieces;
|
|
|
|
pieces->resize(n_pieces);
|
|
|
|
std::copy_n(std::data(sv), std::size(sv), reinterpret_cast<uint8_t*>(std::data(*pieces)));
|
2008-08-20 19:21:57 +00:00
|
|
|
|
2021-11-05 06:29:19 +00:00
|
|
|
auto const* const errstr = parseFiles(
|
|
|
|
inf,
|
|
|
|
tr_variantDictFind(infoDict, TR_KEY_files),
|
|
|
|
tr_variantDictFind(infoDict, TR_KEY_length));
|
|
|
|
if (errstr != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
return errstr;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (inf->fileCount == 0 || inf->totalSize == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return "files";
|
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if ((uint64_t)inf->pieceCount != (inf->totalSize + inf->pieceSize - 1) / inf->pieceSize)
|
|
|
|
{
|
|
|
|
return "files";
|
|
|
|
}
|
2010-02-02 22:45:22 +00:00
|
|
|
}
|
2006-12-17 16:36:27 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* get announce or announce-list */
|
2021-11-05 06:29:19 +00:00
|
|
|
auto const* const errstr = getannounce(inf, meta);
|
|
|
|
if (errstr != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-11-05 06:29:19 +00:00
|
|
|
return errstr;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* get the url-list */
|
|
|
|
geturllist(inf, meta);
|
2008-06-07 21:26:41 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* filename of Transmission's copy */
|
|
|
|
tr_free(inf->torrent);
|
2021-11-20 00:36:25 +00:00
|
|
|
inf->torrent = session != nullptr ? tr_strvDup(getTorrentFilename(session, inf, TR_METAINFO_BASENAME_HASH)) : nullptr;
|
2008-04-14 11:52:50 +00:00
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
return nullptr;
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
2007-03-05 00:07:48 +00:00
|
|
|
|
2021-11-12 16:42:51 +00:00
|
|
|
std::optional<tr_metainfo_parsed> tr_metainfoParse(tr_session const* session, tr_variant const* meta_in, tr_error** error)
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-11-12 16:42:51 +00:00
|
|
|
auto out = tr_metainfo_parsed{};
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-11-12 16:42:51 +00:00
|
|
|
char const* bad_tag = tr_metainfoParseImpl(session, &out.info, &out.pieces, &out.info_dict_length, meta_in);
|
|
|
|
if (bad_tag != nullptr)
|
2008-08-20 21:01:17 +00:00
|
|
|
{
|
2021-11-12 16:42:51 +00:00
|
|
|
tr_error_set(error, TR_ERROR_EINVAL, _("Error parsing metainfo: %s"), bad_tag);
|
|
|
|
tr_metainfoFree(&out.info);
|
|
|
|
return {};
|
2008-08-20 21:01:17 +00:00
|
|
|
}
|
2009-08-05 01:59:16 +00:00
|
|
|
|
2021-11-12 16:42:51 +00:00
|
|
|
return std::optional<tr_metainfo_parsed>{ std::move(out) };
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_metainfoFree(tr_info* inf)
|
2006-12-17 16:36:27 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
if (inf->webseeds != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
for (unsigned int i = 0; i < inf->webseedCount; i++)
|
|
|
|
{
|
|
|
|
tr_free(inf->webseeds[i]);
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-06-07 21:26:41 +00:00
|
|
|
|
2021-12-14 20:59:40 +00:00
|
|
|
if (inf->files != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-12-14 20:59:40 +00:00
|
|
|
for (tr_file_index_t ff = 0; ff < inf->fileCount; ff++)
|
|
|
|
{
|
|
|
|
tr_free(inf->files[ff].name);
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-02-14 00:52:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_free(inf->comment);
|
|
|
|
tr_free(inf->creator);
|
2021-12-14 20:59:40 +00:00
|
|
|
tr_free(inf->files);
|
|
|
|
tr_free(inf->name);
|
2021-10-18 23:05:39 +00:00
|
|
|
tr_free(inf->source);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_free(inf->torrent);
|
2021-12-14 20:59:40 +00:00
|
|
|
tr_free(inf->webseeds);
|
2007-07-25 17:19:29 +00:00
|
|
|
|
2021-12-14 20:59:40 +00:00
|
|
|
inf->comment = nullptr;
|
|
|
|
inf->creator = nullptr;
|
|
|
|
inf->files = nullptr;
|
|
|
|
inf->name = nullptr;
|
|
|
|
inf->source = nullptr;
|
|
|
|
inf->torrent = nullptr;
|
|
|
|
inf->webseeds = nullptr;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-14 20:59:40 +00:00
|
|
|
inf->announce_list.reset();
|
2006-12-17 16:36:27 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
void tr_metainfoRemoveSaved(tr_session const* session, tr_info const* inf)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2021-11-20 00:36:25 +00:00
|
|
|
auto filename = getTorrentFilename(session, inf, TR_METAINFO_BASENAME_HASH);
|
|
|
|
tr_sys_path_remove(filename.c_str(), nullptr);
|
2017-07-26 18:20:30 +00:00
|
|
|
|
|
|
|
filename = getTorrentFilename(session, inf, TR_METAINFO_BASENAME_NAME_AND_PARTIAL_HASH);
|
2021-11-20 00:36:25 +00:00
|
|
|
tr_sys_path_remove(filename.c_str(), nullptr);
|
2017-07-26 18:20:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
void tr_metainfoMigrateFile(
|
|
|
|
tr_session const* session,
|
|
|
|
tr_info const* info,
|
|
|
|
enum tr_metainfo_basename_format old_format,
|
2017-07-26 18:20:30 +00:00
|
|
|
enum tr_metainfo_basename_format new_format)
|
|
|
|
{
|
2021-11-20 00:36:25 +00:00
|
|
|
auto const old_filename = getTorrentFilename(session, info, old_format);
|
|
|
|
auto const new_filename = getTorrentFilename(session, info, new_format);
|
2017-07-26 18:20:30 +00:00
|
|
|
|
2021-11-20 00:36:25 +00:00
|
|
|
if (tr_sys_path_rename(old_filename.c_str(), new_filename.c_str(), nullptr))
|
2017-07-26 18:20:30 +00:00
|
|
|
{
|
2021-11-20 00:36:25 +00:00
|
|
|
tr_logAddNamedError(
|
|
|
|
info->name,
|
|
|
|
"Migrated torrent file from \"%s\" to \"%s\"",
|
|
|
|
old_filename.c_str(),
|
|
|
|
new_filename.c_str());
|
2017-07-26 18:20:30 +00:00
|
|
|
}
|
2007-03-13 06:56:50 +00:00
|
|
|
}
|