-
The destructor of session will notify all trackers that our torrents has been shut down.
+
The destructor of session will notify all trackers that our torrents have been shut down.
If some trackers are down, they will timout. All this before the destructor of session
returns. So, it's adviced that any kind of interface (such as windows) are closed before
destructing the sessoin object. Because it can take a few second for it to finish. The
@@ -371,7 +370,7 @@ timeout can be set with set_http_settings(
torrent_handle add_torrent(
- torrent_info const& t
+ entry const& e
, boost::filesystem::path const& save_path
, entry const& resume_data = entry());
@@ -420,7 +419,7 @@ of upload rate.
session_status status() const;
-
status() returns session wide statistics and status. The session_status
+
status() returns session wide-statistics and status. The session_status
struct has the following members:
struct session_status
@@ -442,7 +441,7 @@ struct session_status
int num_peers;
};
-
has_incoming_connections is false as long as no incoming connections has been
+
has_incoming_connections is false as long as no incoming connections have been
established on the listening socket. Every time you change the listen port, this will
be reset to false.
upload_rate, download_rate, payload_download_rate and payload_upload_rate
@@ -1408,6 +1407,15 @@ sure not to clash with anybody else. Here are some taken id's:
'MT' |
Moonlight Torrent |
+
'TS' |
+Torrent Storm |
+
+
'SS' |
+Swarm Scope |
+
+
'XT' |
+Xan Torrent |
+
The major, minor, revision and tag parameters are used to identify the
@@ -1540,14 +1548,14 @@ public:
alert(severity_t severity, const std::string& msg);
virtual ~alert();
- const std::string& msg() const;
+ std::string const& msg() const;
severity_t severity() const;
virtual std::auto_ptr<alert> clone() const = 0;
};
This means that all alerts have at least a string describing it. They also
-have a severity leve that can be used to sort them or present them to the
+have a severity level that can be used to sort them or present them to the
user in different ways.
The specific alerts, that all derives from alert, are:
@@ -1899,8 +1907,7 @@ int main(int argc, char* argv[])
std::ifstream in(argv[1], std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
- torrent_info t(e);
- s.add_torrent(t, "");
+ s.add_torrent(e, "");
// wait for the user to end
char a;
diff --git a/docs/manual.rst b/docs/manual.rst
index fb67a424f..c6133de21 100755
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -218,7 +218,7 @@ The ``session`` class has the following synopsis::
, const char* listen_interface = 0);
torrent_handle add_torrent(
- torrent_info const& t
+ entry const& e
, boost::filesystem::path const& save_path
, entry const& resume_data = entry());
@@ -269,7 +269,7 @@ the parameters, see ``listen_on()`` function.
~session()
----------
-The destructor of session will notify all trackers that our torrents has been shut down.
+The destructor of session will notify all trackers that our torrents have been shut down.
If some trackers are down, they will timout. All this before the destructor of session
returns. So, it's adviced that any kind of interface (such as windows) are closed before
destructing the sessoin object. Because it can take a few second for it to finish. The
@@ -282,7 +282,7 @@ add_torrent()
::
torrent_handle add_torrent(
- torrent_info const& t
+ entry const& e
, boost::filesystem::path const& save_path
, entry const& resume_data = entry());
@@ -336,7 +336,7 @@ status()
session_status status() const;
-``status()`` returns session wide statistics and status. The ``session_status``
+``status()`` returns session wide-statistics and status. The ``session_status``
struct has the following members::
struct session_status
@@ -358,7 +358,7 @@ struct has the following members::
int num_peers;
};
-``has_incoming_connections`` is false as long as no incoming connections has been
+``has_incoming_connections`` is false as long as no incoming connections have been
established on the listening socket. Every time you change the listen port, this will
be reset to false.
@@ -1407,6 +1407,12 @@ sure not to clash with anybody else. Here are some taken id's:
+----------+-----------------------+
| 'MT' | Moonlight Torrent |
+----------+-----------------------+
+| 'TS' | Torrent Storm |
++----------+-----------------------+
+| 'SS' | Swarm Scope |
++----------+-----------------------+
+| 'XT' | Xan Torrent |
++----------+-----------------------+
The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the
@@ -1555,14 +1561,14 @@ is its synopsis::
alert(severity_t severity, const std::string& msg);
virtual ~alert();
- const std::string& msg() const;
+ std::string const& msg() const;
severity_t severity() const;
virtual std::auto_ptr
clone() const = 0;
};
This means that all alerts have at least a string describing it. They also
-have a severity leve that can be used to sort them or present them to the
+have a severity level that can be used to sort them or present them to the
user in different ways.
The specific alerts, that all derives from ``alert``, are:
@@ -1967,8 +1973,7 @@ This is a simple client. It doesn't have much output to keep it simple::
std::ifstream in(argv[1], std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
entry e = bdecode(std::istream_iterator(in), std::istream_iterator());
- torrent_info t(e);
- s.add_torrent(t, "");
+ s.add_torrent(e, "");
// wait for the user to end
char a;
diff --git a/examples/client_test.cpp b/examples/client_test.cpp
index a74803afb..9474773ad 100755
--- a/examples/client_test.cpp
+++ b/examples/client_test.cpp
@@ -226,7 +226,7 @@ int main(int argc, char* argv[])
return 1;
}
- namespace fs = boost::filesystem;
+ namespace fs = boost::filesystem;
fs::path::default_name_check(fs::no_check);
http_settings settings;
@@ -243,8 +243,8 @@ int main(int argc, char* argv[])
std::vector handles;
session ses(fingerprint("LT", 0, 1, 0, 0));
- ses.listen_on(std::make_pair(100, 110));
- ses.set_upload_rate_limit(50000);
+ ses.listen_on(std::make_pair(6881, 6889));
+ ses.set_upload_rate_limit(100000);
// ses.set_download_rate_limit(50000);
ses.set_http_settings(settings);
ses.set_severity_level(alert::debug);
@@ -254,13 +254,26 @@ int main(int argc, char* argv[])
{
try
{
+ boost::filesystem::path save_path("");
+
+ if (std::string(argv[i+1]).substr(0, 7) == "http://")
+ {
+ sha1_hash info_hash = boost::lexical_cast(argv[i+2]);
+
+ handles.push_back(ses.add_torrent(argv[i+1], info_hash, save_path));
+ handles.back().set_max_connections(60);
+ handles.back().set_max_uploads(7);
+ handles.back().set_ratio(1.02f);
+ ++i;
+
+ continue;
+ }
std::ifstream in(argv[i+1], std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
entry e = bdecode(std::istream_iterator(in), std::istream_iterator());
torrent_info t(e);
t.print(std::cout);
- boost::filesystem::path save_path("");
entry resume_data;
try
{
@@ -275,9 +288,9 @@ int main(int argc, char* argv[])
catch (invalid_encoding&) {}
catch (boost::filesystem::filesystem_error&) {}
- handles.push_back(ses.add_torrent(t, save_path, resume_data));
- handles.back().set_max_connections(60);
- handles.back().set_max_uploads(7);
+ handles.push_back(ses.add_torrent(e, save_path, resume_data));
+ handles.back().set_max_connections(200);
+ handles.back().set_max_uploads(20);
handles.back().set_ratio(1.02f);
}
catch (std::exception& e)
@@ -286,6 +299,8 @@ int main(int argc, char* argv[])
}
}
+ if (handles.empty()) return 1;
+
std::vector peers;
std::vector queue;
@@ -301,6 +316,8 @@ int main(int argc, char* argv[])
++i)
{
torrent_handle h = *i;
+ if (!h.get_torrent_info().is_valid()) continue;
+
entry data = h.write_resume_data();
std::stringstream s;
s << h.get_torrent_info().name() << ".fastresume";
@@ -396,6 +413,9 @@ int main(int argc, char* argv[])
case torrent_status::connecting_to_tracker:
out << "connecting to tracker ";
break;
+ case torrent_status::downloading_metadata:
+ out << "downloading metadata ";
+ break;
case torrent_status::downloading:
out << "downloading ";
break;
@@ -419,6 +439,7 @@ int main(int argc, char* argv[])
<< "u:" << add_suffix(s.upload_rate) << "/s "
<< "(" << add_suffix(s.total_upload) << ") "
<< "ratio: " << ratio(s.total_payload_download, s.total_payload_upload) << "\n";
+ out << "info-hash: " << i->info_hash() << "\n";
boost::posix_time::time_duration t = s.next_announce;
out << "next announce: " << boost::posix_time::to_simple_string(t) << "\n";
diff --git a/examples/simple_client.cpp b/examples/simple_client.cpp
index 9a25c5af6..0138a447e 100755
--- a/examples/simple_client.cpp
+++ b/examples/simple_client.cpp
@@ -62,8 +62,7 @@ int main(int argc, char* argv[])
std::ifstream in(argv[1], std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
entry e = bdecode(std::istream_iterator(in), std::istream_iterator());
- torrent_info t(e);
- s.add_torrent(t, "");
+ s.add_torrent(e, "");
// wait for the user to end
char a;
diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp
index a844f071c..4aa0e7276 100755
--- a/include/libtorrent/peer_connection.hpp
+++ b/include/libtorrent/peer_connection.hpp
@@ -105,6 +105,12 @@ namespace libtorrent
, selector& sel
, boost::shared_ptr s);
+ // this function is called once the torrent associated
+ // with this peer connection has retrieved the meta-
+ // data. If the torrent was spawned with metadata
+ // this is called from the constructor.
+ void init();
+
~peer_connection();
// this adds an announcement in the announcement queue
@@ -205,6 +211,18 @@ namespace libtorrent
boost::shared_ptr m_logger;
#endif
+ enum extension_index
+ {
+ extended_chat_message,
+ extended_metadata_message,
+ extended_peer_exchange_message,
+ extended_listen_port_message,
+ num_supported_extensions
+ };
+
+ bool supports_extension(extension_index ex) const
+ { return m_extension_messages[ex] != -1; }
+
// the message handlers are called
// each time a recv() returns some new
// data, the last time it will be called
@@ -225,6 +243,11 @@ namespace libtorrent
void on_extension_list(int received);
void on_extended(int received);
+ void on_chat();
+ void on_metadata();
+ void on_peer_exchange();
+ void on_listen_port();
+
typedef void (peer_connection::*message_handler)(int received);
// the following functions appends messages
@@ -240,6 +263,8 @@ namespace libtorrent
void send_handshake();
void send_extensions();
void send_chat_message(const std::string& msg);
+ void send_metadata(int start, int size);
+ void send_metadata_request(int start, int size);
// how much bandwidth we're using, how much we want,
// and how much we are allowed to use.
@@ -249,10 +274,16 @@ namespace libtorrent
private:
+#ifndef NDEBUG
void check_invariant() const;
+#endif
bool dispatch_message(int received);
+ // if we don't have all metadata
+ // this function will request a part of it
+ // from this peer
+ void request_metadata();
// this is called each time this peer generates some
// data to be sent. It will add this socket to
@@ -336,10 +367,6 @@ namespace libtorrent
selector& m_selector;
boost::shared_ptr m_socket;
- // upload bandwidth used this second.
- // Must not exceed m_ul_bandwidth_quota.given.
-// int m_ul_bandwidth_quota_used;
-
// this is the torrent this connection is
// associated with. If the connection is an
// incoming conncetion, this is set to zero
@@ -443,11 +470,6 @@ namespace libtorrent
// considered a bad peer and will be banned.
int m_trust_points;
- enum extension_index
- {
- extended_chat_message,
- num_supported_extensions
- };
static const char* extension_names[num_supported_extensions];
int m_extension_messages[num_supported_extensions];
@@ -475,6 +497,10 @@ namespace libtorrent
// the time when we sent a not_interested message to
// this peer the last time.
boost::posix_time::ptime m_became_uninteresting;
+
+ // this is set to the current time each time we get a
+ // "I don't have metadata" message.
+ boost::posix_time::ptime m_no_metadata;
};
}
diff --git a/include/libtorrent/peer_id.hpp b/include/libtorrent/peer_id.hpp
index b06534536..11d88591f 100755
--- a/include/libtorrent/peer_id.hpp
+++ b/include/libtorrent/peer_id.hpp
@@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include
#include
#include
+#include
#include
namespace libtorrent
@@ -43,10 +44,17 @@ namespace libtorrent
class big_number
{
+ // private type
+ struct private_pointer {};
// the number of bytes of the number
enum { number_size = 20 };
public:
+ big_number() {}
+
+ // when initialized with 0
+ big_number(private_pointer*) { clear(); }
+
void clear()
{
std::fill(m_number,m_number+number_size,0);
@@ -101,7 +109,7 @@ namespace libtorrent
typedef big_number peer_id;
typedef big_number sha1_hash;
- inline std::ostream& operator<<(std::ostream& os, const big_number& peer)
+ inline std::ostream& operator<<(std::ostream& os, big_number const& peer)
{
for (big_number::const_iterator i = peer.begin();
i != peer.end();
@@ -114,6 +122,19 @@ namespace libtorrent
return os;
}
+ inline std::istream& operator>>(std::istream& is, big_number& peer)
+ {
+ for (big_number::iterator i = peer.begin();
+ i != peer.end(); ++i)
+ {
+ char c[2];
+ is >> c[0] >> c[1];
+ *i = ((std::isdigit(c[0])?c[0]-'0':c[0]-'a'+10) << 4)
+ + (std::isdigit(c[1])?c[1]-'0':c[1]-'a'+10);
+ }
+ return is;
+ }
+
}
#endif // TORRENT_PEER_ID_HPP_INCLUDED
diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp
index edc6381f5..448f59ed7 100755
--- a/include/libtorrent/session.hpp
+++ b/include/libtorrent/session.hpp
@@ -289,9 +289,15 @@ namespace libtorrent
// all torrent_handles must be destructed before the session is destructed!
torrent_handle add_torrent(
- const torrent_info& ti
- , const boost::filesystem::path& save_path
- , const entry& resume_data = entry());
+ entry const& metadata
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data = entry());
+
+ torrent_handle add_torrent(
+ char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data = entry());
session_status status() const;
diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp
index b079e0d0a..65cb0dd26 100755
--- a/include/libtorrent/torrent.hpp
+++ b/include/libtorrent/torrent.hpp
@@ -56,7 +56,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/policy.hpp"
-#include "libtorrent/storage.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/stat.hpp"
#include "libtorrent/alert.hpp"
@@ -68,6 +67,8 @@ namespace libtorrent
struct logger;
#endif
+ class piece_manager;
+
std::string escape_string(const char* str, int len);
std::string unescape_string(std::string const& s);
@@ -75,6 +76,7 @@ namespace libtorrent
namespace detail
{
struct session_impl;
+ struct piece_checker_data;
}
// a torrent is a class that holds information
@@ -86,12 +88,25 @@ namespace libtorrent
torrent(
detail::session_impl& ses
- , const torrent_info& torrent_file
- , const boost::filesystem::path& save_path
+ , entry const& metadata
+ , boost::filesystem::path const& save_path
+ , address const& net_interface);
+
+ // used with metadata-less torrents
+ // (the metadata is downloaded from the peers)
+ torrent(
+ detail::session_impl& ses
+ , char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
, address const& net_interface);
~torrent();
+ // this is called when the torrent has metadata.
+ // it will initialize the storage and the piece-picker
+ void init();
+
// this will flag the torrent as aborted. The main
// loop in session_impl will check for this state
// on all torrents once every second, and take
@@ -219,19 +234,23 @@ namespace libtorrent
const std::vector& pieces() const
{ return m_have_pieces; }
+ int num_pieces() const { return m_num_pieces; }
+
// when we get a have- or bitfield- messages, this is called for every
// piece a peer has gained.
void peer_has(int index)
{
+ assert(m_picker.get());
assert(index >= 0 && index < (signed)m_have_pieces.size());
- m_picker.inc_refcount(index);
+ m_picker->inc_refcount(index);
}
// when peer disconnects, this is called for every piece it had
void peer_lost(int index)
{
+ assert(m_picker.get());
assert(index >= 0 && index < (signed)m_have_pieces.size());
- m_picker.dec_refcount(index);
+ m_picker->dec_refcount(index);
}
int block_size() const { return m_block_size; }
@@ -265,14 +284,20 @@ namespace libtorrent
}
bool is_seed() const
- { return m_num_pieces == m_torrent_file.num_pieces(); }
+ {
+ return valid_metadata()
+ && m_num_pieces == m_torrent_file.num_pieces();
+ }
- boost::filesystem::path save_path() const
- { return m_storage.save_path(); }
+ boost::filesystem::path save_path() const;
alert_manager& alerts() const;
- piece_picker& picker() { return m_picker; }
+ piece_picker& picker()
+ {
+ assert(m_picker.get());
+ return *m_picker;
+ }
policy& get_policy() { return *m_policy; }
- piece_manager& filesystem() { return m_storage; }
+ piece_manager& filesystem();
torrent_info const& torrent_file() const { return m_torrent_file; }
torrent_handle get_handle() const;
@@ -282,7 +307,7 @@ namespace libtorrent
logger* spawn_logger(const char* title);
virtual void debug_log(const std::string& line);
- void check_invariant();
+ void check_invariant() const;
#endif
// --------------------------------------------
@@ -300,14 +325,16 @@ namespace libtorrent
void set_upload_limit(int limit);
void set_download_limit(int limit);
+ bool valid_metadata() const { return m_storage.get() != 0; }
+ std::vector const& metadata() const { return m_metadata; }
+
+ bool received_metadata(char const* buf, int size, int offset, int total_size);
+
private:
void try_next_tracker();
- // the size of a request block
- // each piece is divided into these
- // blocks when requested
- int m_block_size;
+ torrent_info m_torrent_file;
// is set to true when the torrent has
// been aborted.
@@ -320,8 +347,15 @@ namespace libtorrent
void parse_response(const entry& e, std::vector& peer_list);
- torrent_info m_torrent_file;
- piece_manager m_storage;
+ // the size of a request block
+ // each piece is divided into these
+ // blocks when requested
+ int m_block_size;
+
+ // if this pointer is 0, the peer_connection is in
+ // a state where the metadata hasn't been
+ // received yet.
+ std::auto_ptr m_storage;
// the time of next tracker request
boost::posix_time::ptime m_next_request;
@@ -347,7 +381,7 @@ namespace libtorrent
// this torrent belongs to.
detail::session_impl& m_ses;
- piece_picker m_picker;
+ std::auto_ptr m_picker;
// this is an index into m_torrent_file.trackers()
int m_last_working_tracker;
@@ -399,6 +433,20 @@ namespace libtorrent
// can upload per second
int m_upload_bandwidth_limit;
int m_download_bandwidth_limit;
+
+ // this buffer is filled with the info-section of
+ // the metadata file while downloading it from
+ // peers, and while sending it.
+ std::vector m_metadata;
+
+ // this is a bitfield of size 256, each bit represents
+ // a piece of the metadata. It is set to one if we
+ // have that piece. This vector may be empty
+ // (size 0) if we haven't received any metadata
+ // or if we already have all metadata
+ std::vector m_have_metadata;
+
+ boost::filesystem::path m_save_path;
};
inline boost::posix_time::ptime torrent::next_announce() const
diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp
index 185b806a7..6b6978569 100755
--- a/include/libtorrent/torrent_handle.hpp
+++ b/include/libtorrent/torrent_handle.hpp
@@ -92,6 +92,7 @@ namespace libtorrent
queued_for_checking,
checking_files,
connecting_to_tracker,
+ downloading_metadata,
downloading,
seeding
};
diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp
index 3dc9a6eb3..1d184a020 100755
--- a/include/libtorrent/torrent_info.hpp
+++ b/include/libtorrent/torrent_info.hpp
@@ -86,7 +86,7 @@ namespace libtorrent
public:
torrent_info(const entry& torrent_file);
- torrent_info(int piece_size, const char* name);
+ torrent_info(int piece_size, const char* name, sha1_hash const& info_hash = sha1_hash(0));
entry create_torrent() const;
void set_comment(char const* str);
@@ -104,8 +104,10 @@ namespace libtorrent
reverse_file_iterator rbegin_files() const { return m_files.rbegin(); }
reverse_file_iterator rend_files() const { return m_files.rend(); }
- int num_files() const { return (int)m_files.size(); }
- const file_entry& file_at(int index) const { assert(index >= 0 && index < (int)m_files.size()); return m_files[index]; }
+ int num_files() const
+ { assert(m_piece_length > 0); return (int)m_files.size(); }
+ const file_entry& file_at(int index) const
+ { assert(index >= 0 && index < (int)m_files.size()); return m_files[index]; }
const std::vector& trackers() const { return m_urls; }
@@ -114,12 +116,13 @@ namespace libtorrent
// the begining) and return the new index to the tracker.
int prioritize_tracker(int index);
- size_type total_size() const { return m_total_size; }
- size_type piece_length() const { return m_piece_length; }
- int num_pieces() const { return (int)m_piece_hash.size(); }
+ size_type total_size() const { assert(m_piece_length > 0); return m_total_size; }
+ size_type piece_length() const { assert(m_piece_length > 0); return m_piece_length; }
+ int num_pieces() const { assert(m_piece_length > 0); return (int)m_piece_hash.size(); }
const sha1_hash& info_hash() const { return m_info_hash; }
- const std::string& name() const { return m_name; }
+ const std::string& name() const { assert(m_piece_length > 0); return m_name; }
void print(std::ostream& os) const;
+ bool is_valid() const { return m_piece_length > 0; }
void convert_file_names();
@@ -138,6 +141,8 @@ namespace libtorrent
const std::string& comment() const
{ return m_comment; }
+ void parse_info_section(entry const& e);
+
private:
void read_torrent_info(const entry& libtorrent);
@@ -146,6 +151,8 @@ namespace libtorrent
std::vector m_urls;
// the length of one piece
+ // if this is 0, the torrent_info is
+ // in an uninitialized state
size_type m_piece_length;
// the sha-1 hashes of each piece
diff --git a/src/escape_string.cpp b/src/escape_string.cpp
index 53dcf16de..4e70d5f2d 100755
--- a/src/escape_string.cpp
+++ b/src/escape_string.cpp
@@ -54,7 +54,7 @@ namespace libtorrent
{
if(*i == '+')
{
- ret+=' ';
+ ret += ' ';
}
else if (*i != '%')
{
@@ -67,9 +67,9 @@ namespace libtorrent
throw std::runtime_error("invalid escaped string");
int high;
- if(*i >= '0' && *i <= '9') high=*i - '0';
- else if(*i >= 'A' && *i <= 'F') high=*i + 10 - 'A';
- else if(*i >= 'a' && *i <= 'f') high=*i + 10 - 'a';
+ if(*i >= '0' && *i <= '9') high = *i - '0';
+ else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A';
+ else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a';
else throw std::runtime_error("invalid escaped string");
++i;
@@ -77,9 +77,9 @@ namespace libtorrent
throw std::runtime_error("invalid escaped string");
int low;
- if(*i >= '0' && *i <= '9') low=*i - '0';
- else if(*i >= 'A' && *i <= 'F') low=*i + 10 - 'A';
- else if(*i >= 'a' && *i <= 'f') low=*i + 10 - 'a';
+ if(*i >= '0' && *i <= '9') low = *i - '0';
+ else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A';
+ else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a';
else throw std::runtime_error("invalid escaped string");
ret += char(high * 16 + low);
diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp
index ad862da8a..149ed9976 100755
--- a/src/http_tracker_connection.cpp
+++ b/src/http_tracker_connection.cpp
@@ -186,7 +186,13 @@ namespace libtorrent
}
m_send_buffer += "\r\n\r\n";
#ifndef NDEBUG
- if (c) c->debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
+ if (c)
+ {
+ c->debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
+ std::stringstream info_hash_str;
+ info_hash_str << req.info_hash;
+ c->debug_log("info_hash: " + info_hash_str.str() + "\n");
+ }
#endif
m_socket = s;
}
@@ -513,7 +519,7 @@ namespace libtorrent
}
catch (const type_error&) {}
- int interval = e["interval"].integer();
+ int interval = (int)e["interval"].integer();
peer_list.clear();
diff --git a/src/identify_client.cpp b/src/identify_client.cpp
index 29716f258..2b483df6a 100755
--- a/src/identify_client.cpp
+++ b/src/identify_client.cpp
@@ -144,6 +144,42 @@ namespace
return boost::optional(ret);
}
+ // checks if a peer id can possibly contain a mainline-style
+ // identification
+ boost::optional parse_mainline_style(const peer_id& id)
+ {
+ fingerprint ret("..", 0, 0, 0, 0);
+ peer_id::const_iterator i = id.begin();
+
+ if (!std::isprint(*i)) return boost::optional();
+ ret.id[0] = *i;
+ ret.id[1] = 0;
+ ++i;
+
+ if (!std::isdigit(*i)) return boost::optional();
+ ret.major_version = *i - '0';
+ ++i;
+
+ if (*i != '-') return boost::optional();
+ ++i;
+
+ if (!std::isdigit(*i)) return boost::optional();
+ ret.minor_version = *i - '0';
+ ++i;
+
+ if (*i != '-') return boost::optional();
+ ++i;
+
+ if (!std::isdigit(*i)) return boost::optional();
+ ret.revision_version = *i - '0';
+ ++i;
+
+ if (!std::equal(i, i+1, "--")) return boost::optional();
+
+ ret.tag_version = 0;
+ return boost::optional(ret);
+ }
+
} // namespace unnamed
namespace libtorrent
@@ -231,6 +267,26 @@ namespace libtorrent
return identity.str();
}
+ f = parse_mainline_style(p);
+ if (f)
+ {
+ std::stringstream identity;
+
+ // Mainline
+ if (std::equal(f->id, f->id+1, "M"))
+ identity << "Mainline ";
+
+ // unknown client
+ else
+ identity << std::string(f->id, f->id+1) << " ";
+
+ identity << (int)f->major_version
+ << "." << (int)f->minor_version
+ << "." << (int)f->revision_version;
+
+ return identity.str();
+ }
+
// ----------------------
// non standard encodings
// ----------------------
diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp
index 92491d8de..4a746a3a0 100755
--- a/src/peer_connection.cpp
+++ b/src/peer_connection.cpp
@@ -62,7 +62,7 @@ namespace libtorrent
// the names of the extensions to look for in
// the extensions-message
const char* peer_connection::extension_names[] =
- { "chat" };
+ { "chat", "metadata", "peer_exchange", "listen_port" };
const peer_connection::message_handler peer_connection::m_message_handler[] =
{
@@ -114,6 +114,9 @@ namespace libtorrent
, m_disconnecting(false)
, m_became_uninterested(boost::posix_time::second_clock::local_time())
, m_became_uninteresting(boost::posix_time::second_clock::local_time())
+ , m_no_metadata(
+ boost::gregorian::date(1970, boost::date_time::Jan, 1)
+ , boost::posix_time::seconds(0))
{
INVARIANT_CHECK;
@@ -144,10 +147,11 @@ namespace libtorrent
m_recv_buffer.resize(1);
// assume the other end has no pieces
- m_have_piece.resize(m_torrent->torrent_file().num_pieces());
- std::fill(m_have_piece.begin(), m_have_piece.end(), false);
-
- send_bitfield();
+ if (m_torrent->valid_metadata())
+ {
+ init();
+ send_bitfield();
+ }
}
peer_connection::peer_connection(
@@ -183,6 +187,9 @@ namespace libtorrent
, m_disconnecting(false)
, m_became_uninterested(boost::posix_time::second_clock::local_time())
, m_became_uninteresting(boost::posix_time::second_clock::local_time())
+ , m_no_metadata(
+ boost::gregorian::date(1970, boost::date_time::Jan, 1)
+ , boost::posix_time::seconds(0))
{
INVARIANT_CHECK;
@@ -220,6 +227,68 @@ namespace libtorrent
m_recv_buffer.resize(1);
}
+ void peer_connection::init()
+ {
+ assert(m_torrent);
+ assert(m_torrent->valid_metadata());
+
+ m_have_piece.resize(m_torrent->torrent_file().num_pieces(), false);
+
+ // now that we have a piece_picker,
+ // update it with this peers pieces
+
+ // build a vector of all pieces
+ std::vector piece_list;
+ for (int i = 0; i < (int)m_have_piece.size(); ++i)
+ {
+ if (m_have_piece[i])
+ {
+ ++m_num_pieces;
+ piece_list.push_back(i);
+ }
+ else if (m_have_piece[i])
+ {
+ --m_num_pieces;
+ m_torrent->peer_lost(i);
+ }
+ }
+
+ // shuffle the piece list
+ std::random_shuffle(piece_list.begin(), piece_list.end());
+
+ // let the torrent know which pieces the
+ // peer has, in a shuffled order
+ bool interesting = false;
+ for (std::vector::iterator i = piece_list.begin();
+ i != piece_list.end();
+ ++i)
+ {
+ int index = *i;
+ m_torrent->peer_has(index);
+ if (!m_torrent->have_piece(index))
+ interesting = true;
+ }
+
+ if (piece_list.size() == m_have_piece.size())
+ {
+#ifndef NDEBUG
+ (*m_logger) << " *** THIS IS A SEED ***\n";
+#endif
+ // if we're a seed too, disconnect
+ if (m_torrent->is_seed())
+ {
+#ifndef NDEBUG
+ (*m_logger) << " we're also a seed, disconnecting\n";
+#endif
+ throw protocol_error("seed to seed connection redundant, disconnecting");
+ }
+ }
+
+ if (interesting)
+ m_torrent->get_policy().peer_is_interesting(*this);
+
+ }
+
peer_connection::~peer_connection()
{
m_selector.remove(m_socket);
@@ -233,6 +302,7 @@ namespace libtorrent
void peer_connection::announce_piece(int index)
{
assert(m_torrent);
+ assert(m_torrent->valid_metadata());
assert(index >= 0 && index < m_torrent->torrent_file().num_pieces());
m_announce_queue.push_back(index);
}
@@ -240,6 +310,7 @@ namespace libtorrent
bool peer_connection::has_piece(int i) const
{
assert(m_torrent);
+ assert(m_torrent->valid_metadata());
assert(i >= 0);
assert(i < m_torrent->torrent_file().num_pieces());
return m_have_piece[i];
@@ -337,7 +408,9 @@ namespace libtorrent
, 0);
// indicate that we support the extension protocol
// curently disabled
-// m_send_buffer[pos] = 0x80;
+#ifdef TORRENT_ENABLE_EXTENSIONS
+ m_send_buffer[pos+7] = 0x01;
+#endif
pos += 8;
// info hash
@@ -364,6 +437,8 @@ namespace libtorrent
// and if it can correspond to a request generated by libtorrent.
bool peer_connection::verify_piece(const peer_request& p) const
{
+ assert(m_torrent->valid_metadata());
+
return p.piece >= 0
&& p.piece < m_torrent->torrent_file().num_pieces()
&& p.length > 0
@@ -543,11 +618,17 @@ namespace libtorrent
else
{
m_have_piece[index] = true;
- ++m_num_pieces;
- m_torrent->peer_has(index);
- if (!m_torrent->have_piece(index) && !is_interesting())
- m_torrent->get_policy().peer_is_interesting(*this);
+ // only update the piece_picker if
+ // we have the metadata
+ if (m_torrent->valid_metadata())
+ {
+ ++m_num_pieces;
+ m_torrent->peer_has(index);
+
+ if (!m_torrent->have_piece(index) && !is_interesting())
+ m_torrent->get_policy().peer_is_interesting(*this);
+ }
if (m_torrent->is_seed() && is_seed())
{
@@ -565,14 +646,32 @@ namespace libtorrent
INVARIANT_CHECK;
assert(received > 0);
- if (m_packet_size - 1 != ((int)m_have_piece.size() + 7) / 8)
+ assert(m_torrent);
+ // if we don't have the metedata, we cannot
+ // verify the bitfield size
+ if (m_torrent->valid_metadata()
+ && m_packet_size - 1 != ((int)m_have_piece.size() + 7) / 8)
throw protocol_error("bitfield with invalid size");
+
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
#ifndef NDEBUG
(*m_logger) << " <== BITFIELD\n";
#endif
+
+ // if we don't have metadata yet
+ // just remember the bitmask
+ // don't update the piecepicker
+ // (since it doesn't exist yet)
+ if (!m_torrent->valid_metadata())
+ {
+ m_have_piece.resize((m_packet_size - 1) * 8, false);
+ for (int i = 0; i < (int)m_have_piece.size(); ++i)
+ m_have_piece[i] = (m_recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0;
+ return;
+ }
+
// build a vector of all pieces
std::vector piece_list;
for (int i = 0; i < (int)m_have_piece.size(); ++i)
@@ -646,12 +745,37 @@ namespace libtorrent
r.start = detail::read_int32(ptr);
r.length = detail::read_int32(ptr);
+ if (!m_torrent->valid_metadata())
+ {
+ // if we don't have valid metadata yet,
+ // we shouldn't get a request
+#ifndef NDEBUG
+ (*m_logger) << " <== UNEXPECTED_REQUEST [ "
+ "piece: " << r.piece << " | "
+ "s: " << r.start << " | "
+ "l: " << r.length << " | "
+ "i: " << m_peer_interested << " | "
+ "t: " << (int)m_torrent->torrent_file().piece_size(r.piece) << " | "
+ "n: " << m_torrent->torrent_file().num_pieces() << " ]\n";
+#endif
+ return;
+ }
+
if (m_requests.size() > 40)
{
// don't allow clients to abuse our
// memory consumption.
// ignore requests if the client
// is making too many of them.
+#ifndef NDEBUG
+ (*m_logger) << " <== TOO MANY REQUESTS [ "
+ "piece: " << r.piece << " | "
+ "s: " << r.start << " | "
+ "l: " << r.length << " | "
+ "i: " << m_peer_interested << " | "
+ "t: " << (int)m_torrent->torrent_file().piece_size(r.piece) << " | "
+ "n: " << m_torrent->torrent_file().num_pieces() << " ]\n";
+#endif
return;
}
@@ -896,6 +1020,7 @@ namespace libtorrent
{
INVARIANT_CHECK;
+ assert(m_torrent);
assert(received > 0);
if (m_packet_size > 100 * 1024)
{
@@ -929,6 +1054,11 @@ namespace libtorrent
(*m_logger) << i->first << "\n";
}
#endif
+ if (!m_torrent->valid_metadata()
+ && supports_extension(extended_metadata_message))
+ {
+ send_metadata_request(0, 255);
+ }
}
catch(invalid_encoding&)
{
@@ -960,52 +1090,155 @@ namespace libtorrent
if (m_recv_pos < 5) return;
const char* ptr = &m_recv_buffer[1];
-
int extended_id = detail::read_int32(ptr);
switch (extended_id)
{
case extended_chat_message:
- {
- if (m_packet_size > 2 * 1024)
- throw protocol_error("CHAT message larger than 2 kB");
- if (m_recv_pos < m_packet_size) return;
- try
- {
- entry d = bdecode(m_recv_buffer.begin()+5, m_recv_buffer.end());
- const std::string& str = d["msg"].string();
-
- if (m_torrent->alerts().should_post(alert::critical))
- {
- m_torrent->alerts().post_alert(
- chat_message_alert(
- m_torrent->get_handle()
- , m_socket->sender(), str));
- }
-
- }
- catch (invalid_encoding&)
- {
- throw protocol_error("invalid bencoding in CHAT message");
- }
- catch (type_error&)
- {
- throw protocol_error("invalid types in bencoded CHAT message");
- }
- return;
- }
+ on_chat(); break;
+ case extended_metadata_message:
+ on_metadata(); break;
+ case extended_peer_exchange_message:
+ on_peer_exchange(); break;
+ case extended_listen_port_message:
+ on_listen_port(); break;
default:
throw protocol_error("unknown extended message id");
-
};
}
+ // -----------------------------
+ // ----------- CHAT ------------
+ // -----------------------------
+ void peer_connection::on_chat()
+ {
+ if (m_packet_size > 2 * 1024)
+ throw protocol_error("CHAT message larger than 2 kB");
+ if (m_recv_pos < m_packet_size) return;
+ try
+ {
+ entry d = bdecode(m_recv_buffer.begin()+5, m_recv_buffer.end());
+ const std::string& str = d["msg"].string();
+ if (m_torrent->alerts().should_post(alert::critical))
+ {
+ m_torrent->alerts().post_alert(
+ chat_message_alert(
+ m_torrent->get_handle()
+ , m_socket->sender(), str));
+ }
+ }
+ catch (invalid_encoding&)
+ {
+ // TODO: make these non-fatal errors
+ // they should just ignore the chat message
+ // and report the error via an alert
+ throw protocol_error("invalid bencoding in CHAT message");
+ }
+ catch (type_error&)
+ {
+ throw protocol_error("invalid types in bencoded CHAT message");
+ }
+ return;
+ }
+ // -----------------------------
+ // --------- METADATA ----------
+ // -----------------------------
+ void peer_connection::on_metadata()
+ {
+ assert(m_torrent);
+
+ if (m_packet_size > 500 * 1024)
+ throw protocol_error("metadata message larger than 500 kB");
+
+ if (m_recv_pos < m_packet_size) return;
+
+ std::vector::iterator ptr = m_recv_buffer.begin()+5;
+ int type = detail::read_uint8(ptr);
+
+ switch (type)
+ {
+ case 0: // request
+ {
+ int start = detail::read_uint8(ptr);
+ int size = detail::read_uint8(ptr);
+
+ if (start + size > 255 || start + size <= 0 || m_packet_size != 8)
+ {
+ // invalid metadata request
+ throw protocol_error("invalid metadata request");
+ }
+
+ send_metadata(start, size);
+ }
+ break;
+ case 1: // data
+ {
+ int total_size = detail::read_int32(ptr);
+ int offset = detail::read_int32(ptr);
+ int data_size = m_packet_size - 5 - 9;
+
+ if (total_size > 500 * 1024)
+ throw protocol_error("metadata size larger than 500 kB");
+ if (offset > total_size)
+ throw protocol_error("invalid metadata offset");
+ if (offset + data_size > total_size)
+ throw protocol_error("invalid metadata message");
+
+ m_torrent->received_metadata(&m_recv_buffer[5+9], data_size, offset, total_size);
+ }
+ break;
+ case 2: // have no data
+ m_no_metadata = boost::posix_time::second_clock::local_time();
+ break;
+ }
+
+ }
+
+ // -----------------------------
+ // ------ PEER EXCHANGE --------
+ // -----------------------------
+
+ void peer_connection::on_peer_exchange()
+ {
+
+ }
+
+ // -----------------------------
+ // ------- LISTEN PORT ---------
+ // -----------------------------
+
+ void peer_connection::on_listen_port()
+ {
+ assert(m_torrent);
+
+ if (m_packet_size != 7)
+ throw protocol_error("invalid listen_port message");
+
+ if (is_local())
+ {
+#ifndef NDEBUG
+ (*m_logger) << "<== LISTEN_PORT [ UNEXPECTED ]\n";
+#endif
+ return;
+ }
+
+ const char* ptr = &m_recv_buffer[5];
+ unsigned short port = detail::read_uint16(ptr);
+
+#ifndef NDEBUG
+ (*m_logger) << "<== LISTEN_PORT [ port: " << port << " ]\n";
+#endif
+
+ address adr = m_socket->sender();
+ adr.port = port;
+ m_torrent->get_policy().peer_from_tracker(adr, m_peer_id);
+ }
void peer_connection::disconnect()
@@ -1050,6 +1283,8 @@ namespace libtorrent
{
INVARIANT_CHECK;
+ assert(m_torrent->valid_metadata());
+
assert(block.piece_index >= 0);
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
assert(block.block_index >= 0);
@@ -1100,6 +1335,7 @@ namespace libtorrent
{
INVARIANT_CHECK;
+ assert(m_torrent->valid_metadata());
assert(block.piece_index >= 0);
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
assert(block.block_index >= 0);
@@ -1151,12 +1387,82 @@ namespace libtorrent
send_buffer_updated();
}
+ void peer_connection::send_metadata(int start, int size)
+ {
+ assert(start >= 0);
+ assert(size > 0);
+ assert(start <= 255);
+ assert(start + size <= 255);
+ assert(m_torrent);
+ INVARIANT_CHECK;
+
+ // abort if the peer doesn't support the metadata extension
+ if (!supports_extension(extended_metadata_message)) return;
+
+ std::back_insert_iterator > ptr(m_send_buffer);
+
+ if (m_torrent->valid_metadata())
+ {
+ int offset = start * (int)m_torrent->metadata().size() / 255;
+ int metadata_size
+ = (start + size) * (int)m_torrent->metadata().size() / 255 - offset;
+
+ // yes, we have metadata, send it
+ assert(size <= (int)m_torrent->metadata().size());
+ detail::write_uint32(5 + 9 + metadata_size, ptr);
+ detail::write_uint8(msg_extended, ptr);
+ detail::write_int32(m_extension_messages[extended_metadata_message], ptr);
+ // means 'data packet'
+ detail::write_uint8(1, ptr);
+ detail::write_uint32((int)m_torrent->metadata().size(), ptr);
+ detail::write_uint32(offset, ptr);
+ std::vector const& metadata = m_torrent->metadata();
+ std::copy(&metadata[offset], &metadata[offset + metadata_size], ptr);
+ }
+ else
+ {
+ // we don't have the metadata, reply with
+ // don't have-message
+ detail::write_uint32(1 + 4 + 1, ptr);
+ detail::write_uint8(msg_extended, ptr);
+ detail::write_int32(m_extension_messages[extended_metadata_message], ptr);
+ // means 'have no data'
+ detail::write_uint8(2, ptr);
+ }
+ send_buffer_updated();
+ }
+
+ void peer_connection::send_metadata_request(int start, int size)
+ {
+ assert(start >= 0);
+ assert(size > 0);
+ assert(start < 255);
+ assert(start + size <= 255);
+ assert(m_torrent);
+ assert(!m_torrent->valid_metadata());
+ INVARIANT_CHECK;
+
+ // abort if the peer doesn't support the metadata extension
+ if (!supports_extension(extended_metadata_message)) return;
+
+ std::back_insert_iterator > ptr(m_send_buffer);
+
+ detail::write_uint32(1 + 4 + 3, ptr);
+ detail::write_uint8(msg_extended, ptr);
+ detail::write_int32(m_extension_messages[extended_metadata_message], ptr);
+ // means 'request data'
+ detail::write_uint8(0, ptr);
+ detail::write_uint8(start, ptr);
+ detail::write_uint8(size, ptr);
+ send_buffer_updated();
+ }
+
void peer_connection::send_chat_message(const std::string& msg)
{
INVARIANT_CHECK;
assert(msg.length() <= 1 * 1024);
- if (m_extension_messages[extended_chat_message] == -1) return;
+ if (!supports_extension(extended_chat_message)) return;
entry e(entry::dictionary_t);
e["msg"] = msg;
@@ -1176,6 +1482,8 @@ namespace libtorrent
{
INVARIANT_CHECK;
+ if (m_torrent->num_pieces() == 0) return;
+
#ifndef NDEBUG
(*m_logger) << " ==> BITFIELD\n";
#endif
@@ -1290,6 +1598,7 @@ namespace libtorrent
void peer_connection::send_have(int index)
{
+ assert(m_torrent->valid_metadata());
assert(index >= 0);
assert(index < m_torrent->torrent_file().num_pieces());
INVARIANT_CHECK;
@@ -1550,10 +1859,10 @@ namespace libtorrent
// these 8 bytes would be used to describe the
// extensions available on the other side
// currently disabled
-// if (m_recv_buffer[0] & 0x80)
-// {
-// m_supports_extensions = true;
-// }
+#ifdef TORRENT_ENABLE_EXTENSIONS
+ if (m_recv_buffer[7] & 0x01)
+ m_supports_extensions = true;
+#endif
if (m_torrent == 0)
{
@@ -1583,8 +1892,11 @@ namespace libtorrent
throw protocol_error("connection rejected by paused torrent");
}
+ if (m_torrent->valid_metadata()) init();
+
// assume the other end has no pieces
- m_have_piece.resize(m_torrent->torrent_file().num_pieces());
+ // if we don't have valid metadata yet,
+ // leave the vector unallocated
std::fill(m_have_piece.begin(), m_have_piece.end(), false);
// yes, we found the torrent
@@ -1747,6 +2059,7 @@ namespace libtorrent
&& ((int)m_send_buffer.size() < m_torrent->block_size())
&& !m_choked)
{
+ assert(m_torrent->valid_metadata());
peer_request& r = m_requests.front();
assert(r.piece >= 0);
@@ -1796,6 +2109,7 @@ namespace libtorrent
if (!m_announce_queue.empty())
{
+ assert(m_torrent->valid_metadata());
for (std::vector::iterator i = m_announce_queue.begin();
i != m_announce_queue.end();
++i)
@@ -1887,6 +2201,7 @@ namespace libtorrent
void peer_connection::check_invariant() const
{
assert(can_write() == m_selector.is_writability_monitored(m_socket));
+
/*
assert(m_num_pieces == std::count(
m_have_piece.begin()
diff --git a/src/session.cpp b/src/session.cpp
index 81bb62062..a7a227108 100755
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -805,10 +805,12 @@ namespace libtorrent
// current platform.
// if the torrent already exists, this will throw duplicate_torrent
torrent_handle session::add_torrent(
- const torrent_info& ti
- , const boost::filesystem::path& save_path
- , const entry& resume_data)
+ entry const& metadata
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data)
{
+ torrent_info ti(metadata);
+
if (ti.begin_files() == ti.end_files())
throw std::runtime_error("no files in torrent");
@@ -834,7 +836,7 @@ namespace libtorrent
// the checker thread and store it before starting
// the thread
boost::shared_ptr torrent_ptr(
- new torrent(m_impl, ti, save_path, m_impl.m_listen_interface));
+ new torrent(m_impl, metadata, save_path, m_impl.m_listen_interface));
detail::piece_checker_data d;
d.torrent_ptr = torrent_ptr;
@@ -854,6 +856,44 @@ namespace libtorrent
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash());
}
+ torrent_handle session::add_torrent(
+ char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data)
+ {
+ {
+ // lock the session
+ boost::mutex::scoped_lock l(m_impl.m_mutex);
+
+ // is the torrent already active?
+ if (m_impl.find_torrent(info_hash))
+ throw duplicate_torrent();
+ }
+
+ {
+ // lock the checker_thread
+ boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
+
+ // is the torrent currently being checked?
+ if (m_checker_impl.find_torrent(info_hash))
+ throw duplicate_torrent();
+ }
+
+ // create the torrent and the data associated with
+ // the checker thread and store it before starting
+ // the thread
+ boost::shared_ptr torrent_ptr(
+ new torrent(m_impl, tracker_url, info_hash, save_path, m_impl.m_listen_interface));
+
+ boost::mutex::scoped_lock l(m_impl.m_mutex);
+ m_impl.m_torrents.insert(
+ std::make_pair(info_hash, torrent_ptr)).first;
+
+ return torrent_handle(&m_impl, &m_checker_impl, info_hash);
+ }
+
+
void session::remove_torrent(const torrent_handle& h)
{
if (h.m_ses != &m_impl) return;
diff --git a/src/storage.cpp b/src/storage.cpp
index 76207139d..d5a36a834 100755
--- a/src/storage.cpp
+++ b/src/storage.cpp
@@ -341,6 +341,7 @@ namespace libtorrent
if (file_offset + read_bytes > file_iter->size)
read_bytes = static_cast(file_iter->size - file_offset);
+ // TODO: this assert will be hit if a file has size 0
assert(read_bytes > 0);
// in.read(buf + buf_pos, read_bytes);
@@ -784,6 +785,7 @@ namespace libtorrent
hasher small_digest;
small_digest.update(&piece_data[0], last_piece_size);
hasher large_digest(small_digest);
+ assert(piece_size - last_piece_size >= 0);
if (piece_size - last_piece_size > 0)
{
large_digest.update(
diff --git a/src/torrent.cpp b/src/torrent.cpp
index 62e97cf2d..f5ae879b0 100755
--- a/src/torrent.cpp
+++ b/src/torrent.cpp
@@ -146,21 +146,20 @@ namespace libtorrent
{
torrent::torrent(
detail::session_impl& ses
- , const torrent_info& torrent_file
- , const boost::filesystem::path& save_path
+ , entry const& metadata
+ , boost::filesystem::path const& save_path
, address const& net_interface)
- : m_block_size(calculate_block_size(torrent_file))
+ : m_torrent_file(metadata)
, m_abort(false)
, m_paused(false)
, m_event(tracker_request::started)
- , m_torrent_file(torrent_file)
- , m_storage(m_torrent_file, save_path)
+ , m_block_size(0)
+ , m_storage(0)
, m_next_request(boost::posix_time::second_clock::local_time())
, m_duration(1800)
, m_policy(new policy(this)) // warning: uses this in member init list
, m_ses(ses)
- , m_picker(static_cast(torrent_file.piece_length() / m_block_size),
- static_cast((torrent_file.total_size()+m_block_size-1)/m_block_size))
+ , m_picker(0)
, m_last_working_tracker(-1)
, m_currently_trying_tracker(0)
, m_failed_trackers(0)
@@ -174,8 +173,42 @@ namespace libtorrent
, m_upload_bandwidth_limit(std::numeric_limits::max())
, m_download_bandwidth_limit(std::numeric_limits::max())
{
- assert(torrent_file.begin_files() != torrent_file.end_files());
- m_have_pieces.resize(torrent_file.num_pieces(), false);
+ bencode(std::back_inserter(m_metadata), metadata["info"]);
+ init();
+ }
+
+ torrent::torrent(
+ detail::session_impl& ses
+ , char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , address const& net_interface)
+ : m_torrent_file(0, 0, info_hash)
+ , m_abort(false)
+ , m_paused(false)
+ , m_event(tracker_request::started)
+ , m_block_size(0)
+ , m_storage(0)
+ , m_next_request(boost::posix_time::second_clock::local_time())
+ , m_duration(1800)
+ , m_policy(new policy(this)) // warning: uses this in member init list
+ , m_ses(ses)
+ , m_picker(0)
+ , m_last_working_tracker(-1)
+ , m_currently_trying_tracker(0)
+ , m_failed_trackers(0)
+ , m_time_scaler(0)
+ , m_priority(.5)
+ , m_num_pieces(0)
+ , m_got_tracker_response(false)
+ , m_ratio(0.f)
+ , m_total_failed_bytes(0)
+ , m_net_interface(net_interface.ip(), address::any_port)
+ , m_upload_bandwidth_limit(std::numeric_limits::max())
+ , m_download_bandwidth_limit(std::numeric_limits::max())
+ , m_save_path(save_path)
+ {
+ m_torrent_file.add_tracker(tracker_url);
}
torrent::~torrent()
@@ -184,6 +217,18 @@ namespace libtorrent
if (m_ses.m_abort) m_abort = true;
}
+ void torrent::init()
+ {
+ assert(m_torrent_file.is_valid());
+
+ m_have_pieces.resize(m_torrent_file.num_pieces(), false);
+ m_storage.reset(new piece_manager(m_torrent_file, m_save_path));
+ m_block_size = calculate_block_size(m_torrent_file);
+ m_picker.reset(new piece_picker(
+ static_cast(m_torrent_file.piece_length() / m_block_size)
+ , static_cast((m_torrent_file.total_size()+m_block_size-1)/m_block_size)));
+ }
+
void torrent::use_interface(const char* net_interface)
{
m_net_interface = address(net_interface, address::any_port);
@@ -246,11 +291,17 @@ namespace libtorrent
size_type torrent::bytes_left() const
{
+ // if we don't have the metadata yet, we
+ // cannot tell how big the torrent is.
+ if (!valid_metadata()) return -1;
return m_torrent_file.total_size() - bytes_done();
}
size_type torrent::bytes_done() const
{
+ if (!valid_metadata()) return 0;
+
+ assert(m_picker.get());
const int last_piece = m_torrent_file.num_pieces()-1;
size_type total_done
@@ -266,7 +317,7 @@ namespace libtorrent
}
const std::vector& dl_queue
- = m_picker.get_download_queue();
+ = m_picker->get_download_queue();
const int blocks_per_piece = static_cast(m_torrent_file.piece_length() / m_block_size);
@@ -285,7 +336,7 @@ namespace libtorrent
// correction if this was the last piece
// and if we have the last block
if (i->index == last_piece
- && i->finished_blocks[m_picker.blocks_in_last_piece()-1])
+ && i->finished_blocks[m_picker->blocks_in_last_piece()-1])
{
total_done -= m_block_size;
total_done += m_torrent_file.piece_size(last_piece) % m_block_size;
@@ -304,7 +355,7 @@ namespace libtorrent
{
if (m_have_pieces[p->piece_index])
continue;
- if (m_picker.is_finished(piece_block(p->piece_index, p->block_index)))
+ if (m_picker->is_finished(piece_block(p->piece_index, p->block_index)))
continue;
total_done += p->bytes_downloaded;
@@ -316,6 +367,8 @@ namespace libtorrent
void torrent::piece_failed(int index)
{
+ assert(m_storage.get());
+ assert(m_picker.get());
assert(index >= 0);
assert(index < m_torrent_file.num_pieces());
@@ -329,7 +382,7 @@ namespace libtorrent
m_total_failed_bytes += m_torrent_file.piece_size(index);
std::vector downloaders;
- m_picker.get_downloaders(downloaders, index);
+ m_picker->get_downloaders(downloaders, index);
// decrease the trust point of all peers that sent
// parts of this piece.
@@ -366,8 +419,8 @@ namespace libtorrent
// if some clients has sent more than one piece
// start with redownloading the pieces that the client
// that has sent the least number of pieces
- m_picker.restore_piece(index);
- m_storage.mark_failed(index);
+ m_picker->restore_piece(index);
+ m_storage->mark_failed(index);
assert(m_have_pieces[index] == false);
}
@@ -375,11 +428,12 @@ namespace libtorrent
void torrent::announce_piece(int index)
{
+ assert(m_picker.get());
assert(index >= 0);
assert(index < m_torrent_file.num_pieces());
std::vector downloaders;
- m_picker.get_downloaders(downloaders, index);
+ m_picker->get_downloaders(downloaders, index);
// increase the trust point of all peers that sent
// parts of this piece.
@@ -392,7 +446,7 @@ namespace libtorrent
p->second->received_valid_data();
}
- m_picker.we_have(index);
+ m_picker->we_have(index);
for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
i->second->announce_piece(index);
}
@@ -416,6 +470,7 @@ namespace libtorrent
req.downloaded = m_stat.total_payload_download();
req.uploaded = m_stat.total_payload_upload();
req.left = bytes_left();
+ if (req.left == -1) req.left = 1000;
req.event = m_event;
req.url = m_torrent_file.trackers()[m_currently_trying_tracker].url;
req.num_want = std::max(
@@ -443,26 +498,29 @@ namespace libtorrent
i != p->download_queue().end();
++i)
{
- m_picker.abort_download(*i);
+ m_picker->abort_download(*i);
}
- std::vector piece_list;
- const std::vector& pieces = p->get_bitfield();
-
- for (std::vector::const_iterator i = pieces.begin();
- i != pieces.end();
- ++i)
+ if (valid_metadata())
{
- if (*i) piece_list.push_back(static_cast(i - pieces.begin()));
- }
+ std::vector piece_list;
+ const std::vector& pieces = p->get_bitfield();
- std::random_shuffle(piece_list.begin(), piece_list.end());
+ for (std::vector::const_iterator i = pieces.begin();
+ i != pieces.end();
+ ++i)
+ {
+ if (*i) piece_list.push_back(static_cast(i - pieces.begin()));
+ }
- for (std::vector::iterator i = piece_list.begin();
- i != piece_list.end();
- ++i)
- {
- peer_lost(*i);
+ std::random_shuffle(piece_list.begin(), piece_list.end());
+
+ for (std::vector::iterator i = piece_list.begin();
+ i != piece_list.end();
+ ++i)
+ {
+ peer_lost(*i);
+ }
}
m_policy->connection_closed(*p);
@@ -576,16 +634,14 @@ namespace libtorrent
void torrent::check_files(detail::piece_checker_data& data,
boost::mutex& mutex)
{
- m_storage.check_pieces(mutex, data, m_have_pieces);
+ assert(m_storage.get());
+ m_storage->check_pieces(mutex, data, m_have_pieces);
m_num_pieces = std::accumulate(
m_have_pieces.begin()
, m_have_pieces.end()
, 0);
- m_picker.files_checked(m_have_pieces, data.unfinished_pieces);
-#ifndef NDEBUG
- m_picker.integrity_check(this);
-#endif
+ m_picker->files_checked(m_have_pieces, data.unfinished_pieces);
}
alert_manager& torrent::alerts() const
@@ -593,6 +649,19 @@ namespace libtorrent
return m_ses.m_alerts;
}
+ boost::filesystem::path torrent::save_path() const
+ {
+ assert(m_storage.get());
+ return m_storage->save_path();
+ }
+
+ piece_manager& torrent::filesystem()
+ {
+ assert(m_storage.get());
+ return *m_storage;
+ }
+
+
torrent_handle torrent::get_handle() const
{
return torrent_handle(&m_ses, 0, m_torrent_file.info_hash());
@@ -601,13 +670,13 @@ namespace libtorrent
#ifndef NDEBUG
- void torrent::check_invariant()
+ void torrent::check_invariant() const
{
assert(m_num_pieces
== std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
assert(m_priority >= 0.f && m_priority < 1.f);
- assert(m_block_size > 0);
- assert((m_torrent_file.piece_length() % m_block_size) == 0);
+ assert(!valid_metadata() || m_block_size > 0);
+ assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0);
}
#endif
@@ -710,13 +779,14 @@ namespace libtorrent
bool torrent::verify_piece(int piece_index)
{
+ assert(m_storage.get());
assert(piece_index >= 0);
assert(piece_index < m_torrent_file.num_pieces());
int size = static_cast(m_torrent_file.piece_size(piece_index));
std::vector buffer(size);
assert(size > 0);
- m_storage.read(&buffer[0], piece_index, 0, size);
+ m_storage->read(&buffer[0], piece_index, 0, size);
hasher h;
h.update(&buffer[0], size);
@@ -748,12 +818,7 @@ namespace libtorrent
torrent_status st;
- if (m_last_working_tracker >= 0)
- {
- st.current_tracker
- = m_torrent_file.trackers()[m_last_working_tracker].url;
- }
-
+ st.num_peers = (int)m_connections.size();
st.paused = m_paused;
st.total_done = bytes_done();
@@ -776,16 +841,35 @@ namespace libtorrent
st.download_payload_rate = m_stat.download_payload_rate();
st.upload_payload_rate = m_stat.upload_payload_rate();
- st.progress = st.total_done
- / static_cast(m_torrent_file.total_size());
-
st.next_announce = next_announce()
- boost::posix_time::second_clock::local_time();
if (st.next_announce.is_negative()) st.next_announce
= boost::posix_time::seconds(0);
st.announce_interval = boost::posix_time::seconds(m_duration);
- st.num_peers = (int)m_connections.size();
+ // if we don't have any metadata, stop here
+
+ if (!valid_metadata())
+ {
+ if (m_got_tracker_response == false)
+ st.state = torrent_status::connecting_to_tracker;
+ else
+ st.state = torrent_status::downloading_metadata;
+ // TODO: implement progress
+ st.progress = 0.f;
+ return st;
+ }
+
+ // fill in status that depends on metadata
+
+ if (m_last_working_tracker >= 0)
+ {
+ st.current_tracker
+ = m_torrent_file.trackers()[m_last_working_tracker].url;
+ }
+
+ st.progress = st.total_done
+ / static_cast(m_torrent_file.total_size());
st.pieces = &m_have_pieces;
@@ -799,6 +883,89 @@ namespace libtorrent
return st;
}
+ bool torrent::received_metadata(char const* buf, int size, int offset, int total_size)
+ {
+ INVARIANT_CHECK;
+
+ if (valid_metadata()) return false;
+
+ if ((int)m_metadata.size() < total_size)
+ m_metadata.resize(total_size);
+
+ std::copy(
+ buf
+ , buf + size
+ , &m_metadata[offset]);
+
+ if (m_have_metadata.empty())
+ m_have_metadata.resize(255, false);
+
+ int start = offset * 255 / (int)m_metadata.size();
+ if ((offset * 255) % (int)m_metadata.size() != 0)
+ throw protocol_error("unaligned metadata message offset");
+
+ int block_size = size * (255 - offset) / (int)m_metadata.size() - start;
+
+ assert(start >= 0);
+ assert(block_size > 0);
+ assert(start < 256);
+ assert(start + block_size <= 256);
+
+ std::fill(
+ m_have_metadata.begin() + start
+ , m_have_metadata.begin() + start + block_size
+ , true);
+
+ bool have_all = std::count(m_have_metadata.begin(), m_have_metadata.end(), true) == 255;
+ if (!have_all) return false;
+
+ hasher h;
+ h.update(&m_metadata[0], m_metadata.size());
+ sha1_hash info_hash = h.final();
+
+ if (info_hash != m_torrent_file.info_hash())
+ {
+ std::fill(
+ m_have_metadata.begin()
+ , m_have_metadata.end() + start + block_size
+ , false);
+ // TODO: rerequest
+ assert(false);
+ return false;
+ }
+
+ m_torrent_file.parse_info_section(bdecode(m_metadata.begin(), m_metadata.end()));
+
+ init();
+
+ boost::mutex m;
+ detail::piece_checker_data d;
+ d.abort = false;
+ // TODO: this check should be moved to the checker thread
+ check_files(d, m);
+
+ // all peer connections have to initialize themselves now that the metadata
+ // is available
+ for (std::map::iterator i = m_connections.begin();
+ i != m_connections.end(); ++i)
+ {
+ i->second->init();
+ }
+
+#ifndef NDEBUG
+ m_picker->integrity_check(this);
+#endif
+
+
+ // clear the storage for the bitfield
+ {
+ std::vector t;
+ m_have_metadata.swap(t);
+ }
+
+ return true;
+ }
+
void torrent::tracker_request_timed_out()
{
#ifndef NDEBUG
diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp
index 6064f2220..04da4e7fb 100755
--- a/src/torrent_handle.cpp
+++ b/src/torrent_handle.cpp
@@ -321,7 +321,9 @@ namespace libtorrent
torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0)
throw invalid_handle();
-
+
+ if (!t->valid_metadata()) return entry();
+
t->filesystem().export_piece_map(piece_index);
entry ret(entry::dictionary_t);
@@ -400,6 +402,9 @@ namespace libtorrent
{
// we cannot save remote connection
// since we don't know their listen port
+ // TODO: iterate the peers in the policy
+ // instead, since peers may be remote
+ // but still connectable
if (!i->second->is_local()) continue;
address ip = i->second->get_socket()->sender();
@@ -552,13 +557,14 @@ namespace libtorrent
{
INVARIANT_CHECK;
- queue.clear();
-
if (m_ses == 0) throw invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
+
+ queue.clear();
if (t == 0) return;
+ if (!t->valid_metadata()) return;
const piece_picker& p = t->picker();
diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp
index 94d6ccd8c..fe1400530 100755
--- a/src/torrent_info.cpp
+++ b/src/torrent_info.cpp
@@ -114,15 +114,70 @@ namespace libtorrent
// just the necessary to use it with piece manager
torrent_info::torrent_info(
int piece_size
- , const char* name)
+ , const char* name
+ , sha1_hash const& info_hash)
: m_piece_length(piece_size)
, m_total_size(0)
+ , m_info_hash(info_hash)
, m_creation_date(second_clock::local_time())
{
- m_info_hash.clear();
}
+ void torrent_info::parse_info_section(entry const& info)
+ {
+ // encode the info-field in order to calculate it's sha1-hash
+ std::vector 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)
+ m_name = info["name"].string();
+
+ // 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;
+ e.size = info["length"].integer();
+ m_files.push_back(e);
+ }
+ else
+ {
+ extract_files(i->list(), m_files);
+ }
+
+ // calculate total size of all pieces
+ m_total_size = 0;
+ for (std::vector::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((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());
+ }
+
// 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)
@@ -197,57 +252,7 @@ namespace libtorrent
if (i == 0) throw invalid_torrent_file();
entry const& info = *i;
- // encode the info-field in order to calculate it's sha1-hash
- std::vector buf;
- bencode(std::back_insert_iterator >(buf), info);
- hasher h;
- h.update(&buf[0], (int)buf.size());
- m_info_hash = h.final();
- std::copy(m_info_hash.begin(), m_info_hash.end(), std::ostream_iterator(std::cout));
-
- // extract piece length
- m_piece_length = (int)info["piece length"].integer();
-
- // extract file name (or the directory name if it's a multifile libtorrent)
- m_name = info["name"].string();
-
- // extract file list
- 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;
- e.size = info["length"].integer();
- m_files.push_back(e);
- }
- else
- {
- extract_files(i->list(), m_files);
- }
-
- // calculate total size of all pieces
- m_total_size = 0;
- for (std::vector::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((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());
+ parse_info_section(info);
}
boost::optional
@@ -306,6 +311,8 @@ namespace libtorrent
entry torrent_info::create_torrent() const
{
+ assert(m_piece_length > 0);
+
using namespace boost::gregorian;
using namespace boost::posix_time;