1 // Copyright Daniel Wallin 2006. Use, modification and distribution is
2 // subject to the Boost Software License, Version 1.0. (See accompanying
3 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4 
5 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
6 
7 #include "boost_python.hpp"
8 #include <memory>
9 
10 #include "libtorrent/torrent_info.hpp"
11 #include "libtorrent/time.hpp"
12 #include "libtorrent/socket_io.hpp"
13 #include "libtorrent/announce_entry.hpp"
14 #include "bytes.hpp"
15 
16 #ifdef _MSC_VER
17 #pragma warning(push)
18 // warning C4996: X: was declared deprecated
19 #pragma warning( disable : 4996 )
20 #endif
21 
22 using namespace boost::python;
23 using namespace lt;
24 
25 namespace
26 {
27 
begin_trackers(torrent_info & i)28     std::vector<announce_entry>::const_iterator begin_trackers(torrent_info& i)
29     {
30         return i.trackers().begin();
31     }
32 
end_trackers(torrent_info & i)33     std::vector<announce_entry>::const_iterator end_trackers(torrent_info& i)
34     {
35         return i.trackers().end();
36     }
37 
add_node(torrent_info & ti,char const * hostname,int port)38     void add_node(torrent_info& ti, char const* hostname, int port)
39     {
40         ti.add_node(std::make_pair(hostname, port));
41     }
42 
nodes(torrent_info const & ti)43     list nodes(torrent_info const& ti)
44     {
45         list result;
46 
47         for (auto const& i : ti.nodes())
48             result.append(boost::python::make_tuple(i.first, i.second));
49 
50         return result;
51     }
52 
get_web_seeds(torrent_info const & ti)53     list get_web_seeds(torrent_info const& ti)
54     {
55         std::vector<web_seed_entry> const& ws = ti.web_seeds();
56         list ret;
57         for (std::vector<web_seed_entry>::const_iterator i = ws.begin()
58             , end(ws.end()); i != end; ++i)
59         {
60             dict d;
61             d["url"] = i->url;
62             d["type"] = i->type;
63             d["auth"] = i->auth;
64             ret.append(d);
65         }
66 
67         return ret;
68     }
69 
set_web_seeds(torrent_info & ti,list ws)70     void set_web_seeds(torrent_info& ti, list ws)
71     {
72         std::vector<web_seed_entry> web_seeds;
73         int const len = static_cast<int>(boost::python::len(ws));
74         for (int i = 0; i < len; i++)
75         {
76            dict e = extract<dict>(ws[i]);
77            int const type = extract<int>(e["type"]);
78            web_seeds.push_back(web_seed_entry(
79               extract<std::string>(e["url"])
80               , static_cast<web_seed_entry::type_t>(type)
81               , extract<std::string>(e["auth"])));
82         }
83         ti.set_web_seeds(web_seeds);
84     }
85 
get_merkle_tree(torrent_info const & ti)86     list get_merkle_tree(torrent_info const& ti)
87     {
88         std::vector<sha1_hash> const& mt = ti.merkle_tree();
89         list ret;
90         for (std::vector<sha1_hash>::const_iterator i = mt.begin()
91             , end(mt.end()); i != end; ++i)
92         {
93             ret.append(bytes(i->to_string()));
94         }
95         return ret;
96     }
97 
set_merkle_tree(torrent_info & ti,list hashes)98     void set_merkle_tree(torrent_info& ti, list hashes)
99     {
100         std::vector<sha1_hash> h;
101         for (int i = 0, e = int(len(hashes)); i < e; ++i)
102             h.push_back(sha1_hash(bytes(extract<bytes>(hashes[i])).arr.data()));
103 
104         ti.set_merkle_tree(h);
105     }
106 
hash_for_piece(torrent_info const & ti,piece_index_t i)107     bytes hash_for_piece(torrent_info const& ti, piece_index_t i)
108     {
109         return bytes(ti.hash_for_piece(i).to_string());
110     }
111 
metadata(torrent_info const & ti)112     bytes metadata(torrent_info const& ti)
113     {
114         return bytes(ti.metadata().get(), ti.metadata_size());
115     }
116 
map_block(torrent_info & ti,piece_index_t piece,std::int64_t offset,int size)117     list map_block(torrent_info& ti, piece_index_t piece, std::int64_t offset, int size)
118     {
119        std::vector<file_slice> p = ti.map_block(piece, offset, size);
120        list result;
121 
122        for (std::vector<file_slice>::iterator i(p.begin()), e(p.end()); i != e; ++i)
123           result.append(*i);
124 
125        return result;
126     }
127 
128 #if TORRENT_ABI_VERSION == 1
129     // Create getters for announce_entry data members with non-trivial types which need converting.
get_next_announce(announce_entry const & ae)130     lt::time_point get_next_announce(announce_entry const& ae)
131     { return ae.endpoints.empty() ? lt::time_point() : lt::time_point(ae.endpoints.front().next_announce); }
get_min_announce(announce_entry const & ae)132     lt::time_point get_min_announce(announce_entry const& ae)
133     { return ae.endpoints.empty() ? lt::time_point() : lt::time_point(ae.endpoints.front().min_announce); }
134     // announce_entry data member bit-fields.
get_fails(announce_entry const & ae)135     int get_fails(announce_entry const& ae)
136     { return ae.endpoints.empty() ? 0 : ae.endpoints.front().fails; }
get_updating(announce_entry const & ae)137     bool get_updating(announce_entry const& ae)
138     { return ae.endpoints.empty() ? false : ae.endpoints.front().updating; }
get_start_sent(announce_entry const & ae)139     bool get_start_sent(announce_entry const& ae)
140     { return ae.endpoints.empty() ? false : ae.endpoints.front().start_sent; }
get_complete_sent(announce_entry const & ae)141     bool get_complete_sent(announce_entry const& ae)
142     { return ae.endpoints.empty() ? false : ae.endpoints.front().complete_sent; }
143     // announce_entry method requires lt::time_point.
can_announce(announce_entry const & ae,bool is_seed)144     bool can_announce(announce_entry const& ae, bool is_seed) {
145         // an entry without endpoints implies it has never been announced so it can be now
146         if (ae.endpoints.empty()) return true;
147         lt::time_point now = lt::clock_type::now();
148         return ae.endpoints.front().can_announce(now, is_seed, ae.fail_limit);
149     }
is_working(announce_entry const & ae)150     bool is_working(announce_entry const& ae)
151     { return ae.endpoints.empty() ? false : ae.endpoints.front().is_working(); }
152 #endif
get_source(announce_entry const & ae)153     int get_source(announce_entry const& ae) { return ae.source; }
get_verified(announce_entry const & ae)154     bool get_verified(announce_entry const& ae) { return ae.verified; }
155 
156 #if TORRENT_ABI_VERSION == 1
get_message(announce_entry const & ae)157     std::string get_message(announce_entry const& ae)
158     { return ae.endpoints.empty() ? "" : ae.endpoints.front().message; }
get_last_error(announce_entry const & ae)159     error_code get_last_error(announce_entry const& ae)
160     { return ae.endpoints.empty() ? error_code() : ae.endpoints.front().last_error; }
get_scrape_incomplete(announce_entry const & ae)161     int get_scrape_incomplete(announce_entry const& ae)
162     { return ae.endpoints.empty() ? 0 : ae.endpoints.front().scrape_incomplete; }
get_scrape_complete(announce_entry const & ae)163     int get_scrape_complete(announce_entry const& ae)
164     { return ae.endpoints.empty() ? 0 : ae.endpoints.front().scrape_complete; }
get_scrape_downloaded(announce_entry const & ae)165     int get_scrape_downloaded(announce_entry const& ae)
166     { return ae.endpoints.empty() ? 0 : ae.endpoints.front().scrape_downloaded; }
next_announce_in(announce_entry const &)167     int next_announce_in(announce_entry const&) { return 0; }
min_announce_in(announce_entry const &)168     int min_announce_in(announce_entry const&) { return 0; }
get_send_stats(announce_entry const & ae)169     bool get_send_stats(announce_entry const& ae) { return ae.send_stats; }
get_size(file_entry const & fe)170     std::int64_t get_size(file_entry const& fe) { return fe.size; }
get_offset(file_entry const & fe)171     std::int64_t get_offset(file_entry const& fe) { return fe.offset; }
get_pad_file(file_entry const & fe)172     bool get_pad_file(file_entry const& fe) { return fe.pad_file; }
get_executable_attribute(file_entry const & fe)173     bool get_executable_attribute(file_entry const& fe) { return fe.executable_attribute; }
get_hidden_attribute(file_entry const & fe)174     bool get_hidden_attribute(file_entry const& fe) { return fe.hidden_attribute; }
get_symlink_attribute(file_entry const & fe)175     bool get_symlink_attribute(file_entry const& fe) { return fe.symlink_attribute; }
176 #endif
177 
dict_to_limits(dict limits)178 load_torrent_limits dict_to_limits(dict limits)
179 {
180     load_torrent_limits ret;
181 
182     list items = limits.items();
183     int const len = int(boost::python::len(limits));
184     for (int i = 0; i < len; i++)
185     {
186         boost::python::api::object_item item = items[i];
187         std::string const key = extract<std::string>(item[0]);
188         object const value = item[1];
189 
190         if (key == "max_buffer_size")
191         {
192             ret.max_buffer_size = extract<int>(value);
193             continue;
194         }
195         else if (key == "max_pieces")
196         {
197             ret.max_pieces = extract<int>(value);
198             continue;
199         }
200         else if (key == "max_decode_depth")
201         {
202             ret.max_decode_depth = extract<int>(value);
203             continue;
204         }
205         else if (key == "max_decode_tokens")
206         {
207             ret.max_decode_tokens = extract<int>(value);
208             continue;
209         }
210     }
211     return ret;
212 }
213 
214 } // namespace unnamed
215 
buffer_constructor0(bytes b)216 std::shared_ptr<torrent_info> buffer_constructor0(bytes b)
217 {
218    return std::make_shared<torrent_info>(b.arr, from_span);
219 }
220 
buffer_constructor1(bytes b,dict limits)221 std::shared_ptr<torrent_info> buffer_constructor1(bytes b, dict limits)
222 {
223    std::shared_ptr<torrent_info> ret = std::make_shared<torrent_info>(b.arr
224         , dict_to_limits(limits), from_span);
225    return ret;
226 }
227 
file_constructor0(std::string const & filename)228 std::shared_ptr<torrent_info> file_constructor0(std::string const& filename)
229 {
230    return std::make_shared<torrent_info>(filename);
231 }
232 
file_constructor1(std::string const & filename,dict limits)233 std::shared_ptr<torrent_info> file_constructor1(std::string const& filename, dict limits)
234 {
235    return std::make_shared<torrent_info>(filename, dict_to_limits(limits));
236 }
237 
bencoded_constructor0(entry const & ent)238 std::shared_ptr<torrent_info> bencoded_constructor0(entry const& ent)
239 {
240     std::vector<char> buf;
241     bencode(std::back_inserter(buf), ent);
242     return std::make_shared<torrent_info>(buf, lt::from_span);
243 }
244 
bencoded_constructor1(entry const & ent,dict limits)245 std::shared_ptr<torrent_info> bencoded_constructor1(entry const& ent, dict limits)
246 {
247     std::vector<char> buf;
248     bencode(std::back_inserter(buf), ent);
249     return std::make_shared<torrent_info>(buf, dict_to_limits(limits)
250         , lt::from_span);
251 }
252 
253 using by_value = return_value_policy<return_by_value>;
bind_torrent_info()254 void bind_torrent_info()
255 {
256     return_value_policy<copy_const_reference> copy;
257 
258     void (torrent_info::*rename_file0)(file_index_t, std::string const&) = &torrent_info::rename_file;
259 #if TORRENT_ABI_VERSION == 1
260 #ifdef TORRENT_WINDOWS
261     void (torrent_info::*rename_file1)(file_index_t, std::wstring const&) = &torrent_info::rename_file;
262 #endif
263 #endif
264 
265     class_<file_slice>("file_slice")
266         .add_property("file_index", make_getter((&file_slice::file_index), by_value()))
267         .def_readwrite("offset", &file_slice::offset)
268         .def_readwrite("size", &file_slice::size)
269         ;
270 
271     enum_<announce_entry::tracker_source>("tracker_source")
272         .value("source_torrent", announce_entry::source_torrent)
273         .value("source_client", announce_entry::source_client)
274         .value("source_magnet_link", announce_entry::source_magnet_link)
275         .value("source_tex", announce_entry::source_tex)
276     ;
277 
278     using add_tracker1 = void (torrent_info::*)(std::string const&, int, announce_entry::tracker_source);
279 
280     class_<torrent_info, std::shared_ptr<torrent_info>>("torrent_info", no_init)
281         .def(init<sha1_hash const&>(arg("info_hash")))
282         .def("__init__", make_constructor(&bencoded_constructor0))
283         .def("__init__", make_constructor(&bencoded_constructor1))
284         .def("__init__", make_constructor(&buffer_constructor0))
285         .def("__init__", make_constructor(&buffer_constructor1))
286         .def("__init__", make_constructor(&file_constructor0))
287         .def("__init__", make_constructor(&file_constructor1))
288         .def(init<torrent_info const&>((arg("ti"))))
289 
290 #if TORRENT_ABI_VERSION == 1
291 #ifdef TORRENT_WINDOWS
292         .def(init<std::wstring>((arg("file"))))
293 #endif
294 #endif
295 
296         .def("add_tracker", (add_tracker1)&torrent_info::add_tracker, arg("url"), arg("tier") = 0, arg("source") = announce_entry::source_client)
297         .def("add_url_seed", &torrent_info::add_url_seed)
298         .def("add_http_seed", &torrent_info::add_http_seed)
299         .def("web_seeds", get_web_seeds)
300         .def("set_web_seeds", set_web_seeds)
301 
302         .def("name", &torrent_info::name, copy)
303         .def("comment", &torrent_info::comment, copy)
304         .def("creator", &torrent_info::creator, copy)
305         .def("total_size", &torrent_info::total_size)
306         .def("piece_length", &torrent_info::piece_length)
307         .def("num_pieces", &torrent_info::num_pieces)
308         .def("info_hash", &torrent_info::info_hash, copy)
309         .def("hash_for_piece", &hash_for_piece)
310         .def("merkle_tree", get_merkle_tree)
311         .def("set_merkle_tree", set_merkle_tree)
312         .def("piece_size", &torrent_info::piece_size)
313 
314         .def("similar_torrents", &torrent_info::similar_torrents)
315         .def("collections", &torrent_info::collections)
316         .def("ssl_cert", &torrent_info::ssl_cert)
317         .def("num_files", &torrent_info::num_files)
318         .def("rename_file", rename_file0)
319         .def("remap_files", &torrent_info::remap_files)
320         .def("files", &torrent_info::files, return_internal_reference<>())
321         .def("orig_files", &torrent_info::orig_files, return_internal_reference<>())
322 #if TORRENT_ABI_VERSION == 1
323         .def("file_at", &torrent_info::file_at)
324         .def("file_at_offset", &torrent_info::file_at_offset)
325 #ifdef TORRENT_WINDOWS
326         .def("rename_file", rename_file1)
327 #endif
328 #endif // TORRENT_ABI_VERSION
329 
330         .def("is_valid", &torrent_info::is_valid)
331         .def("priv", &torrent_info::priv)
332         .def("is_i2p", &torrent_info::is_i2p)
333         .def("is_merkle_torrent", &torrent_info::is_merkle_torrent)
334         .def("trackers", range(begin_trackers, end_trackers))
335 
336         .def("creation_date", &torrent_info::creation_date)
337 
338         .def("add_node", &add_node)
339         .def("nodes", &nodes)
340         .def("metadata", &metadata)
341         .def("metadata_size", &torrent_info::metadata_size)
342         .def("map_block", map_block)
343         .def("map_file", &torrent_info::map_file)
344         ;
345 
346 #if TORRENT_ABI_VERSION == 1
347     class_<file_entry>("file_entry")
348         .def_readwrite("path", &file_entry::path)
349         .def_readwrite("symlink_path", &file_entry::symlink_path)
350         .def_readwrite("filehash", &file_entry::filehash)
351         .def_readwrite("mtime", &file_entry::mtime)
352         .add_property("pad_file", &get_pad_file)
353         .add_property("executable_attribute", &get_executable_attribute)
354         .add_property("hidden_attribute", &get_hidden_attribute)
355         .add_property("symlink_attribute", &get_symlink_attribute)
356         .add_property("offset", &get_offset)
357         .add_property("size", &get_size)
358         ;
359 #endif
360 
361     class_<announce_entry>("announce_entry", init<std::string const&>())
362         .def_readwrite("url", &announce_entry::url)
363         .def_readonly("trackerid", &announce_entry::trackerid)
364 #if TORRENT_ABI_VERSION == 1
365         .add_property("message", &get_message)
366         .add_property("last_error", &get_last_error)
367         .add_property("next_announce", &get_next_announce)
368         .add_property("min_announce", &get_min_announce)
369         .add_property("scrape_incomplete", &get_scrape_incomplete)
370         .add_property("scrape_complete", &get_scrape_complete)
371         .add_property("scrape_downloaded", &get_scrape_downloaded)
372 #endif
373         .def_readwrite("tier", &announce_entry::tier)
374         .def_readwrite("fail_limit", &announce_entry::fail_limit)
375         .add_property("source", &get_source)
376         .add_property("verified", &get_verified)
377 #if TORRENT_ABI_VERSION == 1
378         .add_property("fails", &get_fails)
379         .add_property("updating", &get_updating)
380         .add_property("start_sent", &get_start_sent)
381         .add_property("complete_sent", &get_complete_sent)
382         .add_property("send_stats", &get_send_stats)
383         .def("next_announce_in", &next_announce_in)
384         .def("min_announce_in", &min_announce_in)
385         .def("can_announce", &can_announce)
386         .def("is_working", &is_working)
387 #endif
388         .def("reset", &announce_entry::reset)
389         .def("trim", &announce_entry::trim)
390         ;
391 
392     implicitly_convertible<std::shared_ptr<torrent_info>, std::shared_ptr<const torrent_info>>();
393     boost::python::register_ptr_to_python<std::shared_ptr<const torrent_info>>();
394 }
395 
396 #ifdef _MSC_VER
397 #pragma warning(pop)
398 #endif
399