1 /*
2 
3 Copyright (c) 2016, 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 <deque>
34 
35 #include "make_torrent.hpp"
36 #include "libtorrent/storage.hpp"
37 #include "libtorrent/hasher.hpp"
38 #include "libtorrent/entry.hpp"
39 #include "libtorrent/bencode.hpp"
40 #include "libtorrent/file_pool.hpp"
41 #include "libtorrent/storage_defs.hpp"
42 
43 using namespace lt;
44 
make_test_torrent(torrent_args const & args)45 std::shared_ptr<lt::torrent_info> make_test_torrent(torrent_args const& args)
46 {
47 	entry e;
48 
49 	entry::dictionary_type& info = e["info"].dict();
50 	int total_size = 0;
51 
52 	if (args.m_priv)
53 	{
54 		info["priv"] = 1;
55 	}
56 
57 	// torrent offset ranges where the pad files are
58 	// used when generating hashes
59 	std::deque<std::pair<int,int>> pad_files;
60 
61 	int const piece_length = 32768;
62 	info["piece length"] = piece_length;
63 
64 	if (args.m_files.size() == 1)
65 	{
66 		std::string const& ent = args.m_files[0];
67 		std::string name = "test_file-1";
68 		if (ent.find("name=") != std::string::npos)
69 		{
70 			std::string::size_type pos = ent.find("name=") + 5;
71 			name = ent.substr(pos, ent.find(',', pos));
72 		}
73 		info["name"] = name;
74 		int file_size = atoi(args.m_files[0].c_str());
75 		info["length"] = file_size;
76 		total_size = file_size;
77 	}
78 	else
79 	{
80 		info["name"] = args.m_name;
81 
82 		entry::list_type& files = info["files"].list();
83 		for (int i = 0; i < int(args.m_files.size()); ++i)
84 		{
85 			auto const idx = static_cast<std::size_t>(i);
86 			int file_size = atoi(args.m_files[idx].c_str());
87 
88 			files.push_back(entry());
89 			entry::dictionary_type& file_entry = files.back().dict();
90 			std::string const& ent = args.m_files[idx];
91 			if (ent.find("padfile") != std::string::npos)
92 			{
93 				file_entry["attr"].string() += "p";
94 				pad_files.push_back(std::make_pair(total_size, total_size + file_size));
95 			}
96 			if (ent.find("executable") != std::string::npos)
97 				file_entry["attr"].string() += "x";
98 
99 			char filename[100];
100 			std::snprintf(filename, sizeof(filename), "test_file-%d", i);
101 
102 			std::string name = filename;
103 			if (ent.find("name=") != std::string::npos)
104 			{
105 				std::string::size_type pos = ent.find("name=") + 5;
106 				name = ent.substr(pos, ent.find(',', pos));
107 			}
108 			file_entry["path"].list().push_back(name);
109 			file_entry["length"] = file_size;
110 			total_size += file_size;
111 		}
112 	}
113 
114 	if (!args.m_url_seed.empty())
115 	{
116 		e["url-list"] = args.m_url_seed;
117 	}
118 
119 	if (!args.m_http_seed.empty())
120 	{
121 		e["httpseeds"] = args.m_http_seed;
122 	}
123 
124 	if (!args.m_collection.empty())
125 	{
126 		auto& l = info["collections"].list();
127 		l.push_back(args.m_collection);
128 	}
129 
130 	std::string piece_hashes;
131 
132 	int num_pieces = (total_size + piece_length - 1) / piece_length;
133 	int torrent_offset = 0;
134 	for (int i = 0; i < num_pieces; ++i)
135 	{
136 		hasher h;
137 		int const piece_size = (i < num_pieces - 1)
138 			? piece_length
139 			: total_size - (num_pieces - 1) * piece_length;
140 
141 		char const data = char(i & 0xff);
142 		char const zero = 0;
143 		for (int o = 0; o < piece_size; ++o, ++torrent_offset)
144 		{
145 			while (!pad_files.empty() && torrent_offset >= pad_files.front().second)
146 				pad_files.pop_front();
147 
148 			if (!pad_files.empty() && torrent_offset >= pad_files.front().first)
149 			{
150 				h.update(zero);
151 			}
152 			else
153 			{
154 				h.update(data);
155 			}
156 		}
157 		piece_hashes += h.final().to_string();
158 	}
159 
160 	info["pieces"] = piece_hashes;
161 
162 	std::vector<char> tmp;
163 	bencode(std::back_inserter(tmp), e);
164 
165 	FILE* f = fopen("test.torrent", "w+");
166 	fwrite(&tmp[0], 1, tmp.size(), f);
167 	fclose(f);
168 
169 	return std::make_shared<torrent_info>(tmp, from_span);
170 }
171 
generate_files(lt::torrent_info const & ti,std::string const & path,bool alternate_data)172 void generate_files(lt::torrent_info const& ti, std::string const& path
173 	, bool alternate_data)
174 {
175 	file_pool fp;
176 
177 	aux::vector<download_priority_t, file_index_t> priorities;
178 	sha1_hash info_hash;
179 	storage_params params{
180 		ti.files(),
181 		nullptr,
182 		path,
183 		storage_mode_t::storage_mode_sparse,
184 		priorities,
185 		info_hash
186 	};
187 
188 	default_storage st(params, fp);
189 
190 	file_storage const& fs = ti.files();
191 	std::vector<char> buffer;
192 	for (auto const i : fs.piece_range())
193 	{
194 		int const piece_size = ti.piece_size(i);
195 		buffer.resize(static_cast<std::size_t>(ti.piece_length()));
196 
197 		char const data = static_cast<char>((alternate_data
198 			? 255 - static_cast<int>(i) : static_cast<int>(i)) & 0xff);
199 		for (int o = 0; o < piece_size; ++o)
200 		{
201 			buffer[static_cast<std::size_t>(o)] = data;
202 		}
203 
204 		iovec_t b = { &buffer[0], piece_size };
205 		storage_error ec;
206 		int ret = st.writev(b, i, 0, open_mode::read_only, ec);
207 		if (ret != piece_size || ec)
208 		{
209 			std::printf("ERROR writing files: (%d expected %d) %s\n"
210 				, ret, piece_size, ec.ec.message().c_str());
211 		}
212 	}
213 }
214