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