1 /* 2 3 Copyright (c) 2008-2018, Arvid Norberg 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 10 * Redistributions of source code must retain the above copyright 11 notice, this list of conditions and the following disclaimer. 12 * Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions and the following disclaimer in 14 the documentation and/or other materials provided with the distribution. 15 * Neither the name of the author nor the names of its 16 contributors may be used to endorse or promote products derived 17 from this software without specific prior written permission. 18 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 POSSIBILITY OF SUCH DAMAGE. 30 31 */ 32 33 #include "libtorrent/create_torrent.hpp" 34 #include "libtorrent/utf8.hpp" 35 #include "libtorrent/disk_io_thread.hpp" 36 #include "libtorrent/aux_/merkle.hpp" // for merkle_*() 37 #include "libtorrent/torrent_info.hpp" 38 #include "libtorrent/announce_entry.hpp" 39 #include "libtorrent/performance_counters.hpp" // for counters 40 #include "libtorrent/alert_manager.hpp" 41 #include "libtorrent/aux_/path.hpp" 42 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 46 #include <functional> 47 #include <memory> 48 49 #if TORRENT_ABI_VERSION == 1 && defined TORRENT_WINDOWS 50 #include "libtorrent/aux_/escape_string.hpp" 51 #endif 52 53 using namespace std::placeholders; 54 55 namespace libtorrent { 56 57 constexpr create_flags_t create_torrent::optimize_alignment; 58 #if TORRENT_ABI_VERSION == 1 59 constexpr create_flags_t create_torrent::optimize; 60 #endif 61 constexpr create_flags_t create_torrent::merkle; 62 constexpr create_flags_t create_torrent::modification_time; 63 constexpr create_flags_t create_torrent::symlinks; 64 constexpr create_flags_t create_torrent::mutable_torrent_support; 65 66 namespace { 67 default_pred(std::string const &)68 bool default_pred(std::string const&) { return true; } 69 ignore_subdir(std::string const & leaf)70 bool ignore_subdir(std::string const& leaf) 71 { return leaf == ".." || leaf == "."; } 72 73 #ifndef TORRENT_WINDOWS get_symlink_path_impl(char const * path)74 std::string get_symlink_path_impl(char const* path) 75 { 76 constexpr int MAX_SYMLINK_PATH = 200; 77 78 char buf[MAX_SYMLINK_PATH]; 79 std::string f = convert_to_native_path_string(path); 80 int char_read = int(readlink(f.c_str(), buf, MAX_SYMLINK_PATH)); 81 if (char_read < 0) return ""; 82 if (char_read < MAX_SYMLINK_PATH) buf[char_read] = 0; 83 else buf[0] = 0; 84 return convert_from_native_path(buf); 85 } 86 #endif 87 add_files_impl(file_storage & fs,std::string const & p,std::string const & l,std::function<bool (std::string)> const & pred,create_flags_t const flags)88 void add_files_impl(file_storage& fs, std::string const& p 89 , std::string const& l, std::function<bool(std::string)> const& pred 90 , create_flags_t const flags) 91 { 92 std::string const f = combine_path(p, l); 93 if (!pred(f)) return; 94 error_code ec; 95 file_status s; 96 stat_file(f, &s, ec, (flags & create_torrent::symlinks) ? dont_follow_links : 0); 97 if (ec) return; 98 99 // recurse into directories 100 bool recurse = (s.mode & file_status::directory) != 0; 101 102 // if the file is not a link or we're following links, and it's a directory 103 // only then should we recurse 104 #ifndef TORRENT_WINDOWS 105 if ((s.mode & file_status::link) && (flags & create_torrent::symlinks)) 106 recurse = false; 107 #endif 108 109 if (recurse) 110 { 111 for (directory i(f, ec); !i.done(); i.next(ec)) 112 { 113 std::string const leaf = i.file(); 114 if (ignore_subdir(leaf)) continue; 115 add_files_impl(fs, p, combine_path(l, leaf), pred, flags); 116 } 117 } 118 else 119 { 120 // #error use the fields from s 121 file_flags_t const file_flags = aux::get_file_attributes(f); 122 123 // mask all bits to check if the file is a symlink 124 if ((file_flags & file_storage::flag_symlink) 125 && (flags & create_torrent::symlinks)) 126 { 127 std::string const sym_path = aux::get_symlink_path(f); 128 fs.add_file(l, 0, file_flags, std::time_t(s.mtime), sym_path); 129 } 130 else 131 { 132 fs.add_file(l, s.file_size, file_flags, std::time_t(s.mtime)); 133 } 134 } 135 } 136 137 struct hash_state 138 { 139 create_torrent& ct; 140 storage_holder storage; 141 disk_io_thread& iothread; 142 piece_index_t piece_counter; 143 piece_index_t completed_piece; 144 std::function<void(piece_index_t)> const& f; 145 error_code& ec; 146 }; 147 on_hash(piece_index_t const piece,sha1_hash const & piece_hash,storage_error const & error,hash_state * st)148 void on_hash(piece_index_t const piece, sha1_hash const& piece_hash 149 , storage_error const& error, hash_state* st) 150 { 151 if (error) 152 { 153 // on error 154 st->ec = error.ec; 155 st->iothread.abort(true); 156 return; 157 } 158 st->ct.set_hash(piece, piece_hash); 159 st->f(st->completed_piece); 160 ++st->completed_piece; 161 if (st->piece_counter < st->ct.files().end_piece()) 162 { 163 st->iothread.async_hash(st->storage, st->piece_counter 164 , disk_interface::sequential_access 165 , std::bind(&on_hash, _1, _2, _3, st)); 166 ++st->piece_counter; 167 } 168 else 169 { 170 st->iothread.abort(true); 171 } 172 st->iothread.submit_jobs(); 173 } 174 175 } // anonymous namespace 176 177 namespace aux { 178 get_file_attributes(std::string const & p)179 file_flags_t get_file_attributes(std::string const& p) 180 { 181 auto const path = convert_to_native_path_string(p); 182 183 #ifdef TORRENT_WINDOWS 184 WIN32_FILE_ATTRIBUTE_DATA attr; 185 GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr); 186 if (attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES) return {}; 187 if (attr.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return file_storage::flag_hidden; 188 return {}; 189 #else 190 struct ::stat s{}; 191 if (::lstat(path.c_str(), &s) < 0) return {}; 192 file_flags_t file_attr = {}; 193 if (s.st_mode & S_IXUSR) 194 file_attr |= file_storage::flag_executable; 195 if (S_ISLNK(s.st_mode)) 196 file_attr |= file_storage::flag_symlink; 197 return file_attr; 198 #endif 199 } 200 get_symlink_path(std::string const & p)201 std::string get_symlink_path(std::string const& p) 202 { 203 #if defined TORRENT_WINDOWS 204 TORRENT_UNUSED(p); 205 return ""; 206 #else 207 return get_symlink_path_impl(p.c_str()); 208 #endif 209 } 210 211 } // anonymous aux 212 213 #if TORRENT_ABI_VERSION == 1 214 215 #if defined TORRENT_WINDOWS add_files(file_storage & fs,std::wstring const & wfile,std::function<bool (std::string)> p,create_flags_t const flags)216 void add_files(file_storage& fs, std::wstring const& wfile 217 , std::function<bool(std::string)> p, create_flags_t const flags) 218 { 219 std::string utf8 = convert_from_wstring(wfile); 220 add_files_impl(fs, parent_path(complete(utf8)) 221 , filename(utf8), p, flags); 222 } 223 add_files(file_storage & fs,std::wstring const & wfile,create_flags_t const flags)224 void add_files(file_storage& fs 225 , std::wstring const& wfile, create_flags_t const flags) 226 { 227 std::string utf8 = convert_from_wstring(wfile); 228 add_files_impl(fs, parent_path(complete(utf8)) 229 , filename(utf8), default_pred, flags); 230 } 231 set_piece_hashes(create_torrent & t,std::wstring const & p,std::function<void (int)> f,error_code & ec)232 void set_piece_hashes(create_torrent& t, std::wstring const& p 233 , std::function<void(int)> f, error_code& ec) 234 { 235 std::string utf8 = convert_from_wstring(p); 236 set_piece_hashes(t, utf8, f, ec); 237 } 238 set_piece_hashes_deprecated(create_torrent & t,std::wstring const & p,std::function<void (int)> f,error_code & ec)239 void set_piece_hashes_deprecated(create_torrent& t, std::wstring const& p 240 , std::function<void(int)> f, error_code& ec) 241 { 242 std::string utf8 = convert_from_wstring(p); 243 set_piece_hashes(t, utf8, f, ec); 244 } 245 #endif // TORRENT_WINDOWS 246 #endif // TORRENT_ABI_VERSION 247 add_files(file_storage & fs,std::string const & file,std::function<bool (std::string)> p,create_flags_t const flags)248 void add_files(file_storage& fs, std::string const& file 249 , std::function<bool(std::string)> p, create_flags_t const flags) 250 { 251 add_files_impl(fs, parent_path(complete(file)), filename(file), p, flags); 252 } 253 add_files(file_storage & fs,std::string const & file,create_flags_t const flags)254 void add_files(file_storage& fs, std::string const& file, create_flags_t const flags) 255 { 256 add_files_impl(fs, parent_path(complete(file)), filename(file) 257 , default_pred, flags); 258 } 259 260 namespace { 261 struct disk_aborter 262 { disk_aborterlibtorrent::__anon6188a1270211::disk_aborter263 explicit disk_aborter(disk_io_thread& dio) : m_dio(dio) {} ~disk_aborterlibtorrent::__anon6188a1270211::disk_aborter264 ~disk_aborter() { m_dio.abort(true); } 265 disk_aborter(disk_aborter const&) = delete; 266 disk_aborter& operator=(disk_aborter const&) = delete; 267 private: 268 disk_io_thread& m_dio; 269 }; 270 } 271 set_piece_hashes(create_torrent & t,std::string const & p,std::function<void (piece_index_t)> const & f,error_code & ec)272 void set_piece_hashes(create_torrent& t, std::string const& p 273 , std::function<void(piece_index_t)> const& f, error_code& ec) 274 { 275 // optimized path 276 #ifdef TORRENT_BUILD_SIMULATOR 277 sim::default_config conf; 278 sim::simulation sim{conf}; 279 io_service ios{sim}; 280 #else 281 io_service ios; 282 #endif 283 284 #if TORRENT_USE_UNC_PATHS 285 std::string const path = canonicalize_path(p); 286 #else 287 std::string const& path = p; 288 #endif 289 290 if (t.files().num_files() == 0) 291 { 292 ec = errors::no_files_in_torrent; 293 return; 294 } 295 296 if (t.files().total_size() == 0) 297 { 298 ec = errors::torrent_invalid_length; 299 return; 300 } 301 302 counters cnt; 303 aux::session_settings sett; 304 305 sett.set_int(settings_pack::cache_size, 0); 306 int const num_threads = disk_io_thread::hasher_thread_divisor - 1; 307 int const jobs_per_thread = 4; 308 sett.set_int(settings_pack::aio_threads, num_threads); 309 310 disk_io_thread disk_thread(ios, sett, cnt); 311 disk_aborter da(disk_thread); 312 313 aux::vector<download_priority_t, file_index_t> priorities; 314 sha1_hash info_hash; 315 storage_params params{ 316 t.files(), 317 nullptr, 318 path, 319 storage_mode_t::storage_mode_sparse, 320 priorities, 321 info_hash 322 }; 323 324 storage_holder storage = disk_thread.new_torrent(default_storage_constructor 325 , params, std::shared_ptr<void>()); 326 327 int const piece_read_ahead = std::max(num_threads * jobs_per_thread 328 , default_block_size / t.piece_length()); 329 330 hash_state st = { t, std::move(storage), disk_thread, piece_index_t(0), piece_index_t(0), f, ec }; 331 for (piece_index_t i(0); i < piece_index_t(piece_read_ahead); ++i) 332 { 333 disk_thread.async_hash(st.storage, i, disk_interface::sequential_access 334 , std::bind(&on_hash, _1, _2, _3, &st)); 335 ++st.piece_counter; 336 if (st.piece_counter >= t.files().end_piece()) break; 337 } 338 disk_thread.submit_jobs(); 339 340 #ifdef TORRENT_BUILD_SIMULATOR 341 sim.run(); 342 #else 343 ios.run(ec); 344 #endif 345 } 346 347 create_torrent::~create_torrent() = default; 348 create_torrent(file_storage & fs,int piece_size,int pad_file_limit,create_flags_t const flags,int alignment)349 create_torrent::create_torrent(file_storage& fs, int piece_size 350 , int pad_file_limit, create_flags_t const flags, int alignment) 351 : m_files(fs) 352 , m_creation_date(::time(nullptr)) 353 , m_multifile(fs.num_files() > 1) 354 , m_private(false) 355 , m_merkle_torrent(bool(flags & create_torrent::merkle)) 356 , m_include_mtime(bool(flags & create_torrent::modification_time)) 357 , m_include_symlinks(bool(flags & create_torrent::symlinks)) 358 { 359 // return instead of crash in release mode 360 if (fs.num_files() == 0 || fs.total_size() == 0) return; 361 362 if (!m_multifile && has_parent_path(m_files.file_path(file_index_t(0)))) 363 m_multifile = true; 364 365 // a piece_size of 0 means automatic 366 if (piece_size == 0 && !m_merkle_torrent) 367 { 368 // size_table is computed from the following: 369 // target_list_size = sqrt(total_size) * 2; 370 // target_piece_size = total_size / (target_list_size / hash_size); 371 // Given hash_size = 20 bytes, target_piece_size = (16*1024 * pow(2, i)) 372 // we can determine size_table = (total_size = pow(2 * target_piece_size / hash_size, 2)) 373 std::array<std::int64_t, 10> const size_table{{ 374 2684355LL // -> 16kiB 375 , 10737418LL // -> 32 kiB 376 , 42949673LL // -> 64 kiB 377 , 171798692LL // -> 128 kiB 378 , 687194767LL // -> 256 kiB 379 , 2748779069LL // -> 512 kiB 380 , 10995116278LL // -> 1 MiB 381 , 43980465111LL // -> 2 MiB 382 , 175921860444LL // -> 4 MiB 383 , 703687441777LL}}; // -> 8 MiB 384 385 int i = 0; 386 for (auto const s : size_table) 387 { 388 if (s >= fs.total_size()) break; 389 ++i; 390 } 391 piece_size = default_block_size << i; 392 } 393 else if (piece_size == 0 && m_merkle_torrent) 394 { 395 piece_size = 64*1024; 396 } 397 398 // to support mutable torrents, alignment always has to be the piece size, 399 // because piece hashes are compared to determine whether files are 400 // identical 401 if (flags & mutable_torrent_support) 402 alignment = piece_size; 403 404 // make sure the size is an even power of 2 405 // i.e. only a single bit is set 406 TORRENT_ASSERT((piece_size & (piece_size - 1)) == 0); 407 408 m_files.set_piece_length(piece_size); 409 if (flags & (optimize_alignment | mutable_torrent_support)) 410 m_files.optimize(pad_file_limit, alignment, bool(flags & mutable_torrent_support)); 411 412 m_files.set_num_pieces(static_cast<int>( 413 (m_files.total_size() + m_files.piece_length() - 1) / m_files.piece_length())); 414 m_piece_hash.resize(m_files.num_pieces()); 415 } 416 create_torrent(torrent_info const & ti)417 create_torrent::create_torrent(torrent_info const& ti) 418 : m_files(const_cast<file_storage&>(ti.files())) 419 , m_creation_date(::time(nullptr)) 420 , m_multifile(ti.num_files() > 1) 421 , m_private(ti.priv()) 422 , m_merkle_torrent(ti.is_merkle_torrent()) 423 , m_include_mtime(false) 424 , m_include_symlinks(false) 425 { 426 TORRENT_ASSERT(ti.is_valid()); 427 TORRENT_ASSERT(ti.num_pieces() > 0); 428 TORRENT_ASSERT(ti.num_files() > 0); 429 TORRENT_ASSERT(ti.total_size() > 0); 430 431 if (!ti.is_valid()) return; 432 if (ti.creation_date() > 0) m_creation_date = ti.creation_date(); 433 434 if (!ti.creator().empty()) set_creator(ti.creator().c_str()); 435 if (!ti.comment().empty()) set_comment(ti.comment().c_str()); 436 437 for (auto const& n : ti.nodes()) 438 add_node(n); 439 440 for (auto const& t : ti.trackers()) 441 add_tracker(t.url, t.tier); 442 443 for (auto const& s : ti.web_seeds()) 444 { 445 if (s.type == web_seed_entry::url_seed) 446 add_url_seed(s.url); 447 else if (s.type == web_seed_entry::http_seed) 448 add_http_seed(s.url); 449 } 450 451 m_piece_hash.resize(m_files.num_pieces()); 452 for (auto const i : m_files.piece_range()) 453 set_hash(i, ti.hash_for_piece(i)); 454 455 boost::shared_array<char> const info = ti.metadata(); 456 int const size = ti.metadata_size(); 457 m_info_dict.preformatted().assign(&info[0], &info[0] + size); 458 } 459 generate() const460 entry create_torrent::generate() const 461 { 462 entry dict; 463 464 if (m_files.num_files() == 0 || m_files.total_size() == 0) 465 return dict; 466 467 TORRENT_ASSERT(m_files.piece_length() > 0); 468 469 if (!m_urls.empty()) dict["announce"] = m_urls.front().first; 470 471 if (!m_nodes.empty()) 472 { 473 entry& nodes = dict["nodes"]; 474 entry::list_type& nodes_list = nodes.list(); 475 for (auto const& n : m_nodes) 476 { 477 entry::list_type node; 478 node.emplace_back(n.first); 479 node.emplace_back(n.second); 480 nodes_list.emplace_back(node); 481 } 482 } 483 484 if (m_urls.size() > 1) 485 { 486 entry trackers(entry::list_t); 487 entry tier(entry::list_t); 488 int current_tier = m_urls.front().second; 489 for (auto const& url : m_urls) 490 { 491 if (url.second != current_tier) 492 { 493 current_tier = url.second; 494 trackers.list().push_back(tier); 495 tier.list().clear(); 496 } 497 tier.list().emplace_back(url.first); 498 } 499 trackers.list().push_back(tier); 500 dict["announce-list"] = trackers; 501 } 502 503 if (!m_comment.empty()) 504 dict["comment"] = m_comment; 505 506 dict["creation date"] = m_creation_date; 507 508 if (!m_created_by.empty()) 509 dict["created by"] = m_created_by; 510 511 if (!m_url_seeds.empty()) 512 { 513 if (m_url_seeds.size() == 1) 514 { 515 dict["url-list"] = m_url_seeds.front(); 516 } 517 else 518 { 519 entry& list = dict["url-list"]; 520 for (auto const& url : m_url_seeds) 521 { 522 list.list().emplace_back(url); 523 } 524 } 525 } 526 527 if (!m_http_seeds.empty()) 528 { 529 if (m_http_seeds.size() == 1) 530 { 531 dict["httpseeds"] = m_http_seeds.front(); 532 } 533 else 534 { 535 entry& list = dict["httpseeds"]; 536 for (auto const& url : m_http_seeds) 537 { 538 list.list().emplace_back(url); 539 } 540 } 541 } 542 543 entry& info = dict["info"]; 544 if (m_info_dict.type() == entry::dictionary_t 545 || m_info_dict.type() == entry::preformatted_t) 546 { 547 info = m_info_dict; 548 return dict; 549 } 550 551 if (!m_collections.empty()) 552 { 553 entry& list = info["collections"]; 554 for (auto const& c : m_collections) 555 { 556 list.list().emplace_back(c); 557 } 558 } 559 560 if (!m_similar.empty()) 561 { 562 entry& list = info["similar"]; 563 for (auto const& ih : m_similar) 564 { 565 list.list().emplace_back(ih.to_string()); 566 } 567 } 568 569 info["name"] = m_files.name(); 570 571 if (!m_root_cert.empty()) 572 info["ssl-cert"] = m_root_cert; 573 574 if (m_private) info["private"] = 1; 575 576 if (!m_multifile) 577 { 578 file_index_t const first(0); 579 if (m_include_mtime) info["mtime"] = m_files.mtime(first); 580 info["length"] = m_files.file_size(first); 581 file_flags_t const flags = m_files.file_flags(first); 582 if (flags & (file_storage::flag_pad_file 583 | file_storage::flag_hidden 584 | file_storage::flag_executable 585 | file_storage::flag_symlink)) 586 { 587 std::string& attr = info["attr"].string(); 588 if (flags & file_storage::flag_pad_file) attr += 'p'; 589 if (flags & file_storage::flag_hidden) attr += 'h'; 590 if (flags & file_storage::flag_executable) attr += 'x'; 591 if (m_include_symlinks && (flags & file_storage::flag_symlink)) attr += 'l'; 592 } 593 if (m_include_symlinks 594 && (flags & file_storage::flag_symlink)) 595 { 596 entry& sympath_e = info["symlink path"]; 597 598 std::string const link = lexically_relative("", m_files.internal_symlink(first)); 599 for (auto elems = lsplit_path(link); !elems.first.empty(); 600 elems = lsplit_path(elems.second)) 601 sympath_e.list().emplace_back(elems.first); 602 } 603 if (!m_filehashes.empty()) 604 { 605 info["sha1"] = m_filehashes[first].to_string(); 606 } 607 } 608 else 609 { 610 if (!info.find_key("files")) 611 { 612 entry& files = info["files"]; 613 614 for (auto const i : m_files.file_range()) 615 { 616 files.list().emplace_back(); 617 entry& file_e = files.list().back(); 618 if (m_include_mtime && m_files.mtime(i)) file_e["mtime"] = m_files.mtime(i); 619 file_e["length"] = m_files.file_size(i); 620 621 TORRENT_ASSERT(has_parent_path(m_files.file_path(i))); 622 623 { 624 entry& path_e = file_e["path"]; 625 626 std::string const p = m_files.file_path(i); 627 // deliberately skip the first path element, since that's the 628 // "name" of the torrent already 629 string_view path = lsplit_path(p).second; 630 for (auto elems = lsplit_path(path); !elems.first.empty(); elems = lsplit_path(elems.second)) 631 path_e.list().emplace_back(elems.first); 632 } 633 634 file_flags_t const flags = m_files.file_flags(i); 635 if (flags) 636 { 637 std::string& attr = file_e["attr"].string(); 638 if (flags & file_storage::flag_pad_file) attr += 'p'; 639 if (flags & file_storage::flag_hidden) attr += 'h'; 640 if (flags & file_storage::flag_executable) attr += 'x'; 641 if (m_include_symlinks && (flags & file_storage::flag_symlink)) attr += 'l'; 642 } 643 644 if (m_include_symlinks 645 && (flags & file_storage::flag_symlink)) 646 { 647 entry& sympath_e = file_e["symlink path"]; 648 649 std::string const link = lexically_relative("", m_files.internal_symlink(i)); 650 for (auto elems = lsplit_path(link); !elems.first.empty(); 651 elems = lsplit_path(elems.second)) 652 sympath_e.list().emplace_back(elems.first); 653 } 654 if (!m_filehashes.empty() && m_filehashes[i] != sha1_hash()) 655 { 656 file_e["sha1"] = m_filehashes[i].to_string(); 657 } 658 } 659 } 660 } 661 662 info["piece length"] = m_files.piece_length(); 663 if (m_merkle_torrent) 664 { 665 int const num_leafs = merkle_num_leafs(m_files.num_pieces()); 666 int const num_nodes = merkle_num_nodes(num_leafs); 667 int const first_leaf = num_nodes - num_leafs; 668 m_merkle_tree.resize(num_nodes); 669 auto const num_pieces = int(m_piece_hash.size()); 670 for (int i = 0; i < num_pieces; ++i) 671 m_merkle_tree[first_leaf + i] = m_piece_hash[piece_index_t(i)]; 672 for (int i = num_pieces; i < num_leafs; ++i) 673 m_merkle_tree[first_leaf + i].clear(); 674 675 // now that we have initialized all leaves, build 676 // each level bottom-up 677 int level_start = first_leaf; 678 int level_size = num_leafs; 679 while (level_start > 0) 680 { 681 int parent = merkle_get_parent(level_start); 682 for (int i = level_start; i < level_start + level_size; i += 2, ++parent) 683 { 684 hasher h; 685 h.update(m_merkle_tree[i]); 686 h.update(m_merkle_tree[i + 1]); 687 m_merkle_tree[parent] = h.final(); 688 } 689 level_start = merkle_get_parent(level_start); 690 level_size /= 2; 691 } 692 TORRENT_ASSERT(level_size == 1); 693 info["root hash"] = m_merkle_tree[0]; 694 } 695 else 696 { 697 std::string& p = info["pieces"].string(); 698 699 for (sha1_hash const& h : m_piece_hash) 700 p.append(h.data(), h.size()); 701 } 702 703 return dict; 704 } 705 add_tracker(string_view url,int const tier)706 void create_torrent::add_tracker(string_view url, int const tier) 707 { 708 if (url.empty()) return; 709 using announce_entry = std::pair<std::string, int>; 710 auto const i = std::find_if(m_urls.begin(), m_urls.end() 711 , [&url](announce_entry const& ae) { return ae.first == url; }); 712 if (i != m_urls.end()) return; 713 m_urls.emplace_back(url.to_string(), tier); 714 715 std::sort(m_urls.begin(), m_urls.end() 716 , [](announce_entry const& lhs, announce_entry const& rhs) 717 { return lhs.second < rhs.second; }); 718 } 719 set_root_cert(string_view cert)720 void create_torrent::set_root_cert(string_view cert) 721 { 722 m_root_cert.assign(cert.data(), cert.size()); 723 } 724 add_similar_torrent(sha1_hash ih)725 void create_torrent::add_similar_torrent(sha1_hash ih) 726 { 727 m_similar.emplace_back(ih); 728 } 729 add_collection(string_view c)730 void create_torrent::add_collection(string_view c) 731 { 732 m_collections.emplace_back(c); 733 } 734 set_hash(piece_index_t index,sha1_hash const & h)735 void create_torrent::set_hash(piece_index_t index, sha1_hash const& h) 736 { 737 TORRENT_ASSERT(index >= piece_index_t(0)); 738 TORRENT_ASSERT(index < m_piece_hash.end_index()); 739 m_piece_hash[index] = h; 740 } 741 set_file_hash(file_index_t index,sha1_hash const & h)742 void create_torrent::set_file_hash(file_index_t index, sha1_hash const& h) 743 { 744 TORRENT_ASSERT(index >= file_index_t(0)); 745 TORRENT_ASSERT(index < m_files.end_file()); 746 if (m_filehashes.empty()) m_filehashes.resize(m_files.num_files()); 747 m_filehashes[index] = h; 748 } 749 add_node(std::pair<std::string,int> node)750 void create_torrent::add_node(std::pair<std::string, int> node) 751 { 752 m_nodes.emplace_back(std::move(node)); 753 } 754 add_url_seed(string_view url)755 void create_torrent::add_url_seed(string_view url) 756 { 757 m_url_seeds.emplace_back(url); 758 } 759 add_http_seed(string_view url)760 void create_torrent::add_http_seed(string_view url) 761 { 762 m_http_seeds.emplace_back(url); 763 } 764 set_comment(char const * str)765 void create_torrent::set_comment(char const* str) 766 { 767 if (str == nullptr) m_comment.clear(); 768 else m_comment = str; 769 } 770 set_creator(char const * str)771 void create_torrent::set_creator(char const* str) 772 { 773 if (str == nullptr) m_created_by.clear(); 774 else m_created_by = str; 775 } 776 } 777