3 Copyright (c) 2003-2008, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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.
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.
33 #include "libtorrent/pch.hpp"
44 #pragma warning(push, 1)
47 #include <boost/lexical_cast.hpp>
48 #include <boost/filesystem/path.hpp>
49 #include <boost/filesystem.hpp>
50 #include <boost/bind.hpp>
56 #include "libtorrent/torrent_info.hpp"
57 #include "libtorrent/bencode.hpp"
58 #include "libtorrent/hasher.hpp"
59 #include "libtorrent/entry.hpp"
60 #include "libtorrent/file.hpp"
62 namespace gr
= boost::gregorian
;
64 using namespace libtorrent
;
69 namespace fs
= boost::filesystem
;
71 void convert_to_utf8(std::string
& str
, unsigned char chr
)
73 str
+= 0xc0 | ((chr
& 0xff) >> 6);
74 str
+= 0x80 | (chr
& 0x3f);
77 void verify_encoding(file_entry
& target
)
80 std::string file_path
= target
.path
.string();
81 bool valid_encoding
= true;
82 for (std::string::iterator i
= file_path
.begin()
83 , end(file_path
.end()); i
!= end
; ++i
)
85 // valid ascii-character
92 if (std::distance(i
, end
) < 2)
94 convert_to_utf8(tmp_path
, *i
);
95 valid_encoding
= false;
99 // valid 2-byte utf-8 character
100 if ((i
[0] & 0xe0) == 0xc0
101 && (i
[1] & 0xc0) == 0x80)
109 if (std::distance(i
, end
) < 3)
111 convert_to_utf8(tmp_path
, *i
);
112 valid_encoding
= false;
116 // valid 3-byte utf-8 character
117 if ((i
[0] & 0xf0) == 0xe0
118 && (i
[1] & 0xc0) == 0x80
119 && (i
[2] & 0xc0) == 0x80)
128 if (std::distance(i
, end
) < 4)
130 convert_to_utf8(tmp_path
, *i
);
131 valid_encoding
= false;
135 // valid 4-byte utf-8 character
136 if ((i
[0] & 0xf0) == 0xe0
137 && (i
[1] & 0xc0) == 0x80
138 && (i
[2] & 0xc0) == 0x80
139 && (i
[3] & 0xc0) == 0x80)
149 convert_to_utf8(tmp_path
, *i
);
150 valid_encoding
= false;
152 // the encoding was not valid utf-8
153 // save the original encoding and replace the
154 // commonly used path with the correctly
156 if (!valid_encoding
) target
.path
= tmp_path
;
159 bool extract_single_file(lazy_entry
const& dict
, file_entry
& target
160 , std::string
const& root_dir
)
162 lazy_entry
const* length
= dict
.dict_find("length");
163 if (length
== 0 || length
->type() != lazy_entry::int_t
)
165 target
.size
= length
->int_value();
166 target
.path
= root_dir
;
167 target
.file_base
= 0;
169 // prefer the name.utf-8
170 // because if it exists, it is more
171 // likely to be correctly encoded
173 lazy_entry
const* p
= dict
.dict_find("path.utf-8");
174 if (p
== 0 || p
->type() != lazy_entry::list_t
)
175 p
= dict
.dict_find("path");
176 if (p
== 0 || p
->type() != lazy_entry::list_t
)
179 for (int i
= 0, end(p
->list_size()); i
< end
; ++i
)
181 if (p
->list_at(i
)->type() != lazy_entry::string_t
)
183 std::string path_element
= p
->list_at(i
)->string_value();
184 if (path_element
!= "..")
185 target
.path
/= path_element
;
187 verify_encoding(target
);
188 if (target
.path
.is_complete())
193 #if BOOST_VERSION < 103600
194 if (target
.path
.leaf().substr(0, 18) == "_____padding_file_")
196 if (target
.path
.filename().substr(0, 18) == "_____padding_file_")
198 target
.pad_file
= true;
203 bool extract_files(lazy_entry
const& list
, file_storage
& target
204 , std::string
const& root_dir
)
206 if (list
.type() != lazy_entry::list_t
) return false;
207 for (int i
= 0, end(list
.list_size()); i
< end
; ++i
)
210 if (!extract_single_file(*list
.list_at(i
), e
, root_dir
))
221 int load_file(fs::path
const& filename
, std::vector
<char>& v
)
225 if (!f
.open(filename
, file::read_only
, ec
)) return -1;
226 f
.seek(0, file::end
, ec
);
228 size_type s
= f
.tell(ec
);
230 if (s
> 5000000) return -2;
232 if (s
== 0) return 0;
233 f
.seek(0, file::begin
, ec
);
235 size_type read
= f
.read(&v
[0], s
, ec
);
236 if (read
!= s
) return -3;
241 #ifndef TORRENT_NO_DEPRECATE
242 // standard constructor that parses a torrent file
243 torrent_info::torrent_info(entry
const& torrent_file
)
244 : m_creation_date(pt::ptime(pt::not_a_date_time
))
247 , m_info_section_size(0)
250 std::vector
<char> tmp
;
251 std::back_insert_iterator
<std::vector
<char> > out(tmp
);
252 bencode(out
, torrent_file
);
255 lazy_bdecode(&tmp
[0], &tmp
[0] + tmp
.size(), e
);
257 #ifndef BOOST_NO_EXCEPTIONS
258 if (!parse_torrent_file(e
, error
))
259 throw invalid_torrent_file();
261 parse_torrent_file(e
, error
);
266 torrent_info::torrent_info(lazy_entry
const& torrent_file
)
267 : m_creation_date(pt::ptime(pt::not_a_date_time
))
270 , m_info_section_size(0)
274 #ifndef BOOST_NO_EXCEPTIONS
275 if (!parse_torrent_file(torrent_file
, error
))
276 throw invalid_torrent_file();
278 parse_torrent_file(torrent_file
, error
);
282 torrent_info::torrent_info(char const* buffer
, int size
)
283 : m_creation_date(pt::ptime(pt::not_a_date_time
))
286 , m_info_section_size(0)
291 lazy_bdecode(buffer
, buffer
+ size
, e
);
292 #ifndef BOOST_NO_EXCEPTIONS
293 if (!parse_torrent_file(e
, error
))
294 throw invalid_torrent_file();
296 parse_torrent_file(e
, error
);
300 // constructor used for creating new torrents
301 // will not contain any hashes, comments, creation date
302 // just the necessary to use it with piece manager
303 // used for torrents with no metadata
304 torrent_info::torrent_info(sha1_hash
const& info_hash
)
305 : m_info_hash(info_hash
)
306 , m_creation_date(pt::second_clock::universal_time())
309 , m_info_section_size(0)
313 torrent_info::torrent_info(fs::path
const& filename
)
314 : m_creation_date(pt::ptime(pt::not_a_date_time
))
318 std::vector
<char> buf
;
319 int ret
= load_file(filename
, buf
);
323 #ifndef BOOST_NO_EXCEPTIONS
324 throw invalid_torrent_file();
330 lazy_bdecode(&buf
[0], &buf
[0] + buf
.size(), e
);
332 #ifndef BOOST_NO_EXCEPTIONS
333 if (!parse_torrent_file(e
, error
))
334 throw invalid_torrent_file();
336 parse_torrent_file(e
, error
);
340 torrent_info::~torrent_info()
343 void torrent_info::swap(torrent_info
& ti
)
346 m_urls
.swap(ti
.m_urls
);
347 m_url_seeds
.swap(ti
.m_url_seeds
);
348 m_files
.swap(ti
.m_files
);
349 m_nodes
.swap(ti
.m_nodes
);
350 swap(m_info_hash
, ti
.m_info_hash
);
351 swap(m_creation_date
, ti
.m_creation_date
);
352 m_comment
.swap(ti
.m_comment
);
353 m_created_by
.swap(ti
.m_created_by
);
354 swap(m_multifile
, ti
.m_multifile
);
355 swap(m_private
, ti
.m_private
);
356 swap(m_info_section
, ti
.m_info_section
);
357 swap(m_info_section_size
, ti
.m_info_section_size
);
358 swap(m_piece_hashes
, ti
.m_piece_hashes
);
359 swap(m_info_dict
, ti
.m_info_dict
);
362 bool torrent_info::parse_info_section(lazy_entry
const& info
, std::string
& error
)
364 if (info
.type() != lazy_entry::dict_t
)
366 error
= "'info' entry is not a dictionary";
370 // hash the info-field to calculate info-hash
372 std::pair
<char const*, int> section
= info
.data_section();
373 h
.update(section
.first
, section
.second
);
374 m_info_hash
= h
.final();
376 // copy the info section
377 m_info_section_size
= section
.second
;
378 m_info_section
.reset(new char[m_info_section_size
]);
379 memcpy(m_info_section
.get(), section
.first
, m_info_section_size
);
380 TORRENT_ASSERT(section
.first
[0] == 'd');
381 TORRENT_ASSERT(section
.first
[m_info_section_size
-1] == 'e');
383 // extract piece length
384 int piece_length
= info
.dict_find_int_value("piece length", -1);
385 if (piece_length
<= 0)
387 error
= "invalid or missing 'piece length' entry in torrent file";
390 m_files
.set_piece_length(piece_length
);
392 // extract file name (or the directory name if it's a multifile libtorrent)
393 std::string name
= info
.dict_find_string_value("name.utf-8");
394 if (name
.empty()) name
= info
.dict_find_string_value("name");
397 error
= "missing name in torrent file";
402 if (tmp
.is_complete())
406 #if BOOST_VERSION < 103600
407 else if (tmp
.has_branch_path())
409 else if (tmp
.has_parent_path())
413 for (fs::path::iterator i
= tmp
.begin()
414 , end(tmp
.end()); i
!= end
; ++i
)
416 if (*i
== "." || *i
== "..") continue;
421 if (name
== ".." || name
== ".")
423 error
= "invalid 'name' of torrent (possible exploit attempt)";
428 lazy_entry
const* i
= info
.dict_find_list("files");
431 // if there's no list of files, there has to be a length
436 e
.size
= info
.dict_find_int_value("length", -1);
438 #if BOOST_VERSION < 103600
439 if (e
.path
.leaf().substr(0, 18) == "_____padding_file_")
441 if (e
.path
.filename().substr(0, 18) == "_____padding_file_")
446 error
= "invalid length of torrent";
454 if (!extract_files(*i
, m_files
, name
))
456 error
= "failed to parse files from torrent file";
461 m_files
.set_name(name
);
463 // extract sha-1 hashes for all pieces
464 // we want this division to round upwards, that's why we have the
467 m_files
.set_num_pieces(int((m_files
.total_size() + m_files
.piece_length() - 1)
468 / m_files
.piece_length()));
470 lazy_entry
const* pieces
= info
.dict_find("pieces");
471 if (pieces
== 0 || pieces
->type() != lazy_entry::string_t
)
473 error
= "invalid or missing 'pieces' entry in torrent file";
477 if (pieces
->string_length() != m_files
.num_pieces() * 20)
479 error
= "incorrect number of piece hashes in torrent file";
483 m_piece_hashes
= m_info_section
.get() + (pieces
->string_ptr() - section
.first
);
484 TORRENT_ASSERT(m_piece_hashes
>= m_info_section
.get());
485 TORRENT_ASSERT(m_piece_hashes
< m_info_section
.get() + m_info_section_size
);
487 m_private
= info
.dict_find_int_value("private", 0);
491 bool torrent_info::parse_torrent_file(lazy_entry
const& torrent_file
, std::string
& error
)
493 if (torrent_file
.type() != lazy_entry::dict_t
)
495 error
= "torrent file is not a dictionary";
499 // extract the url of the tracker
500 lazy_entry
const* i
= torrent_file
.dict_find_list("announce-list");
503 m_urls
.reserve(i
->list_size());
504 for (int j
= 0, end(i
->list_size()); j
< end
; ++j
)
506 lazy_entry
const* tier
= i
->list_at(j
);
507 if (tier
->type() != lazy_entry::list_t
) continue;
508 for (int k
= 0, end(tier
->list_size()); k
< end
; ++k
)
510 announce_entry
e(tier
->list_string_value_at(k
));
511 if (e
.url
.empty()) continue;
518 std::vector
<announce_entry
>::iterator start
= m_urls
.begin();
519 std::vector
<announce_entry
>::iterator stop
;
520 int current_tier
= m_urls
.front().tier
;
521 for (stop
= m_urls
.begin(); stop
!= m_urls
.end(); ++stop
)
523 if (stop
->tier
!= current_tier
)
525 std::random_shuffle(start
, stop
);
527 current_tier
= stop
->tier
;
530 std::random_shuffle(start
, stop
);
536 announce_entry
e(torrent_file
.dict_find_string_value("announce"));
537 if (!e
.url
.empty()) m_urls
.push_back(e
);
540 lazy_entry
const* nodes
= torrent_file
.dict_find_list("nodes");
543 for (int i
= 0, end(nodes
->list_size()); i
< end
; ++i
)
545 lazy_entry
const* n
= nodes
->list_at(i
);
546 if (n
->type() != lazy_entry::list_t
547 || n
->list_size() < 2
548 || n
->list_at(0)->type() != lazy_entry::string_t
549 || n
->list_at(1)->type() != lazy_entry::int_t
)
551 m_nodes
.push_back(std::make_pair(
552 n
->list_at(0)->string_value()
553 , int(n
->list_at(1)->int_value())));
557 // extract creation date
558 size_type cd
= torrent_file
.dict_find_int_value("creation date", -1);
561 m_creation_date
= pt::ptime(gr::date(1970, gr::Jan
, 1))
562 + pt::seconds(long(cd
));
565 // if there are any url-seeds, extract them
566 lazy_entry
const* url_seeds
= torrent_file
.dict_find("url-list");
567 if (url_seeds
&& url_seeds
->type() == lazy_entry::string_t
)
569 m_url_seeds
.push_back(url_seeds
->string_value());
571 else if (url_seeds
&& url_seeds
->type() == lazy_entry::list_t
)
573 for (int i
= 0, end(url_seeds
->list_size()); i
< end
; ++i
)
575 lazy_entry
const* url
= url_seeds
->list_at(i
);
576 if (url
->type() != lazy_entry::string_t
) continue;
577 m_url_seeds
.push_back(url
->string_value());
581 m_comment
= torrent_file
.dict_find_string_value("comment.utf-8");
582 if (m_comment
.empty()) m_comment
= torrent_file
.dict_find_string_value("comment");
584 m_created_by
= torrent_file
.dict_find_string_value("created by.utf-8");
585 if (m_created_by
.empty()) m_created_by
= torrent_file
.dict_find_string_value("created by");
587 lazy_entry
const* info
= torrent_file
.dict_find_dict("info");
590 error
= "missing or invalid 'info' section in torrent file";
593 return parse_info_section(*info
, error
);
596 boost::optional
<pt::ptime
>
597 torrent_info::creation_date() const
599 if (m_creation_date
!= pt::ptime(gr::date(pt::not_a_date_time
)))
601 return boost::optional
<pt::ptime
>(m_creation_date
);
603 return boost::optional
<pt::ptime
>();
606 void torrent_info::add_tracker(std::string
const& url
, int tier
)
608 announce_entry
e(url
);
613 std::sort(m_urls
.begin(), m_urls
.end(), boost::bind
<bool>(std::less
<int>()
614 , bind(&announce_entry::tier
, _1
), bind(&announce_entry::tier
, _2
)));
617 #ifndef TORRENT_NO_DEPRECATE
618 // ------- start deprecation -------
620 void torrent_info::print(std::ostream
& os
) const
623 for (std::vector
<announce_entry
>::const_iterator i
= trackers().begin();
624 i
!= trackers().end(); ++i
)
626 os
<< i
->tier
<< ": " << i
->url
<< "\n";
628 if (!m_comment
.empty())
629 os
<< "comment: " << m_comment
<< "\n";
630 // if (m_creation_date != pt::ptime(gr::date(pt::not_a_date_time)))
631 // os << "creation date: " << to_simple_string(m_creation_date) << "\n";
632 os
<< "private: " << (m_private
?"yes":"no") << "\n";
633 os
<< "number of pieces: " << num_pieces() << "\n";
634 os
<< "piece length: " << piece_length() << "\n";
636 for (file_storage::iterator i
= m_files
.begin(); i
!= m_files
.end(); ++i
)
637 os
<< " " << std::setw(11) << i
->size
<< " " << i
->path
.string() << "\n";
640 // ------- end deprecation -------