2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-18 14:14:12 +00:00
|
|
|
#include <algorithm>
|
2022-08-17 16:08:36 +00:00
|
|
|
#include <cerrno> // for ENOENT
|
2023-06-22 16:50:57 +00:00
|
|
|
#include <cmath>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <ctime> // time()
|
2023-09-30 12:31:43 +00:00
|
|
|
#include <iterator>
|
2022-08-02 19:46:08 +00:00
|
|
|
#include <set>
|
|
|
|
#include <string>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <string_view>
|
2022-08-17 16:08:36 +00:00
|
|
|
#include <utility>
|
2022-05-27 01:29:10 +00:00
|
|
|
#include <vector>
|
2007-07-29 18:11:21 +00:00
|
|
|
|
2023-04-16 20:34:19 +00:00
|
|
|
#include <fmt/core.h>
|
2022-03-14 04:43:35 +00:00
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/block-info.h" // tr_block_info
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/crypto-utils.h"
|
|
|
|
#include "libtransmission/error.h"
|
|
|
|
#include "libtransmission/file.h"
|
|
|
|
#include "libtransmission/log.h"
|
|
|
|
#include "libtransmission/makemeta.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/quark.h" // TR_KEY_length, TR_KEY_a...
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/session.h" // TR_NAME
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/torrent-files.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/tr-assert.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/tr-strbuf.h" // tr_pathbuf
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/utils.h" // for _()
|
|
|
|
#include "libtransmission/variant.h"
|
|
|
|
#include "libtransmission/version.h"
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2021-12-16 02:09:46 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
namespace
|
|
|
|
{
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
namespace find_files_helpers
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
struct TorrentFile
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
TorrentFile(std::string_view subpath, size_t size)
|
|
|
|
: subpath_{ subpath }
|
|
|
|
, lowercase_{ tr_strlower(subpath) }
|
|
|
|
, size_{ size }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] auto operator<(TorrentFile const& that) const noexcept
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
return lowercase_ < that.lowercase_;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
std::string subpath_;
|
|
|
|
std::string lowercase_;
|
|
|
|
uint64_t size_ = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
void walkTree(std::string_view const top, std::string_view const subpath, std::set<TorrentFile>& files)
|
|
|
|
{
|
|
|
|
TR_ASSERT(!std::empty(top));
|
|
|
|
TR_ASSERT(!std::empty(subpath));
|
|
|
|
|
|
|
|
if (std::empty(top) || std::empty(subpath))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-08-11 18:11:55 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
auto path = tr_pathbuf{ top, '/', subpath };
|
|
|
|
tr_sys_path_native_separators(std::data(path));
|
2023-11-04 16:39:41 +00:00
|
|
|
auto error = tr_error{};
|
2022-08-02 19:46:08 +00:00
|
|
|
auto const info = tr_sys_path_get_info(path, 0, &error);
|
2023-11-04 16:39:41 +00:00
|
|
|
if (error)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddWarn(fmt::format(
|
2022-03-16 00:51:36 +00:00
|
|
|
_("Skipping '{path}': {error} ({error_code})"),
|
2022-08-02 19:46:08 +00:00
|
|
|
fmt::arg("path", path),
|
2023-11-04 16:39:41 +00:00
|
|
|
fmt::arg("error", error.message()),
|
|
|
|
fmt::arg("error_code", error.code())));
|
2022-08-13 17:11:07 +00:00
|
|
|
}
|
|
|
|
if (!info)
|
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
return;
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 17:41:04 +00:00
|
|
|
switch (info->type)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2022-08-02 17:41:04 +00:00
|
|
|
case TR_SYS_PATH_IS_DIRECTORY:
|
2023-04-18 21:23:20 +00:00
|
|
|
for (auto const& name : tr_sys_dir_get_files(path))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-04-18 21:23:20 +00:00
|
|
|
if (!std::empty(subpath))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-04-18 21:23:20 +00:00
|
|
|
walkTree(top, tr_pathbuf{ subpath, '/', name }, files);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
walkTree(top, name, files);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-02 17:41:04 +00:00
|
|
|
break;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-08-02 17:41:04 +00:00
|
|
|
case TR_SYS_PATH_IS_FILE:
|
2022-08-02 19:46:08 +00:00
|
|
|
files.emplace(subpath, info->size);
|
2022-08-02 17:41:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
2022-08-02 19:46:08 +00:00
|
|
|
}
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
} // namespace find_files_helpers
|
|
|
|
|
|
|
|
tr_torrent_files findFiles(std::string_view const top, std::string_view const subpath)
|
|
|
|
{
|
|
|
|
using namespace find_files_helpers;
|
|
|
|
|
|
|
|
auto tmp = std::set<TorrentFile>{};
|
|
|
|
walkTree(top, subpath, tmp);
|
|
|
|
auto files = tr_torrent_files{};
|
|
|
|
for (auto const& file : tmp)
|
|
|
|
{
|
|
|
|
files.add(file.subpath_, file.size_);
|
|
|
|
}
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2022-08-03 06:15:37 +00:00
|
|
|
tr_metainfo_builder::tr_metainfo_builder(std::string_view single_file_or_parent_directory)
|
|
|
|
: top_{ single_file_or_parent_directory }
|
2022-08-02 19:46:08 +00:00
|
|
|
{
|
2022-08-03 06:15:37 +00:00
|
|
|
files_ = findFiles(tr_sys_path_dirname(top_), tr_sys_path_basename(top_));
|
2023-05-06 04:11:05 +00:00
|
|
|
block_info_ = tr_block_info{ files_.totalSize(), default_piece_size(files_.totalSize()) };
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
bool tr_metainfo_builder::set_piece_size(uint32_t piece_size) noexcept
|
2014-06-10 00:43:21 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
if (!is_legal_piece_size(piece_size))
|
2014-06-10 00:43:21 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
return false;
|
2014-06-10 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
block_info_ = tr_block_info{ files_.totalSize(), piece_size };
|
2017-04-19 12:04:45 +00:00
|
|
|
return true;
|
2013-01-17 18:55:51 +00:00
|
|
|
}
|
|
|
|
|
2023-11-04 16:39:41 +00:00
|
|
|
bool tr_metainfo_builder::blocking_make_checksums(tr_error* error)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
checksum_piece_ = 0;
|
|
|
|
cancel_ = false;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
if (total_size() == 0U)
|
2022-08-02 19:46:08 +00:00
|
|
|
{
|
2023-11-04 16:39:41 +00:00
|
|
|
if (error != nullptr)
|
|
|
|
{
|
2023-11-28 16:51:13 +00:00
|
|
|
error->set(ENOENT, "zero-length torrents are not allowed"sv);
|
2023-11-04 16:39:41 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
return false;
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
auto hashes = std::vector<std::byte>(std::size(tr_sha1_digest_t{}) * piece_count());
|
2022-08-02 19:46:08 +00:00
|
|
|
auto* walk = std::data(hashes);
|
|
|
|
auto sha = tr_sha1::create();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
auto file_index = tr_file_index_t{ 0U };
|
|
|
|
auto piece_index = tr_piece_index_t{ 0U };
|
2023-05-06 04:11:05 +00:00
|
|
|
auto total_remain = total_size();
|
2022-08-02 19:46:08 +00:00
|
|
|
auto off = uint64_t{ 0U };
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
auto buf = std::vector<char>(piece_size());
|
2013-01-25 06:01:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
auto const parent = tr_sys_path_dirname(top_);
|
|
|
|
auto fd = tr_sys_file_open(
|
|
|
|
tr_pathbuf{ parent, '/', path(file_index) },
|
|
|
|
TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL,
|
|
|
|
0,
|
|
|
|
error);
|
2017-04-19 12:04:45 +00:00
|
|
|
if (fd == TR_BAD_SYS_FILE)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
return false;
|
2008-03-02 19:42:45 +00:00
|
|
|
}
|
2013-01-25 06:01:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
while (!cancel_ && (total_remain > 0U))
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
checksum_piece_ = piece_index;
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
TR_ASSERT(piece_index < piece_count());
|
2017-06-13 02:24:09 +00:00
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
auto const piece_size = block_info_.piece_size(piece_index);
|
2022-08-02 19:46:08 +00:00
|
|
|
buf.resize(piece_size);
|
2021-12-21 22:14:15 +00:00
|
|
|
auto* bufptr = std::data(buf);
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
auto left_in_piece = piece_size;
|
|
|
|
while (left_in_piece > 0U)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
auto const n_this_pass = std::min(file_size(file_index) - off, uint64_t{ left_in_piece });
|
2022-08-02 19:46:08 +00:00
|
|
|
auto n_read = uint64_t{};
|
|
|
|
|
|
|
|
(void)tr_sys_file_read(fd, bufptr, n_this_pass, &n_read, error);
|
2017-04-19 12:04:45 +00:00
|
|
|
bufptr += n_read;
|
|
|
|
off += n_read;
|
2022-08-02 19:46:08 +00:00
|
|
|
left_in_piece -= n_read;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
if (off == file_size(file_index))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
off = 0;
|
2022-03-27 17:37:29 +00:00
|
|
|
tr_sys_file_close(fd);
|
2017-04-19 12:04:45 +00:00
|
|
|
fd = TR_BAD_SYS_FILE;
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
if (++file_index < file_count())
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
fd = tr_sys_file_open(
|
|
|
|
tr_pathbuf{ parent, '/', path(file_index) },
|
|
|
|
TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL,
|
|
|
|
0,
|
|
|
|
error);
|
2017-04-19 12:04:45 +00:00
|
|
|
if (fd == TR_BAD_SYS_FILE)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
return false;
|
2008-03-02 19:42:45 +00:00
|
|
|
}
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
TR_ASSERT(bufptr - std::data(buf) == (int)piece_size);
|
|
|
|
TR_ASSERT(left_in_piece == 0);
|
|
|
|
sha->add(std::data(buf), std::size(buf));
|
2022-08-20 20:57:32 +00:00
|
|
|
auto const digest = sha->finish();
|
2022-07-31 20:58:14 +00:00
|
|
|
walk = std::copy(std::begin(digest), std::end(digest), walk);
|
2022-08-02 19:46:08 +00:00
|
|
|
sha->clear();
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
total_remain -= piece_size;
|
|
|
|
++piece_index;
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
TR_ASSERT(cancel_ || size_t(walk - std::data(hashes)) == std::size(hashes));
|
|
|
|
TR_ASSERT(cancel_ || total_remain == 0U);
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (fd != TR_BAD_SYS_FILE)
|
|
|
|
{
|
2022-03-27 17:37:29 +00:00
|
|
|
tr_sys_file_close(fd);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
if (cancel_)
|
2016-03-13 10:41:52 +00:00
|
|
|
{
|
2023-11-04 16:39:41 +00:00
|
|
|
if (error != nullptr)
|
|
|
|
{
|
|
|
|
error->set_from_errno(ECANCELED);
|
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
return false;
|
2022-02-22 00:01:36 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
piece_hashes_ = std::move(hashes);
|
|
|
|
return true;
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
|
|
|
|
2023-11-04 16:39:41 +00:00
|
|
|
std::string tr_metainfo_builder::benc(tr_error* error) const
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2022-08-02 19:46:08 +00:00
|
|
|
TR_ASSERT_MSG(!std::empty(piece_hashes_), "did you forget to call makeChecksums() first?");
|
2013-01-25 06:01:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
auto const anonymize = this->anonymize();
|
|
|
|
auto const& comment = this->comment();
|
|
|
|
auto const& source = this->source();
|
|
|
|
auto const& webseeds = this->webseeds();
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
if (total_size() == 0)
|
2022-02-12 18:50:47 +00:00
|
|
|
{
|
2023-11-04 16:39:41 +00:00
|
|
|
if (error != nullptr)
|
|
|
|
{
|
|
|
|
error->set_from_errno(ENOENT);
|
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
return {};
|
2022-02-12 18:50:47 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
auto top = tr_variant::Map{ 8U };
|
2008-04-18 16:23:59 +00:00
|
|
|
|
2023-03-01 19:10:20 +00:00
|
|
|
// add the announce URLs
|
2023-09-30 12:31:43 +00:00
|
|
|
announce_list().add_to_map(top);
|
2022-02-12 18:50:47 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
// add the webseeds
|
2023-09-30 12:31:43 +00:00
|
|
|
if (auto const n_webseeds = std::size(webseeds); n_webseeds > 0U)
|
2008-03-24 15:58:06 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
auto webseeds_vec = tr_variant::Vector{};
|
|
|
|
webseeds_vec.reserve(n_webseeds);
|
|
|
|
std::copy_n(std::cbegin(webseeds), n_webseeds, std::back_inserter(webseeds_vec));
|
|
|
|
top.try_emplace(TR_KEY_url_list, std::move(webseeds_vec));
|
2008-03-24 15:58:06 +00:00
|
|
|
}
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
// add the comment
|
|
|
|
if (!std::empty(comment))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
top.try_emplace(TR_KEY_comment, comment);
|
2007-06-18 03:40:41 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
// maybe add some optional metainfo
|
|
|
|
if (!anonymize)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
top.try_emplace(TR_KEY_created_by, TR_NAME "/" LONG_VERSION_STRING);
|
|
|
|
top.try_emplace(TR_KEY_creation_date, time(nullptr));
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
top.try_emplace(TR_KEY_encoding, "UTF-8"sv);
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
auto info_dict = tr_variant::Map{ 8U };
|
2022-08-02 19:46:08 +00:00
|
|
|
auto const base = tr_sys_path_basename(top_);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
// "There is also a key `length` or a key `files`, but not both or neither.
|
|
|
|
// If length is present then the download represents a single file,
|
|
|
|
// otherwise it represents a set of files which go in a directory structure."
|
2023-06-30 14:49:58 +00:00
|
|
|
if (file_count() == 1U && !tr_strv_contains(path(0), '/'))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
info_dict.try_emplace(TR_KEY_length, file_size(0));
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2022-08-02 19:46:08 +00:00
|
|
|
else
|
2022-02-12 18:50:47 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
auto const n_files = file_count();
|
2023-09-30 12:31:43 +00:00
|
|
|
auto file_vec = tr_variant::Vector{};
|
|
|
|
file_vec.reserve(n_files);
|
2022-02-12 18:50:47 +00:00
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
for (tr_file_index_t i = 0U; i < n_files; ++i)
|
2022-08-02 19:46:08 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
auto file_map = tr_variant::Map{ 2U };
|
|
|
|
file_map.try_emplace(TR_KEY_length, file_size(i));
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
auto subpath = std::string_view{ path(i) };
|
|
|
|
if (!std::empty(base))
|
|
|
|
{
|
|
|
|
subpath.remove_prefix(std::size(base) + std::size("/"sv));
|
|
|
|
}
|
2021-11-20 21:20:45 +00:00
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
auto path_vec = tr_variant::Vector{};
|
2022-08-02 19:46:08 +00:00
|
|
|
auto token = std::string_view{};
|
2023-06-30 14:49:58 +00:00
|
|
|
while (tr_strv_sep(&subpath, &token, '/'))
|
2022-08-02 19:46:08 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
path_vec.emplace_back(token);
|
2022-08-02 19:46:08 +00:00
|
|
|
}
|
2023-09-30 12:31:43 +00:00
|
|
|
file_map.try_emplace(TR_KEY_path, std::move(path_vec));
|
|
|
|
|
|
|
|
file_vec.emplace_back(std::move(file_map));
|
2022-08-02 19:46:08 +00:00
|
|
|
}
|
2023-10-02 15:18:38 +00:00
|
|
|
|
|
|
|
info_dict.try_emplace(TR_KEY_files, std::move(file_vec));
|
2022-08-02 19:46:08 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-08-02 19:46:08 +00:00
|
|
|
if (!std::empty(base))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
info_dict.try_emplace(TR_KEY_name, base);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2022-08-02 19:46:08 +00:00
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
info_dict.try_emplace(TR_KEY_piece_length, piece_size());
|
|
|
|
info_dict.try_emplace(TR_KEY_pieces, tr_variant::make_raw(std::data(piece_hashes_), std::size(piece_hashes_)));
|
2023-02-11 02:33:24 +00:00
|
|
|
|
|
|
|
if (is_private_)
|
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
info_dict.try_emplace(TR_KEY_private, 1);
|
2023-02-11 02:33:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!std::empty(source))
|
|
|
|
{
|
2023-09-30 12:31:43 +00:00
|
|
|
info_dict.try_emplace(TR_KEY_source, source_);
|
2023-02-11 02:33:24 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 12:31:43 +00:00
|
|
|
top.try_emplace(TR_KEY_info, std::move(info_dict));
|
|
|
|
return tr_variant_serde::benc().to_string(tr_variant{ std::move(top) });
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2023-06-22 16:50:57 +00:00
|
|
|
|
2023-12-24 17:02:54 +00:00
|
|
|
bool tr_metainfo_builder::save(std::string_view filename, tr_error* error) const
|
|
|
|
{
|
|
|
|
return tr_file_save(filename, benc(error), error);
|
|
|
|
}
|
|
|
|
|
2023-06-22 16:50:57 +00:00
|
|
|
uint32_t tr_metainfo_builder::default_piece_size(uint64_t total_size) noexcept
|
|
|
|
{
|
|
|
|
// Ideally, we want approximately 2^10 = 1024 pieces, give or take a few hundred pieces.
|
|
|
|
// So we subtract 10 from the log2 of total size.
|
|
|
|
// The ideal number of pieces is up for debate.
|
2023-11-28 16:51:13 +00:00
|
|
|
auto exp = (total_size > 0U ? std::log2(total_size) : 0) - 10;
|
2023-06-22 16:50:57 +00:00
|
|
|
|
|
|
|
// We want a piece size between 16KiB (2^14 bytes) and 16MiB (2^24 bytes) for maximum compatibility
|
|
|
|
exp = std::clamp(exp, 14., 24.);
|
|
|
|
|
|
|
|
return uint32_t{ 1U } << std::lround(exp);
|
|
|
|
}
|