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