2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2003, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ctime>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iterator>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2004-03-05 13:04:47 +01:00
|
|
|
#include <boost/date_time/gregorian/gregorian_types.hpp>
|
|
|
|
#include <boost/filesystem/path.hpp>
|
2004-10-10 02:42:48 +02:00
|
|
|
#include <boost/next_prior.hpp>
|
2004-11-01 00:16:08 +01:00
|
|
|
#include <boost/bind.hpp>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/torrent_info.hpp"
|
|
|
|
#include "libtorrent/bencode.hpp"
|
|
|
|
#include "libtorrent/hasher.hpp"
|
|
|
|
#include "libtorrent/entry.hpp"
|
|
|
|
|
|
|
|
using namespace libtorrent;
|
2004-10-10 02:42:48 +02:00
|
|
|
using namespace boost::filesystem;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
void extract_single_file(const entry& dict, file_entry& target
|
|
|
|
, std::string const& root_dir)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-03-05 13:04:47 +01:00
|
|
|
target.size = dict["length"].integer();
|
2005-05-12 01:03:12 +02:00
|
|
|
target.path = root_dir;
|
2005-08-17 03:35:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
// prefer the name.utf-8
|
|
|
|
// because if it exists, it is more
|
|
|
|
// likely to be correctly encoded
|
|
|
|
|
|
|
|
const entry::list_type* list = 0;
|
|
|
|
if (entry const* p = dict.find_key("path.utf-8"))
|
|
|
|
{
|
|
|
|
list = &p->list();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
list = &dict["path"].list();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (entry::list_type::const_iterator i = list->begin();
|
|
|
|
i != list->end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
if (i->string() != "..")
|
2006-06-18 00:04:25 +02:00
|
|
|
target.path /= i->string();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2005-02-24 07:51:59 +01:00
|
|
|
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
|
|
|
|
"a file with an absolute path: '"
|
|
|
|
+ target.path.native_file_string() + "'");
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
|
|
|
|
, std::string const& root_dir)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-06-18 00:04:25 +02:00
|
|
|
size_type offset = 0;
|
2003-10-23 01:00:57 +02:00
|
|
|
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
|
|
|
|
{
|
2004-01-16 00:57:11 +01:00
|
|
|
target.push_back(file_entry());
|
2005-05-12 01:03:12 +02:00
|
|
|
extract_single_file(*i, target.back(), root_dir);
|
2006-06-18 00:04:25 +02:00
|
|
|
target.back().offset = offset;
|
|
|
|
offset += target.back().size;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
2004-03-05 13:04:47 +01:00
|
|
|
|
2004-10-10 02:42:48 +02:00
|
|
|
void remove_dir(path& p)
|
|
|
|
{
|
|
|
|
assert(p.begin() != p.end());
|
|
|
|
path tmp;
|
|
|
|
for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i)
|
|
|
|
tmp /= *i;
|
|
|
|
p = tmp;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
|
2004-03-05 13:04:47 +01:00
|
|
|
using namespace boost::gregorian;
|
|
|
|
using namespace boost::posix_time;
|
|
|
|
|
2004-03-03 14:47:12 +01:00
|
|
|
// standard constructor that parses a torrent file
|
|
|
|
torrent_info::torrent_info(const entry& torrent_file)
|
2004-03-05 13:04:47 +01:00
|
|
|
: m_creation_date(date(not_a_date_time))
|
2005-05-12 01:03:12 +02:00
|
|
|
, m_multifile(false)
|
2006-08-01 17:27:08 +02:00
|
|
|
, m_private(false)
|
2005-10-16 18:58:41 +02:00
|
|
|
, m_extra_info(entry::dictionary_t)
|
2004-03-03 14:47:12 +01:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
read_torrent_info(torrent_file);
|
|
|
|
}
|
|
|
|
catch(type_error&)
|
|
|
|
{
|
|
|
|
throw invalid_torrent_file();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// constructor used for creating new torrents
|
|
|
|
// will not contain any hashes, comments, creation date
|
|
|
|
// just the necessary to use it with piece manager
|
2005-07-06 02:58:23 +02:00
|
|
|
// used for torrents with no metadata
|
2004-10-10 02:42:48 +02:00
|
|
|
torrent_info::torrent_info(sha1_hash const& info_hash)
|
|
|
|
: m_piece_length(256 * 1024)
|
2004-03-05 13:04:47 +01:00
|
|
|
, m_total_size(0)
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_info_hash(info_hash)
|
2004-10-10 02:42:48 +02:00
|
|
|
, m_name()
|
2004-10-14 03:17:04 +02:00
|
|
|
, m_creation_date(second_clock::universal_time())
|
2005-05-12 01:03:12 +02:00
|
|
|
, m_multifile(false)
|
2005-10-16 18:58:41 +02:00
|
|
|
, m_extra_info(entry::dictionary_t)
|
2004-10-10 02:42:48 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
torrent_info::torrent_info()
|
|
|
|
: m_piece_length(256 * 1024)
|
|
|
|
, m_total_size(0)
|
|
|
|
, m_info_hash(0)
|
|
|
|
, m_name()
|
2004-10-14 03:17:04 +02:00
|
|
|
, m_creation_date(second_clock::universal_time())
|
2005-05-12 01:03:12 +02:00
|
|
|
, m_multifile(false)
|
2005-10-16 18:58:41 +02:00
|
|
|
, m_extra_info(entry::dictionary_t)
|
2004-03-03 14:47:12 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
torrent_info::~torrent_info()
|
|
|
|
{}
|
|
|
|
|
2004-10-10 02:42:48 +02:00
|
|
|
void torrent_info::set_piece_size(int size)
|
|
|
|
{
|
2005-07-10 12:42:00 +02:00
|
|
|
// make sure the size is an even power of 2
|
2004-10-10 02:42:48 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
for (int i = 0; i < 32; ++i)
|
|
|
|
{
|
|
|
|
if (size & (1 << i))
|
|
|
|
{
|
|
|
|
assert((size & ~(1 << i)) == 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
m_piece_length = size;
|
2004-10-16 03:10:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
int num_pieces = static_cast<int>(
|
|
|
|
(m_total_size + m_piece_length - 1) / m_piece_length);
|
|
|
|
int old_num_pieces = static_cast<int>(m_piece_hash.size());
|
|
|
|
|
|
|
|
m_piece_hash.resize(num_pieces);
|
2004-11-21 11:49:02 +01:00
|
|
|
for (int i = old_num_pieces; i < num_pieces; ++i)
|
2004-10-16 03:10:42 +02:00
|
|
|
{
|
2004-11-21 11:49:02 +01:00
|
|
|
m_piece_hash[i].clear();
|
2004-10-16 03:10:42 +02:00
|
|
|
}
|
2004-10-10 02:42:48 +02:00
|
|
|
}
|
2004-03-03 14:47:12 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
void torrent_info::parse_info_section(entry const& info)
|
|
|
|
{
|
|
|
|
// encode the info-field in order to calculate it's sha1-hash
|
|
|
|
std::vector<char> buf;
|
|
|
|
bencode(std::back_inserter(buf), info);
|
|
|
|
hasher h;
|
|
|
|
h.update(&buf[0], (int)buf.size());
|
|
|
|
m_info_hash = h.final();
|
|
|
|
|
|
|
|
// extract piece length
|
|
|
|
m_piece_length = (int)info["piece length"].integer();
|
|
|
|
|
|
|
|
// extract file name (or the directory name if it's a multifile libtorrent)
|
2005-08-17 03:35:37 +02:00
|
|
|
if (entry const* e = info.find_key("name.utf-8"))
|
2005-08-18 00:59:21 +02:00
|
|
|
{ m_name = e->string(); }
|
2005-08-17 03:35:37 +02:00
|
|
|
else
|
2005-08-18 00:59:21 +02:00
|
|
|
{ m_name = info["name"].string(); }
|
2005-08-17 03:35:37 +02:00
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
path tmp = m_name;
|
|
|
|
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
|
|
|
|
"a file with an absolute path: '" + m_name + "'");
|
|
|
|
if (tmp.has_branch_path()) throw std::runtime_error(
|
|
|
|
"torrent contains name with directories: '" + m_name + "'");
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// extract file list
|
|
|
|
entry const* i = info.find_key("files");
|
|
|
|
if (i == 0)
|
|
|
|
{
|
|
|
|
// if there's no list of files, there has to be a length
|
|
|
|
// field.
|
|
|
|
file_entry e;
|
|
|
|
e.path = m_name;
|
2006-06-18 00:04:25 +02:00
|
|
|
e.offset = 0;
|
2004-06-14 01:30:42 +02:00
|
|
|
e.size = info["length"].integer();
|
|
|
|
m_files.push_back(e);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
extract_files(i->list(), m_files, m_name);
|
|
|
|
m_multifile = true;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// calculate total size of all pieces
|
|
|
|
m_total_size = 0;
|
|
|
|
for (std::vector<file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i)
|
|
|
|
m_total_size += i->size;
|
|
|
|
|
|
|
|
// extract sha-1 hashes for all pieces
|
|
|
|
// we want this division to round upwards, that's why we have the
|
|
|
|
// extra addition
|
|
|
|
|
|
|
|
int num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
|
|
|
|
m_piece_hash.resize(num_pieces);
|
|
|
|
const std::string& hash_string = info["pieces"].string();
|
|
|
|
|
|
|
|
if ((int)hash_string.length() != num_pieces * 20)
|
|
|
|
throw invalid_torrent_file();
|
|
|
|
|
|
|
|
for (int i = 0; i < num_pieces; ++i)
|
|
|
|
std::copy(
|
|
|
|
hash_string.begin() + i*20
|
|
|
|
, hash_string.begin() + (i+1)*20
|
|
|
|
, m_piece_hash[i].begin());
|
2005-10-16 18:58:41 +02:00
|
|
|
|
|
|
|
for (entry::dictionary_type::const_iterator i = info.dict().begin()
|
|
|
|
, end(info.dict().end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (i->first == "pieces"
|
|
|
|
|| i->first == "piece length"
|
|
|
|
|| i->first == "length")
|
|
|
|
continue;
|
|
|
|
m_extra_info[i->first] = i->second;
|
|
|
|
}
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
if (info.find_key("private"))
|
|
|
|
{
|
|
|
|
// this key exists, don't care about its value, consider
|
|
|
|
// the torrent private
|
|
|
|
m_private = true;
|
|
|
|
}
|
|
|
|
|
2005-10-16 18:58:41 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
std::vector<char> info_section_buf;
|
|
|
|
entry gen_info_section = create_info_metadata();
|
|
|
|
bencode(std::back_inserter(info_section_buf), gen_info_section);
|
|
|
|
assert(hasher(&info_section_buf[0], info_section_buf.size()).final()
|
|
|
|
== m_info_hash);
|
|
|
|
#endif
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// extracts information from a libtorrent file and fills in the structures in
|
|
|
|
// the torrent object
|
|
|
|
void torrent_info::read_torrent_info(const entry& torrent_file)
|
|
|
|
{
|
|
|
|
// extract the url of the tracker
|
2006-04-25 23:04:48 +02:00
|
|
|
if (entry const* i = torrent_file.find_key("announce-list"))
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-03-17 13:14:44 +01:00
|
|
|
const entry::list_type& l = i->list();
|
2003-10-23 01:00:57 +02:00
|
|
|
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
|
|
|
|
{
|
|
|
|
const entry::list_type& ll = j->list();
|
|
|
|
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
|
|
|
|
{
|
2004-09-12 12:12:16 +02:00
|
|
|
announce_entry e(k->string());
|
2004-03-27 23:02:31 +01:00
|
|
|
e.tier = (int)std::distance(l.begin(), j);
|
2003-10-23 01:00:57 +02:00
|
|
|
m_urls.push_back(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-11 04:01:03 +02:00
|
|
|
if (m_urls.size() == 0)
|
|
|
|
{
|
|
|
|
// the announce-list is empty
|
|
|
|
// fall back to look for announce
|
|
|
|
m_urls.push_back(announce_entry(
|
|
|
|
torrent_file["announce"].string()));
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
// shuffle each tier
|
2006-04-25 23:04:48 +02:00
|
|
|
std::vector<announce_entry>::iterator start = m_urls.begin();
|
|
|
|
std::vector<announce_entry>::iterator stop;
|
2003-10-23 01:00:57 +02:00
|
|
|
int current_tier = m_urls.front().tier;
|
2006-04-25 23:04:48 +02:00
|
|
|
for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
if (stop->tier != current_tier)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
std::random_shuffle(start, stop);
|
|
|
|
start = stop;
|
|
|
|
current_tier = stop->tier;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
std::random_shuffle(start, stop);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
else if (entry const* i = torrent_file.find_key("announce"))
|
|
|
|
{
|
|
|
|
m_urls.push_back(announce_entry(i->string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry const* i = torrent_file.find_key("nodes"))
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-08-01 17:27:08 +02:00
|
|
|
entry::list_type const& list = i->list();
|
|
|
|
for (entry::list_type::const_iterator i(list.begin())
|
|
|
|
, end(list.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (i->type() != entry::list_t) continue;
|
|
|
|
entry::list_type const& l = i->list();
|
|
|
|
entry::list_type::const_iterator iter = l.begin();
|
|
|
|
if (l.size() < 1) continue;
|
|
|
|
std::string const& hostname = iter->string();
|
|
|
|
++iter;
|
|
|
|
int port = 6881;
|
2006-08-27 20:38:30 +02:00
|
|
|
if (l.end() != iter) port = iter->integer();
|
2006-08-01 17:27:08 +02:00
|
|
|
m_nodes.push_back(std::make_pair(hostname, port));
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-24 18:14:03 +01:00
|
|
|
// extract creation date
|
2005-08-11 04:01:03 +02:00
|
|
|
try
|
2004-01-24 18:14:03 +01:00
|
|
|
{
|
2005-08-11 04:01:03 +02:00
|
|
|
m_creation_date = ptime(date(1970, Jan, 1))
|
2005-09-28 20:32:05 +02:00
|
|
|
+ seconds(long(torrent_file["creation date"].integer()));
|
2004-01-24 18:14:03 +01:00
|
|
|
}
|
2005-08-11 04:01:03 +02:00
|
|
|
catch (type_error) {}
|
2004-01-24 18:14:03 +01:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
// if there are any url-seeds, extract them
|
|
|
|
try
|
|
|
|
{
|
|
|
|
entry const& url_seeds = torrent_file["url-list"];
|
|
|
|
if (url_seeds.type() == entry::string_t)
|
|
|
|
{
|
|
|
|
m_url_seeds.push_back(url_seeds.string());
|
|
|
|
}
|
|
|
|
else if (url_seeds.type() == entry::list_t)
|
|
|
|
{
|
|
|
|
entry::list_type const& l = url_seeds.list();
|
|
|
|
for (entry::list_type::const_iterator i = l.begin();
|
|
|
|
i != l.end(); ++i)
|
|
|
|
{
|
|
|
|
m_url_seeds.push_back(i->string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (type_error&) {}
|
|
|
|
|
2004-01-24 18:14:03 +01:00
|
|
|
// extract comment
|
2005-08-18 00:59:21 +02:00
|
|
|
if (entry const* e = torrent_file.find_key("comment.utf-8"))
|
|
|
|
{ m_comment = e->string(); }
|
|
|
|
else if (entry const* e = torrent_file.find_key("comment"))
|
|
|
|
{ m_comment = e->string(); }
|
2005-08-11 04:01:03 +02:00
|
|
|
|
2005-08-18 00:59:21 +02:00
|
|
|
if (entry const* e = torrent_file.find_key("created by.utf-8"))
|
|
|
|
{ m_created_by = e->string(); }
|
|
|
|
else if (entry const* e = torrent_file.find_key("created by"))
|
|
|
|
{ m_created_by = e->string(); }
|
|
|
|
|
2005-08-11 04:01:03 +02:00
|
|
|
parse_info_section(torrent_file["info"]);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-11-18 23:33:50 +01:00
|
|
|
boost::optional<ptime>
|
2004-03-05 13:04:47 +01:00
|
|
|
torrent_info::creation_date() const
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2004-11-18 23:33:50 +01:00
|
|
|
if (m_creation_date != ptime(date(not_a_date_time)))
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2004-11-18 23:33:50 +01:00
|
|
|
return boost::optional<ptime>(m_creation_date);
|
2004-03-05 13:04:47 +01:00
|
|
|
}
|
2004-11-18 23:33:50 +01:00
|
|
|
return boost::optional<ptime>();
|
2004-03-05 13:04:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::add_tracker(std::string const& url, int tier)
|
|
|
|
{
|
2004-09-12 12:12:16 +02:00
|
|
|
announce_entry e(url);
|
2004-03-05 13:04:47 +01:00
|
|
|
e.tier = tier;
|
|
|
|
m_urls.push_back(e);
|
2004-11-01 00:16:08 +01:00
|
|
|
|
|
|
|
using boost::bind;
|
2006-01-10 01:38:52 +01:00
|
|
|
std::sort(m_urls.begin(), m_urls.end(), boost::bind<bool>(std::less<int>()
|
2004-11-01 00:16:08 +01:00
|
|
|
, bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2)));
|
2004-03-05 13:04:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::add_file(boost::filesystem::path file, size_type size)
|
|
|
|
{
|
2004-10-10 02:42:48 +02:00
|
|
|
assert(file.begin() != file.end());
|
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
if (!file.has_branch_path())
|
2004-10-10 02:42:48 +02:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
// you have already added at least one file with a
|
|
|
|
// path to the file (branch_path), which means that
|
|
|
|
// all the other files need to be in the same top
|
|
|
|
// directory as the first file.
|
|
|
|
assert(m_files.empty());
|
|
|
|
assert(!m_multifile);
|
2004-10-10 02:42:48 +02:00
|
|
|
m_name = file.string();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
2005-05-12 01:03:12 +02:00
|
|
|
if (!m_files.empty())
|
2004-10-10 02:42:48 +02:00
|
|
|
assert(m_name == *file.begin());
|
|
|
|
#endif
|
2005-05-12 01:03:12 +02:00
|
|
|
m_multifile = true;
|
2004-10-10 02:42:48 +02:00
|
|
|
m_name = *file.begin();
|
|
|
|
}
|
|
|
|
|
2004-03-05 13:04:47 +01:00
|
|
|
file_entry e;
|
|
|
|
e.path = file;
|
|
|
|
e.size = size;
|
|
|
|
m_files.push_back(e);
|
|
|
|
|
|
|
|
m_total_size += size;
|
|
|
|
|
2004-03-23 23:58:18 +01:00
|
|
|
int num_pieces = static_cast<int>(
|
|
|
|
(m_total_size + m_piece_length - 1) / m_piece_length);
|
2004-03-07 21:50:56 +01:00
|
|
|
int old_num_pieces = static_cast<int>(m_piece_hash.size());
|
2004-03-05 13:04:47 +01:00
|
|
|
|
|
|
|
m_piece_hash.resize(num_pieces);
|
|
|
|
for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces;
|
2005-05-12 01:03:12 +02:00
|
|
|
i != m_piece_hash.end(); ++i)
|
2004-03-05 13:04:47 +01:00
|
|
|
{
|
|
|
|
i->clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
void torrent_info::add_url_seed(std::string const& url)
|
|
|
|
{
|
|
|
|
m_url_seeds.push_back(url);
|
|
|
|
}
|
|
|
|
|
2004-03-23 23:58:18 +01:00
|
|
|
void torrent_info::set_comment(char const* str)
|
|
|
|
{
|
|
|
|
m_comment = str;
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::set_creator(char const* str)
|
|
|
|
{
|
|
|
|
m_created_by = str;
|
|
|
|
}
|
|
|
|
|
2004-07-24 13:54:17 +02:00
|
|
|
entry torrent_info::create_info_metadata() const
|
2004-03-05 13:04:47 +01:00
|
|
|
{
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
// you have to add files to the torrent first
|
2004-10-10 02:42:48 +02:00
|
|
|
assert(!m_files.empty());
|
2005-05-12 01:03:12 +02:00
|
|
|
|
2005-10-16 18:58:41 +02:00
|
|
|
entry info(m_extra_info);
|
|
|
|
|
|
|
|
if (!info.find_key("name"))
|
|
|
|
info["name"] = m_name;
|
2004-10-10 02:42:48 +02:00
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
if (!m_multifile)
|
2004-03-05 13:04:47 +01:00
|
|
|
{
|
2004-10-14 03:17:04 +02:00
|
|
|
info["length"] = m_files.front().size;
|
2004-03-05 13:04:47 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-10-16 18:58:41 +02:00
|
|
|
if (!info.find_key("files"))
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2005-10-16 18:58:41 +02:00
|
|
|
entry& files = info["files"];
|
|
|
|
files = entry(entry::list_t);
|
|
|
|
|
|
|
|
for (std::vector<file_entry>::const_iterator i = m_files.begin();
|
|
|
|
i != m_files.end(); ++i)
|
2004-03-05 13:04:47 +01:00
|
|
|
{
|
2005-10-16 18:58:41 +02:00
|
|
|
files.list().push_back(entry(entry::dictionary_t));
|
|
|
|
entry& file_e = files.list().back();
|
|
|
|
file_e["length"] = i->size;
|
|
|
|
entry& path_e = file_e["path"];
|
|
|
|
path_e = entry(entry::list_t);
|
|
|
|
|
|
|
|
fs::path const& file_path(i->path);
|
|
|
|
assert(file_path.has_branch_path());
|
|
|
|
assert(*file_path.begin() == m_name);
|
|
|
|
|
|
|
|
for (fs::path::iterator j = boost::next(file_path.begin());
|
|
|
|
j != file_path.end(); ++j)
|
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
path_e.list().push_back(entry(*j));
|
2005-10-16 18:58:41 +02:00
|
|
|
}
|
2004-03-05 13:04:47 +01:00
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
}
|
2004-03-05 13:04:47 +01:00
|
|
|
|
|
|
|
info["piece length"] = piece_length();
|
|
|
|
entry& pieces = info["pieces"];
|
|
|
|
pieces = entry(entry::string_t);
|
|
|
|
|
|
|
|
std::string& p = pieces.string();
|
|
|
|
|
|
|
|
for (std::vector<sha1_hash>::const_iterator i = m_piece_hash.begin();
|
2005-10-16 18:58:41 +02:00
|
|
|
i != m_piece_hash.end(); ++i)
|
2004-03-05 13:04:47 +01:00
|
|
|
{
|
|
|
|
p.append((char*)i->begin(), (char*)i->end());
|
|
|
|
}
|
2004-10-13 17:36:44 +02:00
|
|
|
|
2004-07-24 13:54:17 +02:00
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2005-10-16 18:58:41 +02:00
|
|
|
entry torrent_info::create_torrent() const
|
2004-07-24 13:54:17 +02:00
|
|
|
{
|
|
|
|
assert(m_piece_length > 0);
|
|
|
|
|
|
|
|
using namespace boost::gregorian;
|
|
|
|
using namespace boost::posix_time;
|
|
|
|
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
|
|
entry dict(entry::dictionary_t);
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
|
2004-07-24 13:54:17 +02:00
|
|
|
{
|
|
|
|
// TODO: throw something here
|
|
|
|
// throw
|
|
|
|
return entry();
|
|
|
|
}
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
if (m_private) dict["private"] = 1;
|
|
|
|
|
|
|
|
if (!m_urls.empty())
|
|
|
|
dict["announce"] = m_urls.front().url;
|
|
|
|
|
|
|
|
if (!m_nodes.empty())
|
|
|
|
{
|
|
|
|
entry& nodes = dict["nodes"];
|
|
|
|
nodes = entry(entry::list_t);
|
|
|
|
entry::list_type& nodes_list = nodes.list();
|
|
|
|
for (nodes_t::const_iterator i = m_nodes.begin()
|
|
|
|
, end(m_nodes.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
entry::list_type node;
|
|
|
|
node.push_back(entry(i->first));
|
|
|
|
node.push_back(entry(i->second));
|
|
|
|
nodes_list.push_back(entry(node));
|
|
|
|
}
|
|
|
|
}
|
2004-11-01 00:16:08 +01:00
|
|
|
|
|
|
|
if (m_urls.size() > 1)
|
|
|
|
{
|
|
|
|
entry trackers(entry::list_t);
|
|
|
|
entry tier(entry::list_t);
|
|
|
|
int current_tier = m_urls.front().tier;
|
|
|
|
for (std::vector<announce_entry>::const_iterator i = m_urls.begin();
|
|
|
|
i != m_urls.end(); ++i)
|
|
|
|
{
|
|
|
|
if (i->tier != current_tier)
|
|
|
|
{
|
|
|
|
current_tier = i->tier;
|
|
|
|
trackers.list().push_back(tier);
|
|
|
|
tier.list().clear();
|
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
tier.list().push_back(entry(i->url));
|
2004-11-01 00:16:08 +01:00
|
|
|
}
|
|
|
|
trackers.list().push_back(tier);
|
|
|
|
dict["announce-list"] = trackers;
|
|
|
|
}
|
|
|
|
|
2004-07-24 13:54:17 +02:00
|
|
|
if (!m_comment.empty())
|
|
|
|
dict["comment"] = m_comment;
|
|
|
|
|
|
|
|
dict["creation date"] =
|
2004-11-01 00:16:08 +01:00
|
|
|
(m_creation_date - ptime(date(1970, Jan, 1))).total_seconds();
|
2004-07-24 13:54:17 +02:00
|
|
|
|
|
|
|
if (!m_created_by.empty())
|
|
|
|
dict["created by"] = m_created_by;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
if (!m_url_seeds.empty())
|
|
|
|
{
|
|
|
|
if (m_url_seeds.size() == 1)
|
|
|
|
{
|
|
|
|
dict["url-list"] = m_url_seeds.front();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entry& list = dict["url-list"];
|
|
|
|
list = entry(entry::list_t);
|
|
|
|
for (std::vector<std::string>::const_iterator i
|
|
|
|
= m_url_seeds.begin(); i != m_url_seeds.end(); ++i)
|
|
|
|
{
|
|
|
|
list.list().push_back(entry(*i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-07-24 13:54:17 +02:00
|
|
|
|
|
|
|
dict["info"] = create_info_metadata();
|
2004-10-13 17:36:44 +02:00
|
|
|
|
2005-07-06 02:58:23 +02:00
|
|
|
entry const& info_section = dict["info"];
|
|
|
|
std::vector<char> buf;
|
|
|
|
bencode(std::back_inserter(buf), info_section);
|
|
|
|
m_info_hash = hasher(&buf[0], buf.size()).final();
|
|
|
|
|
2004-03-05 13:04:47 +01:00
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::set_hash(int index, const sha1_hash& h)
|
|
|
|
{
|
|
|
|
assert(index >= 0);
|
2004-03-07 21:50:56 +01:00
|
|
|
assert(index < (int)m_piece_hash.size());
|
2004-03-05 13:04:47 +01:00
|
|
|
m_piece_hash[index] = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::convert_file_names()
|
|
|
|
{
|
|
|
|
assert(false);
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void torrent_info::print(std::ostream& os) const
|
|
|
|
{
|
|
|
|
os << "trackers:\n";
|
|
|
|
for (std::vector<announce_entry>::const_iterator i = trackers().begin();
|
2006-08-06 18:36:00 +02:00
|
|
|
i != trackers().end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
os << i->tier << ": " << i->url << "\n";
|
|
|
|
}
|
2003-11-20 20:58:29 +01:00
|
|
|
if (!m_comment.empty())
|
|
|
|
os << "comment: " << m_comment << "\n";
|
2004-11-18 23:33:50 +01:00
|
|
|
if (m_creation_date != ptime(date(not_a_date_time)))
|
|
|
|
os << "creation date: " << to_simple_string(m_creation_date) << "\n";
|
2006-08-06 18:36:00 +02:00
|
|
|
os << "private: " << (m_private?"yes":"no") << "\n";
|
2003-10-23 01:00:57 +02:00
|
|
|
os << "number of pieces: " << num_pieces() << "\n";
|
|
|
|
os << "piece length: " << piece_length() << "\n";
|
|
|
|
os << "files:\n";
|
|
|
|
for (file_iterator i = begin_files(); i != end_files(); ++i)
|
2004-03-05 13:04:47 +01:00
|
|
|
os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n";
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-03-07 21:50:56 +01:00
|
|
|
size_type torrent_info::piece_size(int index) const
|
|
|
|
{
|
|
|
|
assert(index >= 0 && index < num_pieces());
|
|
|
|
if (index == num_pieces()-1)
|
|
|
|
{
|
|
|
|
size_type size = total_size()
|
|
|
|
- (num_pieces() - 1) * piece_length();
|
|
|
|
assert(size > 0);
|
|
|
|
assert(size <= piece_length());
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return piece_length();
|
|
|
|
}
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
void torrent_info::add_node(std::pair<std::string, int> const& node)
|
|
|
|
{
|
|
|
|
m_nodes.push_back(node);
|
|
|
|
}
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
|
|
|
|
, int size) const
|
|
|
|
{
|
|
|
|
assert(num_files() > 0);
|
|
|
|
std::vector<file_slice> ret;
|
|
|
|
|
|
|
|
size_type start = piece * (size_type)m_piece_length + offset;
|
2006-07-26 12:21:25 +02:00
|
|
|
assert(start + size <= m_total_size);
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
// find the file iterator and file offset
|
|
|
|
// TODO: make a vector that can map piece -> file index in O(1)
|
|
|
|
size_type file_offset = start;
|
|
|
|
std::vector<file_entry>::const_iterator file_iter;
|
|
|
|
|
|
|
|
int counter = 0;
|
|
|
|
for (file_iter = begin_files();; ++counter, ++file_iter)
|
|
|
|
{
|
|
|
|
assert(file_iter != end_files());
|
|
|
|
if (file_offset < file_iter->size)
|
|
|
|
{
|
|
|
|
file_slice f;
|
|
|
|
f.file_index = counter;
|
|
|
|
f.offset = file_offset;
|
|
|
|
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
|
|
|
|
size -= f.size;
|
|
|
|
file_offset += f.size;
|
|
|
|
ret.push_back(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(size >= 0);
|
|
|
|
if (size <= 0) break;
|
|
|
|
|
|
|
|
file_offset -= file_iter->size;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
peer_request torrent_info::map_file(int file_index, size_type file_offset
|
|
|
|
, int size) const
|
|
|
|
{
|
2006-06-18 00:04:25 +02:00
|
|
|
assert(file_index < (int)m_files.size());
|
|
|
|
assert(file_index >= 0);
|
|
|
|
size_type offset = file_offset + m_files[file_index].offset;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
peer_request ret;
|
|
|
|
ret.piece = offset / piece_length();
|
|
|
|
ret.start = offset - ret.piece * piece_length();
|
|
|
|
ret.length = size;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2003-10-26 04:18:17 +01:00
|
|
|
}
|