1 /*
2 
3 Copyright (c) 2008, 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/session.hpp"
34 #include "libtorrent/session_settings.hpp"
35 #include "libtorrent/time.hpp"
36 #include "libtorrent/hasher.hpp"
37 #include "libtorrent/create_torrent.hpp"
38 #include "libtorrent/alert_types.hpp"
39 #include "libtorrent/torrent.hpp"
40 #include "libtorrent/peer_info.hpp"
41 #include "libtorrent/extensions.hpp"
42 #include "libtorrent/aux_/path.hpp" // for combine_path, current_working_directory
43 #include "libtorrent/magnet_uri.hpp"
44 #include "libtorrent/random.hpp"
45 #include "settings.hpp"
46 #include <tuple>
47 #include <iostream>
48 
49 #include "test.hpp"
50 #include "test_utils.hpp"
51 #include "setup_transfer.hpp"
52 
53 using namespace lt;
54 
55 namespace {
56 
wait_priority(torrent_handle const & h,aux::vector<download_priority_t,file_index_t> const & prio)57 bool wait_priority(torrent_handle const& h, aux::vector<download_priority_t, file_index_t> const& prio)
58 {
59 	for (int i = 0; i < 10; ++i)
60 	{
61 		if (h.get_file_priorities() == prio) return true;
62 
63 #ifdef NDEBUG
64 		std::this_thread::sleep_for(lt::milliseconds(100));
65 #else
66 		std::this_thread::sleep_for(lt::milliseconds(300));
67 #endif
68 	}
69 
70 	return h.get_file_priorities() == prio;
71 }
72 
prioritize_files(torrent_handle const & h,aux::vector<download_priority_t,file_index_t> const & prio)73 bool prioritize_files(torrent_handle const& h, aux::vector<download_priority_t, file_index_t> const& prio)
74 {
75 	h.prioritize_files(prio);
76 	return wait_priority(h, prio);
77 }
78 
test_running_torrent(std::shared_ptr<torrent_info> info,std::int64_t file_size)79 void test_running_torrent(std::shared_ptr<torrent_info> info, std::int64_t file_size)
80 {
81 	settings_pack pack = settings();
82 	pack.set_int(settings_pack::alert_mask, alert_category::piece_progress | alert_category::storage);
83 	pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
84 	pack.set_int(settings_pack::max_retry_port_bind, 10);
85 	lt::session ses(pack);
86 
87 	aux::vector<download_priority_t, file_index_t> zeroes;
88 	zeroes.resize(1000, 0_pri);
89 	add_torrent_params p;
90 	p.flags &= ~torrent_flags::paused;
91 	p.flags &= ~torrent_flags::auto_managed;
92 	p.ti = info;
93 	p.save_path = ".";
94 
95 	// make sure we correctly handle the case where we pass in
96 	// more values than there are files
97 	p.file_priorities = zeroes;
98 
99 	error_code ec;
100 	torrent_handle h = ses.add_torrent(p, ec);
101 	if (ec)
102 	{
103 		std::printf("add_torrent: %s\n", ec.message().c_str());
104 		return;
105 	}
106 
107 	aux::vector<download_priority_t, file_index_t> ones(std::size_t(info->num_files()), 1_pri);
108 	TEST_CHECK(prioritize_files(h, ones));
109 
110 	torrent_status st = h.status();
111 
112 	TEST_EQUAL(st.total_wanted, file_size); // we want the single file
113 	TEST_EQUAL(st.total_wanted_done, 0);
114 
115 	aux::vector<download_priority_t, file_index_t> prio(std::size_t(info->num_files()), 1_pri);
116 	prio[file_index_t(0)] = 0_pri;
117 	TEST_CHECK(prioritize_files(h, prio));
118 	st = h.status();
119 
120 	st = h.status();
121 	TEST_EQUAL(st.total_wanted, 0); // we don't want anything
122 	TEST_EQUAL(st.total_wanted_done, 0);
123 	TEST_EQUAL(int(h.get_file_priorities().size()), info->num_files());
124 
125 	if (info->num_files() > 1)
126 	{
127 		prio[file_index_t{1}] = 0_pri;
128 		TEST_CHECK(prioritize_files(h, prio));
129 
130 		st = h.status();
131 		TEST_EQUAL(st.total_wanted, file_size);
132 		TEST_EQUAL(st.total_wanted_done, 0);
133 	}
134 
135 	if (info->num_pieces() > 0)
136 	{
137 		h.piece_priority(piece_index_t(0), 1_pri);
138 		st = h.status();
139 		TEST_CHECK(st.pieces.size() > 0 && st.pieces[piece_index_t(0)] == false);
140 		std::vector<char> piece(std::size_t(info->piece_length()));
141 		for (int i = 0; i < int(piece.size()); ++i)
142 			piece[std::size_t(i)] = (i % 26) + 'A';
143 		h.add_piece(piece_index_t(0), &piece[0], torrent_handle::overwrite_existing);
144 
145 		// wait until the piece is done writing and hashing
146 		wait_for_alert(ses, piece_finished_alert::alert_type, "piece_finished_alert");
147 		st = h.status();
148 		TEST_CHECK(st.pieces.size() > 0);
149 
150 		std::cout << "reading piece 0" << std::endl;
151 		h.read_piece(piece_index_t(0));
152 		alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece");
153 		TEST_CHECK(a);
154 		read_piece_alert const* rpa = alert_cast<read_piece_alert>(a);
155 		TEST_CHECK(rpa);
156 		if (rpa)
157 		{
158 			std::cout << "SUCCEEDED!" << std::endl;
159 			TEST_CHECK(std::memcmp(&piece[0], rpa->buffer.get()
160 				, std::size_t(info->piece_size(piece_index_t(0)))) == 0);
161 			TEST_CHECK(rpa->size == info->piece_size(piece_index_t(0)));
162 			TEST_CHECK(rpa->piece == piece_index_t(0));
163 			TEST_CHECK(hasher(piece).final() == info->hash_for_piece(piece_index_t(0)));
164 		}
165 	}
166 
167 	TEST_CHECK(h.get_file_priorities() == prio);
168 }
169 
test_large_piece_size(int const size)170 void test_large_piece_size(int const size)
171 {
172 	entry torrent;
173 	entry& info = torrent["info"];
174 	info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
175 	info["name"] = "test";
176 	info["piece length"] = size;
177 	info["length"] = size;
178 
179 	std::vector<char> buf;
180 	bencode(std::back_inserter(buf), torrent);
181 	add_torrent_params atp;
182 	atp.ti = std::make_shared<torrent_info>(buf, from_span);
183 	atp.save_path = ".";
184 
185 	lt::session ses;
186 	auto h = ses.add_torrent(atp);
187 	TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size));
188 	h.clear_error();
189 	TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size));
190 }
191 
192 } // anonymous namespace
193 
TORRENT_TEST(long_names)194 TORRENT_TEST(long_names)
195 {
196 	entry torrent;
197 	entry& info = torrent["info"];
198 	info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
199 	info["name"] = "slightly shorter name, it's kind of sad that people started "
200 		"the trend of incorrectly encoding the regular name field and then adding "
201 		"another one with correct encoding";
202 	info["name.utf-8"] = "this is a long ass name in order to try to make "
203 		"make_magnet_uri overflow and hopefully crash. Although, by the time you "
204 		"read this that particular bug should have been fixed";
205 	info["piece length"] = 16 * 1024;
206 	info["length"] = 3245;
207 
208 	std::vector<char> buf;
209 	bencode(std::back_inserter(buf), torrent);
210 	auto ti = std::make_shared<torrent_info>(buf, from_span);
211 }
212 
TORRENT_TEST(large_piece_size)213 TORRENT_TEST(large_piece_size)
214 {
215 	test_large_piece_size(32768 * 16 * 1024);
216 	test_large_piece_size(65536 * 16 * 1024);
217 	test_large_piece_size(65537 * 16 * 1024);
218 }
219 
TORRENT_TEST(total_wanted)220 TORRENT_TEST(total_wanted)
221 {
222 	file_storage fs;
223 
224 	fs.add_file("test_torrent_dir4/tmp1", 1024);
225 	fs.add_file("test_torrent_dir4/tmp2", 1024);
226 	fs.add_file("test_torrent_dir4/tmp3", 1024);
227 	fs.add_file("test_torrent_dir4/tmp4", 1024);
228 
229 	lt::create_torrent t(fs, 1024);
230 	std::vector<char> tmp;
231 	bencode(std::back_inserter(tmp), t.generate());
232 	auto info = std::make_shared<torrent_info>(tmp, from_span);
233 
234 	settings_pack pack = settings();
235 	pack.set_int(settings_pack::alert_mask, alert_category::storage);
236 	pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
237 	pack.set_int(settings_pack::max_retry_port_bind, 10);
238 	lt::session ses(pack);
239 
240 	add_torrent_params p;
241 	p.ti = info;
242 	p.save_path = ".";
243 
244 	// we just want 1 out of 4 files, 1024 out of 4096 bytes
245 	p.file_priorities.resize(4, 0_pri);
246 	p.file_priorities[1] = 1_pri;
247 
248 	torrent_handle h = ses.add_torrent(std::move(p));
249 
250 	torrent_status st = h.status();
251 	TEST_EQUAL(st.total_wanted, 1024);
252 	TEST_EQUAL(st.total_wanted_done, 0);
253 
254 	// make sure that selecting and unseleting a file quickly still end up with
255 	// the last set priority
256 	h.file_priority(file_index_t{1}, default_priority);
257 	h.file_priority(file_index_t{1}, dont_download);
258 	TEST_CHECK(wait_priority(h, aux::vector<download_priority_t, file_index_t>(static_cast<std::size_t>(fs.num_files()))));
259 	TEST_EQUAL(h.status({}).total_wanted, 0);
260 }
261 
TORRENT_TEST(added_peers)262 TORRENT_TEST(added_peers)
263 {
264 	file_storage fs;
265 
266 	fs.add_file("test_torrent_dir4/tmp1", 1024);
267 
268 	lt::create_torrent t(fs, 1024);
269 	std::vector<char> tmp;
270 	bencode(std::back_inserter(tmp), t.generate());
271 	auto info = std::make_shared<torrent_info>(tmp, from_span);
272 
273 	settings_pack pack = settings();
274 	pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
275 	pack.set_int(settings_pack::max_retry_port_bind, 10);
276 	lt::session ses(pack);
277 
278 	add_torrent_params p = parse_magnet_uri(
279 		"magnet:?xt=urn:btih:abababababababababababababababababababab&x.pe=127.0.0.1:48081&x.pe=127.0.0.2:48082");
280 	p.ti = info;
281 	p.info_hash.clear();
282 	p.save_path = ".";
283 
284 	torrent_handle h = ses.add_torrent(std::move(p));
285 
286 	h.save_resume_data();
287 	alert const* a = wait_for_alert(ses, save_resume_data_alert::alert_type);
288 
289 	TEST_CHECK(a);
290 	save_resume_data_alert const* ra = alert_cast<save_resume_data_alert>(a);
291 	TEST_CHECK(ra);
292 	if (ra) TEST_EQUAL(ra->params.peers.size(), 2);
293 }
294 
TORRENT_TEST(mismatching_info_hash)295 TORRENT_TEST(mismatching_info_hash)
296 {
297 	file_storage fs;
298 	fs.add_file("test_torrent_dir4/tmp1", 1024);
299 	lt::create_torrent t(fs, 1024);
300 	std::vector<char> tmp;
301 	bencode(std::back_inserter(tmp), t.generate());
302 	auto info = std::make_shared<torrent_info>(tmp, from_span);
303 
304 	add_torrent_params p;
305 	p.ti = std::move(info);
306 
307 	// this info-hash is definitely different from the one in `info`, this
308 	// should trigger a failure
309 	p.info_hash = lt::sha1_hash("01010101010101010101");
310 	p.save_path = ".";
311 
312 	lt::session ses(settings());
313 	error_code ec;
314 	torrent_handle h = ses.add_torrent(std::move(p), ec);
315 	TEST_CHECK(ec == lt::errors::mismatching_info_hash);
316 	TEST_CHECK(!h.is_valid());
317 }
318 
TORRENT_TEST(exceed_file_prio)319 TORRENT_TEST(exceed_file_prio)
320 {
321 	file_storage fs;
322 	fs.add_file("test_torrent_dir4/tmp1", 1024);
323 	lt::create_torrent t(fs, 1024);
324 	std::vector<char> tmp;
325 	bencode(std::back_inserter(tmp), t.generate());
326 	auto info = std::make_shared<torrent_info>(tmp, from_span);
327 
328 	add_torrent_params p;
329 	p.ti = std::move(info);
330 
331 	p.file_priorities.resize(9999, lt::low_priority);
332 	p.save_path = ".";
333 
334 	lt::session ses(settings());
335 	error_code ec;
336 	torrent_handle h = ses.add_torrent(std::move(p));
337 	auto const prios = h.get_file_priorities();
338 	TEST_CHECK(prios.size() == 1);
339 }
340 
TORRENT_TEST(exceed_piece_prio)341 TORRENT_TEST(exceed_piece_prio)
342 {
343 	file_storage fs;
344 	fs.add_file("test_torrent_dir4/tmp1", 1024);
345 	lt::create_torrent t(fs, 1024);
346 	std::vector<char> tmp;
347 	bencode(std::back_inserter(tmp), t.generate());
348 	auto info = std::make_shared<torrent_info>(tmp, from_span);
349 	std::size_t const num_pieces = std::size_t(info->num_pieces());
350 
351 	add_torrent_params p;
352 	p.ti = std::move(info);
353 
354 	p.piece_priorities.resize(9999, lt::low_priority);
355 	p.save_path = ".";
356 
357 	lt::session ses(settings());
358 	error_code ec;
359 	torrent_handle h = ses.add_torrent(std::move(p));
360 	auto const prios = h.get_piece_priorities();
361 	TEST_CHECK(prios.size() == num_pieces);
362 }
363 
TORRENT_TEST(exceed_piece_prio_magnet)364 TORRENT_TEST(exceed_piece_prio_magnet)
365 {
366 	add_torrent_params p;
367 	p.info_hash = sha1_hash("abababababababababab");
368 	p.piece_priorities.resize(9999, lt::low_priority);
369 	p.save_path = ".";
370 
371 	lt::session ses(settings());
372 	error_code ec;
373 	torrent_handle h = ses.add_torrent(std::move(p));
374 	auto const prios = h.get_piece_priorities();
375 	TEST_CHECK(prios.empty());
376 }
377 
TORRENT_TEST(torrent)378 TORRENT_TEST(torrent)
379 {
380 /*	{
381 		remove("test_torrent_dir2/tmp1");
382 		remove("test_torrent_dir2/tmp2");
383 		remove("test_torrent_dir2/tmp3");
384 		file_storage fs;
385 		std::int64_t file_size = 256 * 1024;
386 		fs.add_file("test_torrent_dir2/tmp1", file_size);
387 		fs.add_file("test_torrent_dir2/tmp2", file_size);
388 		fs.add_file("test_torrent_dir2/tmp3", file_size);
389 		lt::create_torrent t(fs, 128 * 1024);
390 		t.add_tracker("http://non-existing.com/announce");
391 
392 		std::vector<char> piece(128 * 1024);
393 		for (int i = 0; i < int(piece.size()); ++i)
394 			piece[i] = (i % 26) + 'A';
395 
396 		// calculate the hash for all pieces
397 		sha1_hash ph = hasher(piece).final();
398 		int num = t.num_pieces();
399 		TEST_CHECK(t.num_pieces() > 0);
400 		for (int i = 0; i < num; ++i)
401 			t.set_hash(i, ph);
402 
403 		std::vector<char> tmp;
404 		std::back_insert_iterator<std::vector<char>> out(tmp);
405 		bencode(out, t.generate());
406 		error_code ec;
407 		auto info = std::make_shared<torrent_info>(tmp, std::ref(ec), from_span);
408 		TEST_CHECK(info->num_pieces() > 0);
409 
410 		test_running_torrent(info, file_size);
411 	}
412 */
413 	{
414 		file_storage fs;
415 
416 		fs.add_file("test_torrent_dir2/tmp1", 1024);
417 		lt::create_torrent t(fs, 1024, 6);
418 
419 		std::vector<char> piece(1024);
420 		for (int i = 0; i < int(piece.size()); ++i)
421 			piece[std::size_t(i)] = (i % 26) + 'A';
422 
423 		// calculate the hash for all pieces
424 		sha1_hash const ph = hasher(piece).final();
425 		TEST_CHECK(t.num_pieces() > 0);
426 		for (auto const i : fs.piece_range())
427 			t.set_hash(i, ph);
428 
429 		std::vector<char> tmp;
430 		std::back_insert_iterator<std::vector<char>> out(tmp);
431 		bencode(out, t.generate());
432 		auto info = std::make_shared<torrent_info>(tmp, from_span);
433 		test_running_torrent(info, 1024);
434 	}
435 }
436 
437 #ifndef TORRENT_DISABLE_EXTENSIONS
438 struct test_plugin : lt::torrent_plugin {};
439 
440 struct plugin_creator
441 {
plugin_creatorplugin_creator442 	explicit plugin_creator(int& c) : m_called(c) {}
443 
444 	std::shared_ptr<lt::torrent_plugin>
operator ()plugin_creator445 	operator()(torrent_handle const&, void*)
446 	{
447 		++m_called;
448 		return std::make_shared<test_plugin>();
449 	}
450 
451 	int& m_called;
452 };
453 
TORRENT_TEST(duplicate_is_not_error)454 TORRENT_TEST(duplicate_is_not_error)
455 {
456 	file_storage fs;
457 
458 	fs.add_file("test_torrent_dir2/tmp1", 1024);
459 	lt::create_torrent t(fs, 128 * 1024, 6);
460 
461 	std::vector<char> piece(128 * 1024);
462 	for (int i = 0; i < int(piece.size()); ++i)
463 		piece[std::size_t(i)] = (i % 26) + 'A';
464 
465 	// calculate the hash for all pieces
466 	sha1_hash ph = hasher(piece).final();
467 	TEST_CHECK(t.num_pieces() > 0);
468 	for (auto const i : fs.piece_range())
469 		t.set_hash(i, ph);
470 
471 	std::vector<char> tmp;
472 	std::back_insert_iterator<std::vector<char>> out(tmp);
473 	bencode(out, t.generate());
474 
475 	int called = 0;
476 	plugin_creator creator(called);
477 
478 	add_torrent_params p;
479 	p.ti = std::make_shared<torrent_info>(tmp, from_span);
480 	p.flags &= ~torrent_flags::paused;
481 	p.flags &= ~torrent_flags::auto_managed;
482 	p.flags &= ~torrent_flags::duplicate_is_error;
483 	p.save_path = ".";
484 	p.extensions.push_back(creator);
485 
486 	lt::session ses(settings());
487 	ses.async_add_torrent(p);
488 	ses.async_add_torrent(std::move(p));
489 
490 	wait_for_downloading(ses, "ses");
491 
492 	// we should only have added the plugin once
493 	TEST_EQUAL(called, 1);
494 }
495 #endif
496 
TORRENT_TEST(torrent_total_size_zero)497 TORRENT_TEST(torrent_total_size_zero)
498 {
499 	file_storage fs;
500 
501 	fs.add_file("test_torrent_dir2/tmp1", 0);
502 	TEST_CHECK(fs.num_files() == 1);
503 	TEST_CHECK(fs.total_size() == 0);
504 
505 	error_code ec;
506 	lt::create_torrent t1(fs);
507 	set_piece_hashes(t1, ".", ec);
508 	TEST_CHECK(ec);
509 
510 	fs.add_file("test_torrent_dir2/tmp2", 0);
511 	TEST_CHECK(fs.num_files() == 2);
512 	TEST_CHECK(fs.total_size() == 0);
513 
514 	ec.clear();
515 	lt::create_torrent t2(fs);
516 	set_piece_hashes(t2, ".", ec);
517 	TEST_CHECK(ec);
518 }
519 
TORRENT_TEST(rename_file)520 TORRENT_TEST(rename_file)
521 {
522 	file_storage fs;
523 
524 	fs.add_file("test3/tmp1", 20);
525 	fs.add_file("test3/tmp2", 20);
526 	lt::create_torrent t(fs, 128 * 1024, 6);
527 
528 	std::vector<char> tmp;
529 	std::back_insert_iterator<std::vector<char>> out(tmp);
530 	bencode(out, t.generate());
531 	auto info = std::make_shared<torrent_info>(tmp, from_span);
532 
533 	TEST_EQUAL(info->files().file_path(file_index_t(0)), combine_path("test3","tmp1"));
534 
535 	// move "test3/tmp1" -> "tmp1"
536 	info->rename_file(file_index_t(0), "tmp1");
537 
538 	TEST_EQUAL(info->files().file_path(file_index_t(0)), "tmp1");
539 }
540 
541 #if TORRENT_ABI_VERSION == 1
TORRENT_TEST(async_load_deprecated)542 TORRENT_TEST(async_load_deprecated)
543 {
544 	settings_pack pack = settings();
545 	lt::session ses(pack);
546 
547 	add_torrent_params p;
548 	p.flags &= ~torrent_flags::paused;
549 	p.flags &= ~torrent_flags::auto_managed;
550 	std::string dir = parent_path(current_working_directory());
551 
552 	p.url = "file://" + combine_path(combine_path(dir, "test_torrents"), "base.torrent");
553 	p.save_path = ".";
554 	ses.async_add_torrent(std::move(p));
555 
556 	alert const* a = wait_for_alert(ses, add_torrent_alert::alert_type);
557 	TEST_CHECK(a);
558 	if (a == nullptr) return;
559 	auto const* ta = alert_cast<add_torrent_alert const>(a);
560 	TEST_CHECK(ta);
561 	if (ta == nullptr) return;
562 	TEST_CHECK(!ta->error);
563 	TEST_CHECK(ta->params.ti->name() == "temp");
564 }
565 #endif
566 
TORRENT_TEST(torrent_status)567 TORRENT_TEST(torrent_status)
568 {
569 	TEST_EQUAL(static_cast<int>(torrent_status::error_file_none), -1);
570 #if TORRENT_ABI_VERSION == 1
571 	TEST_EQUAL(static_cast<int>(torrent_status::error_file_url), -2);
572 	TEST_EQUAL(static_cast<int>(torrent_status::error_file_metadata), -4);
573 #endif
574 	TEST_EQUAL(static_cast<int>(torrent_status::error_file_ssl_ctx), -3);
575 	TEST_EQUAL(static_cast<int>(torrent_status::error_file_exception), -5);
576 }
577 
578 namespace {
579 
test_queue(add_torrent_params)580 void test_queue(add_torrent_params)
581 {
582 	lt::settings_pack pack = settings();
583 	// we're not testing the hash check, just accept the data we write
584 	pack.set_bool(settings_pack::disable_hash_checks, true);
585 	lt::session ses(pack);
586 
587 	std::vector<torrent_handle> torrents;
588 	for(int i = 0; i < 6; i++)
589 	{
590 		file_storage fs;
591 		std::stringstream file_path;
592 		file_path << "test_torrent_dir4/queue" << i;
593 		fs.add_file(file_path.str(), 1024);
594 		lt::create_torrent t(fs, 128 * 1024, 6);
595 
596 		std::vector<char> buf;
597 		bencode(std::back_inserter(buf), t.generate());
598 		auto ti = std::make_shared<torrent_info>(buf, from_span);
599 		add_torrent_params p;
600 		p.ti = ti;
601 		p.save_path = ".";
602 		torrents.push_back(ses.add_torrent(std::move(p)));
603 	}
604 
605 	print_alerts(ses, "ses");
606 
607 	std::vector<download_priority_t> pieces(
608 		std::size_t(torrents[5].torrent_file()->num_pieces()), 0_pri);
609 	torrents[5].prioritize_pieces(pieces);
610 	torrent_handle finished = torrents[5];
611 
612 	wait_for_alert(ses, torrent_finished_alert::alert_type, "ses");
613 
614 	// add_torrent should be ordered
615 	TEST_EQUAL(finished.queue_position(), no_pos);
616 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
617 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
618 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
619 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
620 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
621 
622 	// test top and bottom
623 	torrents[2].queue_position_top();
624 	torrents[1].queue_position_bottom();
625 
626 	TEST_EQUAL(finished.queue_position(), no_pos);
627 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{0});
628 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{1});
629 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{2});
630 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{3});
631 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{4});
632 
633 	// test set pos
634 	torrents[0].queue_position_set(queue_position_t{0});
635 	torrents[1].queue_position_set(queue_position_t{1});
636 	// torrent 2 should be get moved down by 0 and 1 to pos 2
637 
638 	TEST_EQUAL(finished.queue_position(), no_pos);
639 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
640 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
641 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
642 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
643 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
644 
645 	//test strange up and down commands
646 	torrents[0].queue_position_up();
647 	torrents[4].queue_position_down();
648 
649 	TEST_EQUAL(finished.queue_position(), no_pos);
650 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
651 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
652 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
653 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
654 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
655 
656 	torrents[1].queue_position_up();
657 	torrents[3].queue_position_down();
658 	finished.queue_position_up();
659 
660 	TEST_EQUAL(finished.queue_position(), no_pos);
661 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{0});
662 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{1});
663 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
664 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{3});
665 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{4});
666 
667 	torrents[1].queue_position_down();
668 	torrents[3].queue_position_up();
669 	finished.queue_position_down();
670 
671 
672 	TEST_EQUAL(finished.queue_position(), no_pos);
673 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
674 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
675 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
676 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
677 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
678 
679 	// test set pos on not existing pos
680 	torrents[3].queue_position_set(queue_position_t{10});
681 	finished.queue_position_set(queue_position_t{10});
682 
683 	TEST_EQUAL(finished.queue_position(), no_pos);
684 	TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
685 	TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
686 	TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
687 	TEST_EQUAL(torrents[4].queue_position(), queue_position_t{3});
688 	TEST_EQUAL(torrents[3].queue_position(), queue_position_t{4});
689 }
690 
691 } // anonymous namespace
692 
TORRENT_TEST(queue)693 TORRENT_TEST(queue)
694 {
695 	test_queue(add_torrent_params());
696 }
697 
TORRENT_TEST(queue_paused)698 TORRENT_TEST(queue_paused)
699 {
700 	add_torrent_params p;
701 	p.flags |= torrent_flags::paused;
702 	p.flags &= ~torrent_flags::auto_managed;
703 	test_queue(p);
704 }
705 
TORRENT_TEST(test_move_storage_no_metadata)706 TORRENT_TEST(test_move_storage_no_metadata)
707 {
708 	lt::session ses(settings());
709 	add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
710 	p.save_path = "save_path";
711 	torrent_handle h = ses.add_torrent(p);
712 
713 	TEST_EQUAL(h.status().save_path, complete("save_path"));
714 
715 	h.move_storage("save_path_1");
716 
717 	TEST_EQUAL(h.status().save_path, complete("save_path_1"));
718 }
719 
TORRENT_TEST(test_have_piece_no_metadata)720 TORRENT_TEST(test_have_piece_no_metadata)
721 {
722 	lt::session ses(settings());
723 	add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
724 	p.save_path = "save_path";
725 	torrent_handle h = ses.add_torrent(p);
726 
727 	TEST_EQUAL(h.have_piece(piece_index_t{-1}), false);
728 	TEST_EQUAL(h.have_piece(piece_index_t{0}), false);
729 	TEST_EQUAL(h.have_piece(piece_index_t{100}), false);
730 }
731 
TORRENT_TEST(test_have_piece_out_of_range)732 TORRENT_TEST(test_have_piece_out_of_range)
733 {
734 	lt::session ses(settings());
735 
736 	add_torrent_params p;
737 	static std::array<const int, 2> const file_sizes{{100000, 100000}};
738 	int const piece_size = 0x8000;
739 	p.ti = make_torrent(file_sizes, piece_size);
740 	p.save_path = "save_path";
741 	p.flags |= torrent_flags::seed_mode;
742 	torrent_handle h = ses.add_torrent(p);
743 
744 	TEST_EQUAL(h.have_piece(piece_index_t{-1}), false);
745 	TEST_EQUAL(h.have_piece(piece_index_t{0}), true);
746 	TEST_EQUAL(h.have_piece(piece_index_t{100}), false);
747 }
748 
TORRENT_TEST(test_read_piece_no_metadata)749 TORRENT_TEST(test_read_piece_no_metadata)
750 {
751 	lt::session ses(settings());
752 	add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
753 	p.save_path = "save_path";
754 	torrent_handle h = ses.add_torrent(p);
755 
756 	h.read_piece(piece_index_t{-1});
757 
758 	alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece_alert");
759 	TEST_CHECK(a);
760 	if (auto* rp = alert_cast<read_piece_alert>(a))
761 	{
762 		TEST_CHECK(rp->error == error_code(lt::errors::no_metadata, lt::libtorrent_category()));
763 	}
764 }
765 
TORRENT_TEST(test_read_piece_out_of_range)766 TORRENT_TEST(test_read_piece_out_of_range)
767 {
768 	lt::session ses(settings());
769 
770 	add_torrent_params p;
771 	static std::array<const int, 2> const file_sizes{{100000, 100000}};
772 	int const piece_size = 0x8000;
773 	p.ti = make_torrent(file_sizes, piece_size);
774 	p.save_path = "save_path";
775 	p.flags |= torrent_flags::seed_mode;
776 	torrent_handle h = ses.add_torrent(p);
777 
778 	h.read_piece(piece_index_t{-1});
779 
780 	alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece_alert");
781 	TEST_CHECK(a);
782 	if (auto* rp = alert_cast<read_piece_alert>(a))
783 	{
784 		TEST_CHECK(rp->error == error_code(lt::errors::invalid_piece_index
785 			, lt::libtorrent_category()));
786 	}
787 }
788 
789 namespace {
790 int const piece_size = 0x4000 * 128;
791 
test_fs()792 file_storage test_fs()
793 {
794 	file_storage fs;
795 	fs.set_piece_length(piece_size);
796 	fs.add_file("temp", 99999999999);
797 	fs.set_num_pieces(int((fs.total_size() + piece_size - 1) / piece_size));
798 	return fs;
799 }
800 }
801 
TORRENT_TEST(test_calc_bytes_pieces)802 TORRENT_TEST(test_calc_bytes_pieces)
803 {
804 	auto const fs = test_fs();
805 	TEST_EQUAL(calc_bytes(fs, piece_count{2, 0, false}), 2 * piece_size);
806 }
807 
TORRENT_TEST(test_calc_bytes_pieces_last)808 TORRENT_TEST(test_calc_bytes_pieces_last)
809 {
810 	auto const fs = test_fs();
811 	TEST_EQUAL(calc_bytes(fs, piece_count{2, 0, true}), piece_size + fs.total_size() % piece_size);
812 }
813 
TORRENT_TEST(test_calc_bytes_no_pieces)814 TORRENT_TEST(test_calc_bytes_no_pieces)
815 {
816 	auto const fs = test_fs();
817 	TEST_EQUAL(calc_bytes(fs, piece_count{0, 0, false}), 0);
818 }
819 
TORRENT_TEST(test_calc_bytes_all_pieces)820 TORRENT_TEST(test_calc_bytes_all_pieces)
821 {
822 	auto const fs = test_fs();
823 	TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 0, true}), fs.total_size());
824 }
825 
TORRENT_TEST(test_calc_bytes_all_pieces_one_pad)826 TORRENT_TEST(test_calc_bytes_all_pieces_one_pad)
827 {
828 	auto const fs = test_fs();
829 	TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 1, true}), fs.total_size() - 0x4000);
830 }
831 
TORRENT_TEST(test_calc_bytes_all_pieces_two_pad)832 TORRENT_TEST(test_calc_bytes_all_pieces_two_pad)
833 {
834 	auto const fs = test_fs();
835 	TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 2, true}), fs.total_size() - 2 * 0x4000);
836 }
837 
838 #if TORRENT_HAS_SYMLINK
TORRENT_TEST(symlinks_restore)839 TORRENT_TEST(symlinks_restore)
840 {
841 	// downloading test torrent with symlinks
842 	std::string const work_dir = current_working_directory();
843 	lt::add_torrent_params p;
844 	p.ti = std::make_shared<lt::torrent_info>(combine_path(
845 		combine_path(parent_path(work_dir), "test_torrents"), "symlink2.torrent"));
846 	p.flags &= ~lt::torrent_flags::paused;
847 	p.save_path = work_dir;
848 	settings_pack pack = settings();
849 	pack.set_int(libtorrent::settings_pack::alert_mask, libtorrent::alert_category::status | libtorrent::alert_category::error);
850 	lt::session ses(std::move(pack));
851 	ses.add_torrent(p);
852 
853 	wait_for_alert(ses, torrent_checked_alert::alert_type, "torrent_checked_alert");
854 
855 	std::string const f = combine_path(combine_path(work_dir, "Some.framework"), "SDL2");
856 	TEST_CHECK(aux::get_file_attributes(f) & file_storage::flag_symlink);
857 	TEST_CHECK(aux::get_symlink_path(f) == "Versions/A/SDL2");
858 }
859 #endif
860 
TORRENT_TEST(redundant_add_piece)861 TORRENT_TEST(redundant_add_piece)
862 {
863 	file_storage fs;
864 	fs.add_file("tmp1", 128 * 1024 * 8);
865 	lt::create_torrent t(fs, 128 * 1024, 6);
866 
867 	TEST_CHECK(t.num_pieces() > 0);
868 
869 	std::vector<char> piece_data(std::size_t(fs.piece_length()), 0);
870 	aux::random_bytes(piece_data);
871 
872 	sha1_hash const ph = lt::hasher(piece_data).final();
873 	for (auto const i : fs.piece_range())
874 		t.set_hash(i, ph);
875 
876 	std::vector<char> buf;
877 	bencode(std::back_inserter(buf), t.generate());
878 	auto ti = std::make_shared<torrent_info>(buf, from_span);
879 
880 	lt::session ses(settings());
881 	lt::add_torrent_params atp;
882 	atp.ti = ti;
883 	atp.flags &= ~torrent_flags::paused;
884 	atp.save_path = ".";
885 	auto h = ses.add_torrent(atp);
886 	wait_for_downloading(ses, "");
887 
888 	h.add_piece(piece_index_t{0}, piece_data.data());
889 	h.set_piece_deadline(piece_index_t{0}, 0, torrent_handle::alert_when_available);
890 	h.prioritize_pieces(std::vector<lt::download_priority_t>(std::size_t(ti->num_pieces()), lt::dont_download));
891 	h.add_piece(piece_index_t{0}, piece_data.data());
892 	std::this_thread::sleep_for(lt::seconds(2));
893 }
894 
TORRENT_TEST(test_in_session)895 TORRENT_TEST(test_in_session)
896 {
897 	lt::session ses(settings());
898 	std::shared_ptr<torrent_info> ti = generate_torrent();
899 	add_torrent_params p;
900 	p.ti = ti;
901 	p.save_path = ".";
902 	torrent_handle h = ses.add_torrent(p);
903 	TEST_CHECK(h.in_session());
904 	ses.remove_torrent(h);
905 	TEST_CHECK(!h.in_session());
906 }
907