From 91c9156de1edc01fa58332f7e38b2807ef29ec4f Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 7 Jan 2004 00:48:02 +0000 Subject: [PATCH] *** empty log message *** --- docs/index.html | 1157 +------------------- docs/index.rst | 1235 +-------------------- docs/libtorrent_screen.png | Bin 0 -> 53067 bytes docs/manual.html | 1264 ++++++++++++++++++++++ docs/manual.rst | 1353 ++++++++++++++++++++++++ examples/client_test.cpp | 21 +- examples/dump_torrent.cpp | 3 +- include/libtorrent/alert.hpp | 2 +- include/libtorrent/entry.hpp | 10 + include/libtorrent/peer_connection.hpp | 23 +- include/libtorrent/session.hpp | 19 +- include/libtorrent/torrent.hpp | 3 + include/libtorrent/torrent_handle.hpp | 2 +- src/entry.cpp | 272 +++-- src/peer_connection.cpp | 78 +- src/session.cpp | 97 +- src/torrent.cpp | 21 +- src/torrent_handle.cpp | 62 +- 18 files changed, 3053 insertions(+), 2569 deletions(-) create mode 100755 docs/libtorrent_screen.png create mode 100755 docs/manual.html create mode 100755 docs/manual.rst diff --git a/docs/index.html b/docs/index.html index 5d73fd2aa..7e9cc3720 100755 --- a/docs/index.html +++ b/docs/index.html @@ -12,69 +12,19 @@

libtorrent

--++++ + +
sourceforge pagemanualscreenshot mailing list
-
-

Contents

- -
-
-

introduction

libtorrent is a C++ library that aims to be a good alternative to all the other bittorrent implementations around. It is a library and not a full featured client, although it comes with a working @@ -87,1104 +37,13 @@ example client.

  • to be very easy to use
  • -

    libtorrent is not finished. It is an ongoing project (including this documentation). -The current state includes the following features:

    -
    -
      -
    • multitracker extension support (as described by TheShadow)
    • -
    • serves multiple torrents on a single port and a single thread
    • -
    • supports http proxies and proxy authentication
    • -
    • gzipped tracker-responses
    • -
    • piece picking on block-level (as opposed to piece-level) like in Azureus
    • -
    • queues torrents for file check, instead of checking all of them in parallel.
    • -
    • uses separate threads for checking files and for main downloader, with a fool-proof -thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).
    • -
    • can limit the upload bandwidth usage and the maximum number of unchoked peers
    • -
    • piece-wise file allocation
    • -
    • tries to maintain a 1:1 share ratio between all peers but also shifts free -download to peers as free upload. To maintain a global 1:1 ratio.
    • -
    • fast resume support, a way to get rid of the costly piece check at the start -of a resumed torrent. Saves the storage state in a separate fast-resume file.
    • -
    -
    -

    Functions that are yet to be implemented:

    -
    -
      -
    • number of connections limit
    • -
    • better handling of peers that send bad data
    • -
    • ip-filters
    • -
    • file-level piece priority
    • -
    -
    -

    libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread, -boost.filesystem, boost.date_time and various other boost libraries as well as zlib.

    -

    libtorrent has been successfully compiled and tested on:

    -
    -
      -
    • Cygwin GCC 3.3.1
    • -
    • Windows 2000 vc7.1
    • -
    • Linux x86 (debian) GCC 3.0
    • -
    -
    -

    It does not compile on

    -
    -
      -
    • GCC 2.95
    • -
    -
    -
    -
    -

    building

    -

    To build libtorrent you need boost and bjam installed. -Then you can use bjam to build libtorrent.

    -

    To make bjam work, you need to set the environment variable BOOST_ROOT to the -path where boost is installed (e.g. c:boost_1_30_2 on windows). Then you can just run -bjam in the libtorrent directory.

    -

    The Jamfile doesn't work yet. On unix-systems you can use the makefile however. You -first have to build boost.thread and boost.filesystem. You do this by, in the directory -'boost-1.30.2/tools/build/jam_src' run the build script ./build.sh. This should -produce at least one folder with the 'bin' prefix (and the rest of the name describes -your platform). Put the files in that folder somewhere in your path.

    -

    You can then invoke bjam in the directories 'boost-1.30.2/libs/thread/build', -'boost-1.30.2/libs/date_time/build' and 'boost-1.30.2/libs/filesystem/build'. That will -produce the needed libraries. Put these libraries in the libtorrent root directory. -You then have to modify the makefile to use you prefered compiler and to have the -correct path to your boost istallation.

    -

    Then the makefile should be able to do the rest.

    -

    When building (with boost 1.30.2) on linux and solaris however, I found that I had to make the following -modifications to the boost.date-time library. In the file: -'boost-1.30.2/boost/date_time/gregorian_calendar.hpp' line 59. Prepend 'boost/date_time/' -to the include path.

    -

    And the second modification was in the file: -'boost-1.30.2/boost/date_time/microsec_time_clock.hpp' add the following include at the top -of the file:

    -
    -#include "boost/cstdint.hpp"
    -
    -

    In developer studio, you may have to set the compiler options "force conformance in for -loop scope" and "treat wchar_t as built-in type" to Yes.

    -

    TODO: more detailed build instructions.

    -
    -
    -

    using

    -

    The interface of libtorrent consists of a few classes. The main class is -the session, it contains the main loop that serves all torrents.

    -
    -

    session

    -

    The session class has the following synopsis:

    -
    -class session: public boost::noncopyable
    -{
    -        session(int listen_port, const fingerprint& print);
    -        session(int listen_port);
    -
    -        torrent_handle add_torrent(const torrent_info& t, const std::string& save_path);
    -        torrent_handle add_torrent(
    -                const torrent_info& t
    -                , const std::string& save_path
    -                , const std::vector<char>& resume_data);
    -
    -        void remove_torrent(const torrent_handle& h);
    -
    -        void set_http_settings(const http_settings& settings);
    -        void set_upload_rate_limit(int bytes_per_second);
    -
    -        std::auto_ptr<alert> pop_alert();
    -        void set_severity_level(alert::severity_t s);
    -
    -};
    -
    -

    Once it's created, it will spawn the main thread that will do all the work. -The main thread will be idle as long it doesn't have any torrents to participate in. -You add torrents through the add_torrent()-function where you give an -object representing the information found in the torrent file and the path where you -want to save the files. The save_path will be prepended to the directory- -structure in the torrent-file. add_torrent will throw duplicate_torrent exception -if the torrent already exists in the session.

    -

    The optional last parameter, resume_data can be given if up to date fast-resume data -is available. The fast-resume data can be acquired from a running torrent by calling -torrent_handle::write_resume_data(). See fast resume.

    -

    remove_torrent() will close all peer connections associated with the torrent and tell -the tracker that we've stopped participating in the swarm.

    -

    If the torrent you are trying to add already exists in the session (is either queued -for checking, being checked or downloading) add_torrent() will throw -duplicate_torrent which derives from std::exception.

    -

    The difference between the two constructors is that one of them takes a fingerprint -as argument. If this is ommited, the client will get a default fingerprint stating -the version of libtorrent. The fingerprint is a short string that will be used in -the peer-id to identify the client and the client's version. For more details see the -fingerprint class.

    -

    set_upload_rate_limit() set the maximum number of bytes allowed to be -sent to peers per second. This bandwidth is distributed among all the peers. If -you don't want to limit upload rate, you can set this to -1 (the default).

    -

    The destructor of session will notify all trackers that our torrents has 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 -timeout can be set with set_http_settings().

    -

    How to parse a torrent file and create a torrent_info object is described below.

    -

    The torrent_handle returned by add_torrent can be used to retrieve information -about the torrent's progress, its peers etc. It is also used to abort a torrent.

    -

    The constructor takes a listen port as argument, if the given port is busy it will -increase the port number by one and try again. If it still fails it will continue -increasing the port number until it succeeds or has failed 9 ports. This will -change in the future to give more control of the listen-port.

    -

    The pop_alert() function is the interface for retrieving alerts, warnings and -errors from libtorrent. If there hasn't occured any errors (matching your severity -level) pop_alert() will return a zero pointer. If there has been some error, it will -return a pointer to an alert object describing it. You can then use the query the -alert object for information about the error or message. To retrieve any alerts, you -have to select a severity level using set_severity_level(). It defaults to -alert::none, which means that you don't get any messages at all, ever. You have -the following levels to select among:

    - ---- - - - - - - - - - - - - - - - - - - - - -
    noneNo alert will ever have this severity level, which -effectively filters all messages.
    fatalFatal errors will have this severity level. Examples can -be disk full or something else that will make it -impossible to continue normal execution.
    criticalSignals errors that requires user interaction.
    warningMessages with the warning severity can be a tracker that -times out or responds with invalid data. It will be -retried automatically, and the possible next tracker in -a multitracker sequence will be tried. It does not -require any user interaction.
    infoEvents that can be considered normal, but still deserves -an event. This could be a piece hash that fails.
    debugThis will include alot of debug events that can be used -both for debugging libtorrent but also when debugging -other clients that are connected to libtorrent. It will -report strange behaviors among the connected peers.
    -

    When setting a severity level, you will receive messages of that severity and all -messages that are more sever. If you set alert::none (the default) you will not recieve -any events at all.

    -

    When you get an alert, you can use typeid() or dynamic_cast<> to get more detailed -information on exactly which type it is. i.e. what kind of error it is. You can also use a -dispatcher mechanism that's available in libtorrent.

    -

    TODO: describe the type dispatching mechanism

    -

    You can do a dynamic_cast to a specific alert type to get more message-specific information. -These are the different alert types.

    -
    -

    tracker_alert

    -

    This alert is generated on tracker time outs, premature disconnects, invalid response or -a HTTP response other than "200 OK". From the alert you can get the handle to the torrent -the tracker belongs to. This alert is generated as severity level warning.

    -
    -struct tracker_alert: alert
    -{
    -        tracker_alert(const torrent_handle& h, const std::string& msg);
    -        virtual std::auto_ptr<alert> clone() const;
    -
    -        torrent_handle handle;
    -};
    -
    -
    -
    -

    hash_failed_alert

    -

    This alert is generated when a finished piece fails its hash check. You can get the handle -to the torrent which got the failed piece and the index of the piece itself from the alert. -This alert is generated as severity level info.

    -
    -struct hash_failed_alert: alert
    -{
    -        hash_failed_alert(
    -                const torrent_handle& h
    -                , int index
    -                , const std::string& msg);
    -
    -        virtual std::auto_ptr<alert> clone() const;
    -
    -        torrent_handle handle;
    -        int piece_index;
    -};
    -
    -
    -
    -

    peer_error_alert

    -

    This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer -will be disconnected, but you get its peer-id from the alert. This alert is generated -as severity level debug.

    -
    -struct peer_error_alert: alert
    -{
    -        peer_error_alert(const peer_id& pid, const std::string& msg);
    -        virtual std::auto_ptr<alert> clone() const;
    -
    -        peer_id id;
    -};
    -
    -
    -
    -
    -

    parsing torrent files

    -

    The torrent files are bencoded. There are two functions in libtorrent that can encode and decode -bencoded data. They are:

    -
    -template<class InIt> entry bdecode(InIt start, InIt end);
    -template<class OutIt> void bencode(OutIt out, const entry& e);
    -
    -

    The entry class is the internal representation of the bencoded data -and it can be used to retreive information, an entry can also be build by -the program and given to bencode() to encode it into the OutIt -iterator.

    -

    The OutIt and InIt are iterators -(InputIterator_ and OutputIterator_ respectively). They -are templates and are usually instantiated as ostream_iterator_, -back_insert_iterator_ or istream_iterator_. These -functions will assume that the iterator refers to a character -(char). So, if you want to encode entry e into a buffer -in memory, you can do it like this:

    -
    -std::vector<char> buffer;
    -bencode(std::back_insert_iterator<std::vector<char> >(buf), e);
    -
    -

    If you want to decode a torrent file from a buffer in memory, you can do it like this:

    -
    -std::vector<char> buffer;
    -// ...
    -entry e = bdecode(buf.begin(), buf.end());
    -
    -

    Or, if you have a raw char buffer:

    -
    -const char* buf;
    -// ...
    -entry e = bdecode(buf, buf + data_size);
    -
    -

    Now we just need to know how to retrieve information from the entry.

    -

    If bdecode() encounters invalid encoded data in the range given to it -it will throw invalid_encoding.

    -
    -
    -

    entry

    -

    The entry class represents one node in a bencoded hierarchy. It works as a -variant type, it can be either a list, a dictionary (std::map), an integer -or a string. This is its synopsis:

    -
    -class entry
    -{
    -public:
    -
    -        typedef std::map<std::string, entry> dictionary_type;
    -        typedef std::string string_type;
    -        typedef std::vector<entry> list_type;
    -        typedef implementation-defined integer_type;
    -
    -        enum data_type
    -        {
    -                int_t,
    -                string_t,
    -                list_t,
    -                dictionary_t,
    -                undefined_t
    -        };
    -
    -        data_type type() const;
    -
    -        entry();
    -        entry(data_type t);
    -        entry(const entry& e);
    -
    -        void operator=(const entry& e);
    -
    -        integer_type& integer()
    -        const integer_type& integer() const;
    -        string_type& string();
    -        const string_type& string() const;
    -        list_type& list();
    -        const list_type& list() const;
    -        dictionary_type& dict();
    -        const dictionary_type& dict() const;
    -
    -        void print(std::ostream& os, int indent = 0) const;
    -};
    -
    -

    The integer(), string(), list() and dict() functions -are accessorts that return the respecive type. If the entry object isn't of the -type you request, the accessor will throw type_error (which derives from -std::runtime_error). You can ask an entry for its type through the -type() function.

    -

    The print() function is there for debug purposes only.

    -

    If you want to create an entry you give it the type you want it to have in its -constructor, and then use one of the non-const accessors to get a reference which you then -can assign the value you want it to have.

    -

    The typical code to get info from a torrent file will then look like this:

    -
    -entry torrent_file;
    -// ...
    -
    -const entry::dictionary_type& dict = torrent_file.dict();
    -entry::dictionary_type::const_iterator i;
    -i = dict.find("announce");
    -if (i != dict.end())
    -{
    -        std::string tracker_url= i->second.string();
    -        std::cout << tracker_url << "\n";
    -}
    -
    -

    To make it easier to extract information from a torren file, the class torrent_info -exists.

    -
    -
    -

    torrent_info

    -

    The torrent_info has the following synopsis:

    -
    -class torrent_info
    -{
    -public:
    -
    -        torrent_info(const entry& torrent_file)
    -
    -        typedef std::vector>file>::const_iterator file_iterator;
    -        typedef std::vector<file>::const_reverse_iterator reverse_file_iterator;
    -
    -        file_iterator begin_files() const;
    -        file_iterator end_files() const;
    -        reverse_file_iterator rbegin_files() const;
    -        reverse_file_iterator rend_files() const;
    -
    -        std::size_t num_files() const;
    -        const file& file_at(int index) const;
    -
    -        const std::vector<announce_entry>& trackers() const;
    -
    -        int prioritize_tracker(int index);
    -
    -        entry::integer_type total_size() const;
    -        entry::integer_type piece_length() const;
    -        std::size_t num_pieces() const;
    -        const sha1_hash& info_hash() const;
    -        const std::stirng& name() const;
    -        const std::string& comment() const;
    -        boost::posiz_time::ptime creation_date() const;
    -
    -
    -        void print(std::ostream& os) const;
    -
    -        entry::integer_type piece_size(unsigned int index) const;
    -        const sha1_hash& hash_for_piece(unsigned int index) const;
    -};
    -
    -

    This class will need some explanation. First of all, to get a list of all files -in the torrent, you can use begin_files(), end_files(), -rbegin_files() and rend_files(). These will give you standard vector -iterators with the type file.

    -
    -struct file
    -{
    -        std::string path;
    -        std::string filename;
    -        entry::integer_type size;
    -};
    -
    -

    If you need index-access to files you can use the num_files() and file_at() -to access files using indices.

    -

    The print() function is there for debug purposes only. It will print the info from -the torrent file to the given outstream.

    -

    name() returns the name of the torrent.

    -

    The trackers() function will return a sorted vector of announce_entry. -Each announce entry contains a string, which is the tracker url, and a tier index. The -tier index is the high-level priority. No matter which trackers that works or not, the -ones with lower tier will always be tried before the one with higher tier number.

    -
    -struct announce_entry
    -{
    -        std::string url;
    -        int tier;
    -};
    -
    -

    The prioritize_tracker() is used internally to move a tracker to the front -of its tier group. i.e. It will never be moved pass a tracker with a different tier -number. For more information about how multiple trackers are dealt with, see the -specification.

    -

    total_size(), piece_length() and num_pieces() returns the total -number of bytes the torrent-file represents (all the files in it), the number of byte for -each piece and the total number of pieces, respectively. The difference between -piece_size() and piece_length() is that piece_size() takes -the piece index as argument and gives you the exact size of that piece. It will always -be the same as piece_length() except in the case of the last piece, which may -be smaller.

    -

    hash_for_piece() takes a piece-index and returns the 20-bytes sha1-hash for that -piece and info_hash() returns the 20-bytes sha1-hash for the info-section of the -torrent file. For more information on the sha1_hash, see the big_number class.

    -

    comment() returns the comment associated with the torrent. If there's no comment, -it will return an empty string. creation_date() returns a boost::posix_time::ptime -object, representing the time when this torrent file was created. If there's no timestamp -in the torrent file, this will return a date of january 1:st 1970.

    -
    -
    -

    torrent_handle

    -

    You will usually have to store your torrent handles somewhere, since it's the -object through which you retrieve infromation about the torrent and aborts the torrent. -Its declaration looks like this:

    -
    -struct torrent_handle
    -{
    -        torrent_handle();
    -
    -        torrent_status status();
    -        void get_download_queue(std::vector<partial_piece_info>& queue);
    -        void get_peer_info(std::vector<peer_info>& v);
    -        const torrent_info& get_torrent_info();
    -        bool is_valid();
    -
    -        void write_resume_data(std::vector<char>& data);
    -
    -        boost::filsystem::path save_path() const;
    -
    -        void set_max_uploads(int max_uploads);
    -
    -        sha1_hash info_hash() const;
    -
    -        bool operator==(const torrent_handle&) const;
    -        bool operator!=(const torrent_handle&) const;
    -        bool operator<(const torrent_handle&) const;
    -};
    -
    -

    The default constructor will initialize the handle to an invalid state. Which means you cannot -perform any operation on it, unless you first assign it a valid handle. If you try to perform -any operation on an uninitialized handle, it will throw invalid_handle.

    -

    save_path() returns the path that was given to add_torrent() when this torrent -was started.

    -

    info_hash() returns the info hash for the torrent.

    -

    set_max_uploads() sets the maximum number of peers that's unchoked at the same time on this -torrent. If you set this to -1, there will be no limit.

    -

    write_resume_data() takes a non-const reference to a char-vector, that vector will be filled -with the fast-resume data. For more information about how fast-resume works, see fast resume.

    -
    -

    status()

    -

    status() will return a structure with information about the status of this -torrent. If the torrent_handle is invalid, it will throw invalid_handle exception. -It contains the following fields:

    -
    -struct torrent_status
    -{
    -        enum state_t
    -        {
    -                invalid_handle,
    -                queued_for_checking,
    -                checking_files,
    -                connecting_to_tracker,
    -                downloading,
    -                seeding
    -        };
    -
    -        state_t state;
    -        float progress;
    -        boost::posix_time::time_duration next_announce;
    -
    -        std::size_t total_download;
    -        std::size_t total_upload;
    -
    -        std::size_t total_payload_download;
    -        std::size_t total_payload_upload;
    -
    -        float download_rate;
    -        float upload_rate;
    -
    -        std::vector<bool> pieces;
    -        std::size_t total_done;
    -};
    -
    -

    progress is a value in the range [0, 1], that represents the progress of the -torrent's current task. It may be checking files or downloading. The torrent's -current task is in the state member, it will be one of the following:

    - ---- - - - - - - - - - - - - - - - - - -
    queued_for_checkingThe torrent is in the queue for being checked. But there -currently is another torrent that are being checked. -This torrent will wait for its turn.
    checking_filesThe torrent has not started its download yet, and is -currently checking existing files.
    connecting_to_trackerThe torrent has sent a request to the tracker and is -currently waiting for a response
    downloadingThe torrent is being downloaded. This is the state -most torrents will be in most of the time. The progress -meter will tell how much of the files that has been -downloaded.
    seedingIn this state the torrent has finished downloading and -is a pure seeder.
    -

    next_announce is the time until the torrent will announce itself to the tracker.

    -

    total_download and total_upload is the number of bytes downloaded and -uploaded to all peers, accumulated, this session only.

    -

    total_payload_download and total_payload_upload counts the amount of bytes -send and received this session, but only the actual oayload data (i.e the interesting -data), these counters ignore any protocol overhead.

    -

    pieces is the bitmask that representw which pieces we have (set to true) and -the pieces we don't have.

    -

    download_rate and upload_rate are the total rates for all peers for this -torrent. These will usually have better precision than summing the rates from -all peers. The rates are given as the number of bytes per second.

    -

    total_done is the total number of bytes of the file(s) that we have.

    -
    -
    -

    get_download_queue()

    -

    get_download_queue() takes a non-const reference to a vector which it will fill -information about pieces that are partially downloaded or not downloaded at all but partially -requested. The entry in the vector (partial_piece_info) looks like this:

    -
    -struct partial_piece_info
    -{
    -        enum { max_blocks_per_piece };
    -        int piece_index;
    -        int blocks_in_piece;
    -        std::bitset<max_blocks_per_piece> requested_blocks;
    -        std::bitset<max_blocks_per_piece> finished_blocks;
    -        peer_id peer[max_blocks_per_piece];
    -        int num_downloads[max_blocks_per_piece];
    -};
    -
    -

    piece_index is the index of the piece in question. blocks_in_piece is the -number of blocks in this particular piece. This number will be the same for most pieces, but -the last piece may have fewer blocks than the standard pieces.

    -

    requested_blocks is a bitset with one bit per block in the piece. If a bit is set, it -means that that block has been requested, but not necessarily fully downloaded yet. To know -from whom the block has been requested, have a look in the peer array. The bit-index -in the requested_blocks and finished_blocks correspons to the array-index into -peers and num_downloads. The array of peers is contains the id of the -peer the piece was requested from. If a piece hasn't been requested (the bit in -requested_blocks is not set) the peer array entry will be undefined.

    -

    The finished_blocks is a bitset where each bit says if the block is fully downloaded -or not. And the num_downloads array says how many times that block has been downloaded. -When a piece fails a hash verification, single blocks may be redownloaded to see if the hash teast -may pass then.

    -
    -
    -

    get_peer_info()

    -

    get_peer_info() takes a reference to a vector that will be cleared and filled -with one entry for each peer connected to this torrent, given the handle is valid. If the -torrent_handle is invalid, it will throw invalid_handle exception. Each entry in -the vector contains information about that particular peer. It contains the following -fields:

    -
    -struct peer_info
    -{
    -        enum
    -        {
    -                interesting = 0x1,
    -                choked = 0x2,
    -                remote_interested = 0x4,
    -                remote_choked = 0x8,
    -                supports_extensions = 0x10
    -        };
    -        unsigned int flags;
    -        address ip;
    -        float up_speed;
    -        float down_speed;
    -        unsigned int total_download;
    -        unsigned int total_upload;
    -        peer_id id;
    -        std::vector<bool> pieces;
    -        int upload_limit;
    -        int upload_ceiling;
    -
    -        int load_balancing;
    -
    -        int downloading_piece_index;
    -        int downloading_block_index;
    -        int downloading_progress;
    -        int downloading_total;
    -};
    -
    -

    The flags attribute tells you in which state the peer is. It is set to -any combination of the four enums above. Where interesting means that we -are interested in pieces from this peer. choked means that we have -choked this peer. remote_interested and remote_choked means the -same thing but that the peer is interested in pieces from us and the peer has choked -us. support_extensions means that this peer supports the extension protocol -as described by nolar.

    -

    The ip field is the IP-address to this peer. Its type is a wrapper around the -actual address and the port number. See address class.

    -

    up_speed and down_speed is the current upload and download speed -we have to and from this peer. These figures are updated aproximately once every second.

    -

    total_download and total_upload are the total number of bytes downloaded -from and uploaded to this peer. These numbers do not include the protocol chatter, but only -the payload data.

    -

    id is the peer's id as used in the bit torrent protocol. This id can be used to -extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer -is using.

    -

    pieces is a vector of booleans that has as many entries as there are pieces -in the torrent. Each boolean tells you if the peer has that piece (if it's set to true) -or if the peer miss that piece (set to false).

    -

    upload_limit is the number of bytes per second we are allowed to send to this -peer every second. It may be -1 if there's no limit. The upload limits of all peers -should sum up to the upload limit set by session::set_upload_limit.

    -

    upload_ceiling is the current maximum allowed upload rate given the cownload -rate and share ratio. If the global upload rate is inlimited, the upload_limit -for every peer will be the same as their upload_ceiling.

    -

    load_balancing is a measurment of the balancing of free download (that we get) -and free upload that we give. Every peer gets a certain amount of free upload, but -this member says how much extra free upload this peer has got. If it is a negative -number it means that this was a peer from which we have got this amount of free -download.

    -

    You can know which piece, and which part of that piece, that is currently being -downloaded from a specific peer by looking at the next four members. -downloading_piece_index is the index of the piece that is currently being downloaded. -This may be set to -1 if there's currently no piece downloading from this peer. If it is ->= 0, the other three members are valid. downloading_block_index is the index of the -block (or sub-piece) that is being downloaded. downloading_progress is the number -of bytes of this block we have received from the peer, and downloading_total is -the total number of bytes in this block.

    -
    -
    -

    get_torrent_info()

    -

    Returns a const reference to the torrent_info object associated with this torrent. -This reference is valid as long as the torrent_handle is valid, no longer. If the -torrent_handle is invalid, invalid_handle exception will be thrown.

    -
    -
    -

    is_valid()

    -

    Returns true if this handle refers to a valid torrent and false if it hasn't been initialized -or if the torrent it refers to has been aborted.

    -
    -
    -
    -

    address

    -

    The address class represents a name of a network endpoint (usually referred to as -IP-address) and a port number. This is the same thing as a sockaddr_in would contain. -Its declaration looks like this:

    -
    -class address
    -{
    -public:
    -        address();
    -        address(unsigned char a
    -                , unsigned char b
    -                , unsigned char c
    -                , unsigned char d
    -                , unsigned short  port);
    -        address(unsigned int addr, unsigned short port);
    -        address(const std::string& addr, unsigned short port);
    -        address(const address& a);
    -        ~address();
    -
    -        std::string as_string() const;
    -        unsigned int ip() const;
    -        unsigned short port() const;
    -
    -        bool operator<(const address& a) const;
    -        bool operator!=(const address& a) const;
    -        bool operator==(const address& a) const;
    -};
    -
    -

    It is less-than comparable to make it possible to use it as a key in a map. as_string() may block -while it does the DNS lookup, it returns a string that points to the address represented by the object.

    -

    ip() will return the 32-bit ip-address as an integer. port() returns the port number.

    -
    -
    -

    http_settings

    -

    You have some control over tracker requests through the http_settings object. You -create it and fill it with your settings and the use session::set_http_settings() -to apply them. You have control over proxy and authorization settings and also the user-agent -that will be sent to the tracker. The user-agent is a good way to identify your client.

    -
    -struct http_settings
    -{
    -        http_settings();
    -        std::string proxy_ip;
    -        int proxy_port;
    -        std::string proxy_login;
    -        std::string proxy_password;
    -        std::string user_agent;
    -        int tracker_timeout;
    -        int tracker_maximum_response_length;
    -};
    -
    -

    proxy_ip may be a hostname or ip to a http proxy to use. If this is -an empty string, no http proxy will be used.

    -

    proxy_port is the port on which the http proxy listens. If proxy_ip -is empty, this will be ignored.

    -

    proxy_login should be the login username for the http proxy, if this -empty, the http proxy will be trid to be used without authentication.

    -

    proxy_password the password string for the http proxy.

    -

    user_agent this is the client identification to the tracker. It will -be followed by the string "(libtorrent)" to identify that this library -is being used. This should be set to your client's name and version number.

    -

    tracker_timeout is the number of seconds the tracker connection will -wait until it considers the tracker to have timed-out. Default value is 10 -seconds.

    -

    tracker_maximum_response_length is the maximum number of bytes in a -tracker response. If a response size passes this number it will be rejected -and the connection will be closed. On gzipped responses this size is measured -on the uncompressed data. So, if you get 20 bytes of gzip response that'll -expand to 2 megs, it will be interrupted before the entire response has been -uncompressed (given your limit is lower than 2 megs). Default limit is -1 megabyte.

    -
    -
    -

    big_number

    -

    Both the peer_id and sha1_hash types are typedefs of the class -big_number. It represents 20 bytes of data. Its synopsis follows:

    -
    -class big_number
    -{
    -public:
    -        bool operator==(const big_number& n) const;
    -        bool operator!=(const big_number& n) const;
    -        bool operator<(const big_number& n) const;
    -
    -        const unsigned char* begin() const;
    -        const unsigned char* end() const;
    -
    -        unsigned char* begin();
    -        unsigned char* end();
    -};
    -
    -

    The iterators gives you access to individual bytes.

    -
    -
    -

    hasher

    -

    This class creates sha1-hashes. Its declaration looks like this:

    -
    -class hasher
    -{
    -public:
    -        hasher();
    -
    -        void update(const char* data, unsigned int len);
    -        sha1_hash final();
    -        void reset();
    -};
    -
    -

    You use it by first instantiating it, then call update() to feed it -with data. i.e. you don't have to keep the entire buffer of which you want to -create the hash in memory. You can feed the hasher parts of it at a time. When -You have fed the hasher with all the data, you call final() and it -will return the sha1-hash of the data.

    -

    If you want to reuse the hasher object once you have created a hash, you have to -call reset() to reinitialize it.

    -

    The sha1-algorithm used was implemented by Steve Reid and released as public domain. -For more info, see src/sha1.c.

    -
    -
    -

    fingerprint

    -

    The fingerprint class represents information about a client and its version. It is used -to encode this information into the client's peer id.

    -

    This is the class declaration:

    -
    -struct fingerprint
    -{
    -        fingerprint(const char* id_string, int major, int minor, int revision, int tag);
    -
    -        std::string to_string() const;
    -
    -        char id[2];
    -        char major_version;
    -        char minor_version;
    -        char revision_version;
    -        char tag_version;
    -
    -};
    -
    -

    The constructor takes a const char* that should point to a string constant containing -exactly two characters. These are the characters that should be unique for your client. Make -sure not to clash with anybody else. Here are some taken id's:

    - ---- - - - - - - - - - - - - - - - - - - - -
    id charsclient
    'AZ'Azureus
    'LT'libtorrent (default)
    'BX'BittorrentX
    'MT'Moonlight Torrent
    -

    The major, minor, revision and tag parameters are used to identify the -version of your client. All these numbers must be within the range [0, 9].

    -

    to_string() will generate the actual string put in the peer-id, and return it.

    -
    -
    -

    alert

    -

    The alert class is used to pass messages of events from the libtorrent code -to the user. It is a base class that specific messages are derived from. This -is its synopsis:

    -
    -class alert
    -{
    -public:
    -
    -        enum severity_t { debug, info, warning, critital, fatal, none };
    -
    -        alert(severity_t severity, const std::string& msg);
    -
    -        virtual ~alert() {}
    -
    -        const std::string& msg() const;
    -        severity_t severity() const;
    -
    -        virtual std::auto_ptr<alert> clone() const = 0;
    -};
    -
    -
    -
    -

    exceptions

    -

    There are a number of exceptions that can be thrown from different places in libtorrent, -here's a complete list with description.

    -
    -

    invalid_handle

    -

    This exception is thrown when querying information from a torrent_handle that hasn't -been initialized or that has become invalid.

    -
    -struct invalid_handle: std::exception
    -{
    -        const char* what() const throw();
    -};
    -
    -
    -
    -

    duplicate_torrent

    -

    This is thrown by session::add_torrent() if the torrent already has been added to -the session.

    -
    -struct duplicate_torrent: std::exception
    -{
    -        const char* what() const throw();
    -};
    -
    -
    -
    -

    invalid_encoding

    -

    This is thrown by bdecode() if the input data is not a valid bencoding.

    -
    -struct invalid_encoding: std::exception
    -{
    -        const char* what() const throw();
    -};
    -
    -
    -
    -

    type_error

    -

    This is thrown from the accessors of entry if the data type of the entry doesn't -match the type you want to extract from it.

    -
    -struct type_error: std::runtime_error
    -{
    -        type_error(const char* error);
    -};
    -
    -
    -
    -

    invalid_torrent_file

    -

    This exception is thrown from the constructor of torrent_info if the given bencoded information -doesn't meet the requirements on what information has to be present in a torrent file.

    -
    -struct invalid_torrent_file: std::exception
    -{
    -        const char* what() const throw();
    -};
    -
    -
    -
    -
    -

    example usage

    -
    -

    dump_torrent

    -

    This is an example of a program that will take a torrent-file as a parameter and -print information about it to std out:

    -
    -#include <iostream>
    -#include <fstream>
    -#include <iterator>
    -#include <exception>
    -#include <iomanip>
    -
    -#include "libtorrent/entry.hpp"
    -#include "libtorrent/bencode.hpp"
    -#include "libtorrent/torrent_info.hpp"
    -
    -
    -int main(int argc, char* argv[])
    -{
    -        using namespace libtorrent;
    -
    -        if (argc != 2)
    -        {
    -                std::cerr << "usage: dump_torrent torrent-file\n";
    -                return 1;
    -        }
    -
    -        try
    -        {
    -                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);
    -
    -                // print info about torrent
    -                std::cout << "\n\n----- torrent file info -----\n\n";
    -                std::cout << "trackers:\n";
    -                for (std::vector<announce_entry>::const_iterator i = t.trackers().begin();
    -                        i != t.trackers().end();
    -                        ++i)
    -                {
    -                        std::cout << i->tier << ": " << i->url << "\n";
    -                }
    -
    -                std::cout << "number of pieces: " << t.num_pieces() << "\n";
    -                std::cout << "piece length: " << t.piece_length() << "\n";
    -                std::cout << "files:\n";
    -                for (torrent_info::file_iterator i = t.begin_files();
    -                        i != t.end_files();
    -                        ++i)
    -                {
    -                        std::cout << "  " << std::setw(11) << i->size
    -                        << "  " << i->path << " " << i->filename << "\n";
    -                }
    -                
    -        }
    -        catch (std::exception& e)
    -        {
    -                std::cout << e.what() << "\n";
    -        }
    -
    -        return 0;
    -}
    -
    -
    -
    -

    simple client

    -

    This is a simple client. It doesn't have much output to keep it simple:

    -
    -#include <iostream>
    -#include <fstream>
    -#include <iterator>
    -#include <exception>
    -
    -#include <boost/format.hpp>
    -#include <boost/date_time/posix_time/posix_time.hpp>
    -
    -#include "libtorrent/entry.hpp"
    -#include "libtorrent/bencode.hpp"
    -#include "libtorrent/session.hpp"
    -#include "libtorrent/http_settings.hpp"
    -
    -int main(int argc, char* argv[])
    -{
    -        using namespace libtorrent;
    -
    -        if (argc != 2)
    -        {
    -                std::cerr << "usage: ./simple_cient torrent-file\n"
    -                        "to stop the client, press return.\n";
    -                return 1;
    -        }
    -
    -        try
    -        {
    -                session s(6881);
    -
    -                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, "");
    -                        
    -                // wait for the user to end
    -                char a;
    -                std::cin.unsetf(std::ios_base::skipws);
    -                std::cin >> a;
    -        }
    -        catch (std::exception& e)
    -        {
    -                std::cout << e.what() << "\n";
    -        }
    -        return 0;
    -}
    -
    -
    -
    -
    -

    fast resume

    -

    The fast resume mechanism is a way to remember which pieces are downloaded and where they -are put between sessions. You can generate fast resume data by calling -torrent_handle::write_resume_data() on torrent_handle. You can then save this data -to disk and use it when resuming the torrent. libtorrent will not check the piece hashes -then, and rely on the information given in the fast-resume data. The fast-resume data -also contains information about which bocks in the unfinished pieces were downloaded, so -it will not have to start from scratch on the partially downloaded pieces.

    -

    To use the fast-resume data you simply give it to session::add_torrent(), and it -will skip the time consuming checks. It may have to do the checking anyway, if the -fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will -not trust the fast-resume data and just do the checking.

    -
    -

    file format

    -

    The format of the fast-resume data is as follows, given that all -4-byte integers are stored as big-endian:

    -
    -20 bytes, the info_hash for the torrent
    -4 bytes, the number of allocated slots in the storage
    -for each slot
    -        4 bytes, piece index in this slot,
    -                 -1 means there's no storage for the slot
    -                 -2 means there's no piece at this slot, it's free
    -4 bytes, the number of blocks per piece.
    -         this must be piece_size / 16k or 1 if piece_size is < 16k
    -         and can be 128 at max.
    -4 bytes, the number of unfinished pieces
    -for each unfinished piece
    -        4 bytes, index of the unfinished piece
    -        blocks_per_piece / 8 bytes, the bitmask describing which
    -                                    blocks are finished in this piece.
    -
    -
    -
    -
    -

    Feedback

    +

    Feedback

    There's a mailing list.

    You can usually find me as hydri in #btports @ irc.freenode.net.

    -

    Aknowledgements

    +

    Aknowledgements

    Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003

    Contributions by Magnus Jonsson

    Thanks to Reimond Retz for bugfixes, suggestions and testing

    diff --git a/docs/index.rst b/docs/index.rst index 4b8de3458..58ea4d88d 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,18 +3,15 @@ libtorrent ========== -=================== =============== -`sourceforge page`_ `mailing list`_ -=================== =============== +=================== ======= =========== =============== +`sourceforge page`_ manual_ screenshot_ `mailing list`_ +=================== ======= =========== =============== .. _sourceforge page: http://www.sourceforge.net/projects/libtorrent +.. _manual: manual.html +.. _screenshot: libtorrent_screen.png .. _mailing list: http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss -.. contents:: - -introduction -============ - libtorrent is a C++ library that aims to be a good alternative to all the `other bittorrent implementations`__ around. It is a library and not a full featured client, although it comes with a working @@ -28,1228 +25,6 @@ The main goals of libtorrent are: * to be memory efficient * to be very easy to use -libtorrent is not finished. It is an ongoing project (including this documentation). -The current state includes the following features: - - * multitracker extension support (as `described by TheShadow`__) - * serves multiple torrents on a single port and a single thread - * supports http proxies and proxy authentication - * gzipped tracker-responses - * piece picking on block-level (as opposed to piece-level) like in Azureus_ - * queues torrents for file check, instead of checking all of them in parallel. - * uses separate threads for checking files and for main downloader, with a fool-proof - thread-safe library interface. (i.e. There's no way for the user to cause a deadlock). - * can limit the upload bandwidth usage and the maximum number of unchoked peers - * piece-wise file allocation - * tries to maintain a 1:1 share ratio between all peers but also shifts free - download to peers as free upload. To maintain a global 1:1 ratio. - * fast resume support, a way to get rid of the costly piece check at the start - of a resumed torrent. Saves the storage state in a separate fast-resume file. - -__ http://home.elp.rr.com/tur/multitracker-spec.txt -.. _Azureus: http://azureus.sourceforge.net - -Functions that are yet to be implemented: - - * number of connections limit - * better handling of peers that send bad data - * ip-filters - * file-level piece priority - -libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread, -boost.filesystem, boost.date_time and various other boost libraries as well as zlib. - -libtorrent has been successfully compiled and tested on: - - * Cygwin GCC 3.3.1 - * Windows 2000 vc7.1 - * Linux x86 (debian) GCC 3.0 - -It does not compile on - - * GCC 2.95 - -building -======== - -To build libtorrent you need boost_ and bjam installed. -Then you can use ``bjam`` to build libtorrent. - -.. _boost: http://www.boost.org - -To make bjam work, you need to set the environment variable ``BOOST_ROOT`` to the -path where boost is installed (e.g. c:\boost_1_30_2 on windows). Then you can just run -``bjam`` in the libtorrent directory. - -The Jamfile doesn't work yet. On unix-systems you can use the makefile however. You -first have to build boost.thread and boost.filesystem. You do this by, in the directory -'boost-1.30.2/tools/build/jam_src' run the build script ``./build.sh``. This should -produce at least one folder with the 'bin' prefix (and the rest of the name describes -your platform). Put the files in that folder somewhere in your path. - -You can then invoke ``bjam`` in the directories 'boost-1.30.2/libs/thread/build', -'boost-1.30.2/libs/date_time/build' and 'boost-1.30.2/libs/filesystem/build'. That will -produce the needed libraries. Put these libraries in the libtorrent root directory. -You then have to modify the makefile to use you prefered compiler and to have the -correct path to your boost istallation. - -Then the makefile should be able to do the rest. - -When building (with boost 1.30.2) on linux and solaris however, I found that I had to make the following -modifications to the boost.date-time library. In the file: -'boost-1.30.2/boost/date_time/gregorian_calendar.hpp' line 59. Prepend 'boost/date_time/' -to the include path. - -And the second modification was in the file: -'boost-1.30.2/boost/date_time/microsec_time_clock.hpp' add the following include at the top -of the file:: - - #include "boost/cstdint.hpp" - -In developer studio, you may have to set the compiler options "force conformance in for -loop scope" and "treat wchar_t as built-in type" to Yes. - -TODO: more detailed build instructions. - - - - - -using -===== - -The interface of libtorrent consists of a few classes. The main class is -the ``session``, it contains the main loop that serves all torrents. - - - -session -------- - -The ``session`` class has the following synopsis:: - - class session: public boost::noncopyable - { - session(int listen_port, const fingerprint& print); - session(int listen_port); - - torrent_handle add_torrent(const torrent_info& t, const std::string& save_path); - torrent_handle add_torrent( - const torrent_info& t - , const std::string& save_path - , const std::vector& resume_data); - - void remove_torrent(const torrent_handle& h); - - void set_http_settings(const http_settings& settings); - void set_upload_rate_limit(int bytes_per_second); - - std::auto_ptr pop_alert(); - void set_severity_level(alert::severity_t s); - - }; - -Once it's created, it will spawn the main thread that will do all the work. -The main thread will be idle as long it doesn't have any torrents to participate in. -You add torrents through the ``add_torrent()``-function where you give an -object representing the information found in the torrent file and the path where you -want to save the files. The ``save_path`` will be prepended to the directory- -structure in the torrent-file. ``add_torrent`` will throw ``duplicate_torrent`` exception -if the torrent already exists in the session. - -The optional last parameter, ``resume_data`` can be given if up to date fast-resume data -is available. The fast-resume data can be acquired from a running torrent by calling -``torrent_handle::write_resume_data()``. See `fast resume`_. - -``remove_torrent()`` will close all peer connections associated with the torrent and tell -the tracker that we've stopped participating in the swarm. - -If the torrent you are trying to add already exists in the session (is either queued -for checking, being checked or downloading) ``add_torrent()`` will throw -``duplicate_torrent`` which derives from ``std::exception``. - -The difference between the two constructors is that one of them takes a fingerprint -as argument. If this is ommited, the client will get a default fingerprint stating -the version of libtorrent. The fingerprint is a short string that will be used in -the peer-id to identify the client and the client's version. For more details see the -fingerprint class. - -``set_upload_rate_limit()`` set the maximum number of bytes allowed to be -sent to peers per second. This bandwidth is distributed among all the peers. If -you don't want to limit upload rate, you can set this to -1 (the default). - -The destructor of session will notify all trackers that our torrents has 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 -timeout can be set with ``set_http_settings()``. - -How to parse a torrent file and create a ``torrent_info`` object is described below. - -The torrent_handle_ returned by ``add_torrent`` can be used to retrieve information -about the torrent's progress, its peers etc. It is also used to abort a torrent. - -The constructor takes a listen port as argument, if the given port is busy it will -increase the port number by one and try again. If it still fails it will continue -increasing the port number until it succeeds or has failed 9 ports. *This will -change in the future to give more control of the listen-port.* - -The ``pop_alert()`` function is the interface for retrieving alerts, warnings and -errors from libtorrent. If there hasn't occured any errors (matching your severity -level) ``pop_alert()`` will return a zero pointer. If there has been some error, it will -return a pointer to an alert object describing it. You can then use the query the -alert_ object for information about the error or message. To retrieve any alerts, you -have to select a severity level using ``set_severity_level()``. It defaults to -``alert::none``, which means that you don't get any messages at all, ever. You have -the following levels to select among: - -+--------------+----------------------------------------------------------+ -| ``none`` | No alert will ever have this severity level, which | -| | effectively filters all messages. | -| | | -+--------------+----------------------------------------------------------+ -| ``fatal`` | Fatal errors will have this severity level. Examples can | -| | be disk full or something else that will make it | -| | impossible to continue normal execution. | -| | | -+--------------+----------------------------------------------------------+ -| ``critical`` | Signals errors that requires user interaction. | -| | | -+--------------+----------------------------------------------------------+ -| ``warning`` | Messages with the warning severity can be a tracker that | -| | times out or responds with invalid data. It will be | -| | retried automatically, and the possible next tracker in | -| | a multitracker sequence will be tried. It does not | -| | require any user interaction. | -| | | -+--------------+----------------------------------------------------------+ -| ``info`` | Events that can be considered normal, but still deserves | -| | an event. This could be a piece hash that fails. | -| | | -+--------------+----------------------------------------------------------+ -| ``debug`` | This will include alot of debug events that can be used | -| | both for debugging libtorrent but also when debugging | -| | other clients that are connected to libtorrent. It will | -| | report strange behaviors among the connected peers. | -| | | -+--------------+----------------------------------------------------------+ - -When setting a severity level, you will receive messages of that severity and all -messages that are more sever. If you set ``alert::none`` (the default) you will not recieve -any events at all. - -When you get an alert, you can use ``typeid()`` or ``dynamic_cast<>`` to get more detailed -information on exactly which type it is. i.e. what kind of error it is. You can also use a -dispatcher mechanism that's available in libtorrent. - -TODO: describe the type dispatching mechanism - -You can do a ``dynamic_cast`` to a specific alert type to get more message-specific information. -These are the different alert types. - -tracker_alert -~~~~~~~~~~~~~ - -This alert is generated on tracker time outs, premature disconnects, invalid response or -a HTTP response other than "200 OK". From the alert you can get the handle to the torrent -the tracker belongs to. This alert is generated as severity level ``warning``. - -:: - - struct tracker_alert: alert - { - tracker_alert(const torrent_handle& h, const std::string& msg); - virtual std::auto_ptr clone() const; - - torrent_handle handle; - }; - -hash_failed_alert -~~~~~~~~~~~~~~~~~ - -This alert is generated when a finished piece fails its hash check. You can get the handle -to the torrent which got the failed piece and the index of the piece itself from the alert. -This alert is generated as severity level ``info``. - -:: - - struct hash_failed_alert: alert - { - hash_failed_alert( - const torrent_handle& h - , int index - , const std::string& msg); - - virtual std::auto_ptr clone() const; - - torrent_handle handle; - int piece_index; - }; - -peer_error_alert -~~~~~~~~~~~~~~~~ - -This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer -will be disconnected, but you get its peer-id from the alert. This alert is generated -as severity level ``debug``. - -:: - - struct peer_error_alert: alert - { - peer_error_alert(const peer_id& pid, const std::string& msg); - virtual std::auto_ptr clone() const; - - peer_id id; - }; - - -parsing torrent files ---------------------- - -The torrent files are bencoded__. There are two functions in libtorrent that can encode and decode -bencoded data. They are:: - - template entry bdecode(InIt start, InIt end); - template void bencode(OutIt out, const entry& e); - -__ http://bitconjurer.org/BitTorrent/protocol.html - - -The ``entry`` class is the internal representation of the bencoded data -and it can be used to retreive information, an entry can also be build by -the program and given to ``bencode()`` to encode it into the ``OutIt`` -iterator. - -The ``OutIt`` and ``InIt`` are iterators -(``InputIterator_`` and ``OutputIterator_`` respectively). They -are templates and are usually instantiated as ``ostream_iterator_``, -``back_insert_iterator_`` or ``istream_iterator_``. These -functions will assume that the iterator refers to a character -(``char``). So, if you want to encode entry ``e`` into a buffer -in memory, you can do it like this:: - - std::vector buffer; - bencode(std::back_insert_iterator >(buf), e); - -.. _InputIterator: http://www.sgi.com/tech/stl/InputIterator.html -.. _OutputIterator: http://www.sgi.com/tech/stl/OutputIterator.html -.. _ostream_iterator: http://www.sgi.com/tech/stl/ostream_iterator.html -.. _back_insert_iterator: http://www.sgi.com/tech/stl/back_insert_iterator.html -.. _istream_iterator: http://www.sgi.com/tech/stl/istream_iterator.html - - -If you want to decode a torrent file from a buffer in memory, you can do it like this:: - - std::vector buffer; - // ... - entry e = bdecode(buf.begin(), buf.end()); - -Or, if you have a raw char buffer:: - - const char* buf; - // ... - entry e = bdecode(buf, buf + data_size); - -Now we just need to know how to retrieve information from the ``entry``. - -If ``bdecode()`` encounters invalid encoded data in the range given to it -it will throw invalid_encoding_. - - - -entry ------ - -The ``entry`` class represents one node in a bencoded hierarchy. It works as a -variant type, it can be either a list, a dictionary (``std::map``), an integer -or a string. This is its synopsis:: - - class entry - { - public: - - typedef std::map dictionary_type; - typedef std::string string_type; - typedef std::vector list_type; - typedef implementation-defined integer_type; - - enum data_type - { - int_t, - string_t, - list_t, - dictionary_t, - undefined_t - }; - - data_type type() const; - - entry(); - entry(data_type t); - entry(const entry& e); - - void operator=(const entry& e); - - integer_type& integer() - const integer_type& integer() const; - string_type& string(); - const string_type& string() const; - list_type& list(); - const list_type& list() const; - dictionary_type& dict(); - const dictionary_type& dict() const; - - void print(std::ostream& os, int indent = 0) const; - }; - -The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions -are accessorts that return the respecive type. If the ``entry`` object isn't of the -type you request, the accessor will throw type_error_ (which derives from -``std::runtime_error``). You can ask an ``entry`` for its type through the -``type()`` function. - -The ``print()`` function is there for debug purposes only. - -If you want to create an ``entry`` you give it the type you want it to have in its -constructor, and then use one of the non-const accessors to get a reference which you then -can assign the value you want it to have. - -The typical code to get info from a torrent file will then look like this:: - - entry torrent_file; - // ... - - const entry::dictionary_type& dict = torrent_file.dict(); - entry::dictionary_type::const_iterator i; - i = dict.find("announce"); - if (i != dict.end()) - { - std::string tracker_url= i->second.string(); - std::cout << tracker_url << "\n"; - } - -To make it easier to extract information from a torren file, the class ``torrent_info`` -exists. - -torrent_info ------------- - -The ``torrent_info`` has the following synopsis:: - - class torrent_info - { - public: - - torrent_info(const entry& torrent_file) - - typedef std::vector>file>::const_iterator file_iterator; - typedef std::vector::const_reverse_iterator reverse_file_iterator; - - file_iterator begin_files() const; - file_iterator end_files() const; - reverse_file_iterator rbegin_files() const; - reverse_file_iterator rend_files() const; - - std::size_t num_files() const; - const file& file_at(int index) const; - - const std::vector& trackers() const; - - int prioritize_tracker(int index); - - entry::integer_type total_size() const; - entry::integer_type piece_length() const; - std::size_t num_pieces() const; - const sha1_hash& info_hash() const; - const std::stirng& name() const; - const std::string& comment() const; - boost::posiz_time::ptime creation_date() const; - - - void print(std::ostream& os) const; - - entry::integer_type piece_size(unsigned int index) const; - const sha1_hash& hash_for_piece(unsigned int index) const; - }; - -This class will need some explanation. First of all, to get a list of all files -in the torrent, you can use ``begin_files()``, ``end_files()``, -``rbegin_files()`` and ``rend_files()``. These will give you standard vector -iterators with the type ``file``. - -:: - - struct file - { - std::string path; - std::string filename; - entry::integer_type size; - }; - -If you need index-access to files you can use the ``num_files()`` and ``file_at()`` -to access files using indices. - -The ``print()`` function is there for debug purposes only. It will print the info from -the torrent file to the given outstream. - -``name()`` returns the name of the torrent. - -The ``trackers()`` function will return a sorted vector of ``announce_entry``. -Each announce entry contains a string, which is the tracker url, and a tier index. The -tier index is the high-level priority. No matter which trackers that works or not, the -ones with lower tier will always be tried before the one with higher tier number. - -:: - - struct announce_entry - { - std::string url; - int tier; - }; - -The ``prioritize_tracker()`` is used internally to move a tracker to the front -of its tier group. i.e. It will never be moved pass a tracker with a different tier -number. For more information about how multiple trackers are dealt with, see the -specification_. - -.. _specification: http://home.elp.rr.com/tur/multitracker-spec.txt - - -``total_size()``, ``piece_length()`` and ``num_pieces()`` returns the total -number of bytes the torrent-file represents (all the files in it), the number of byte for -each piece and the total number of pieces, respectively. The difference between -``piece_size()`` and ``piece_length()`` is that ``piece_size()`` takes -the piece index as argument and gives you the exact size of that piece. It will always -be the same as ``piece_length()`` except in the case of the last piece, which may -be smaller. - -``hash_for_piece()`` takes a piece-index and returns the 20-bytes sha1-hash for that -piece and ``info_hash()`` returns the 20-bytes sha1-hash for the info-section of the -torrent file. For more information on the ``sha1_hash``, see the big_number_ class. - -``comment()`` returns the comment associated with the torrent. If there's no comment, -it will return an empty string. ``creation_date()`` returns a `boost::posix_time::ptime`__ -object, representing the time when this torrent file was created. If there's no timestamp -in the torrent file, this will return a date of january 1:st 1970. - -__ http://www.boost.org/libs/date_time/doc/class_ptime.html - - - - -torrent_handle --------------- - -You will usually have to store your torrent handles somewhere, since it's the -object through which you retrieve infromation about the torrent and aborts the torrent. -Its declaration looks like this:: - - struct torrent_handle - { - torrent_handle(); - - torrent_status status(); - void get_download_queue(std::vector& queue); - void get_peer_info(std::vector& v); - const torrent_info& get_torrent_info(); - bool is_valid(); - - void write_resume_data(std::vector& data); - - boost::filsystem::path save_path() const; - - void set_max_uploads(int max_uploads); - - sha1_hash info_hash() const; - - bool operator==(const torrent_handle&) const; - bool operator!=(const torrent_handle&) const; - bool operator<(const torrent_handle&) const; - }; - -The default constructor will initialize the handle to an invalid state. Which means you cannot -perform any operation on it, unless you first assign it a valid handle. If you try to perform -any operation on an uninitialized handle, it will throw ``invalid_handle``. - -``save_path()`` returns the path that was given to ``add_torrent()`` when this torrent -was started. - -``info_hash()`` returns the info hash for the torrent. - -``set_max_uploads()`` sets the maximum number of peers that's unchoked at the same time on this -torrent. If you set this to -1, there will be no limit. - -``write_resume_data()`` takes a non-const reference to a char-vector, that vector will be filled -with the fast-resume data. For more information about how fast-resume works, see `fast resume`_. - -status() -~~~~~~~~ - -``status()`` will return a structure with information about the status of this -torrent. If the torrent_handle_ is invalid, it will throw invalid_handle_ exception. -It contains the following fields:: - - struct torrent_status - { - enum state_t - { - invalid_handle, - queued_for_checking, - checking_files, - connecting_to_tracker, - downloading, - seeding - }; - - state_t state; - float progress; - boost::posix_time::time_duration next_announce; - - std::size_t total_download; - std::size_t total_upload; - - std::size_t total_payload_download; - std::size_t total_payload_upload; - - float download_rate; - float upload_rate; - - std::vector pieces; - std::size_t total_done; - }; - -``progress`` is a value in the range [0, 1], that represents the progress of the -torrent's current task. It may be checking files or downloading. The torrent's -current task is in the ``state`` member, it will be one of the following: - -+--------------------------+----------------------------------------------------------+ -|``queued_for_checking`` |The torrent is in the queue for being checked. But there | -| |currently is another torrent that are being checked. | -| |This torrent will wait for its turn. | -| | | -+--------------------------+----------------------------------------------------------+ -|``checking_files`` |The torrent has not started its download yet, and is | -| |currently checking existing files. | -| | | -+--------------------------+----------------------------------------------------------+ -|``connecting_to_tracker`` |The torrent has sent a request to the tracker and is | -| |currently waiting for a response | -| | | -+--------------------------+----------------------------------------------------------+ -|``downloading`` |The torrent is being downloaded. This is the state | -| |most torrents will be in most of the time. The progress | -| |meter will tell how much of the files that has been | -| |downloaded. | -| | | -+--------------------------+----------------------------------------------------------+ -|``seeding`` |In this state the torrent has finished downloading and | -| |is a pure seeder. | -| | | -+--------------------------+----------------------------------------------------------+ - -``next_announce`` is the time until the torrent will announce itself to the tracker. - -``total_download`` and ``total_upload`` is the number of bytes downloaded and -uploaded to all peers, accumulated, *this session* only. - -``total_payload_download`` and ``total_payload_upload`` counts the amount of bytes -send and received this session, but only the actual oayload data (i.e the interesting -data), these counters ignore any protocol overhead. - -``pieces`` is the bitmask that representw which pieces we have (set to true) and -the pieces we don't have. - -``download_rate`` and ``upload_rate`` are the total rates for all peers for this -torrent. These will usually have better precision than summing the rates from -all peers. The rates are given as the number of bytes per second. - -``total_done`` is the total number of bytes of the file(s) that we have. - -get_download_queue() -~~~~~~~~~~~~~~~~~~~~ - -``get_download_queue()`` takes a non-const reference to a vector which it will fill -information about pieces that are partially downloaded or not downloaded at all but partially -requested. The entry in the vector (``partial_piece_info``) looks like this:: - - struct partial_piece_info - { - enum { max_blocks_per_piece }; - int piece_index; - int blocks_in_piece; - std::bitset requested_blocks; - std::bitset finished_blocks; - peer_id peer[max_blocks_per_piece]; - int num_downloads[max_blocks_per_piece]; - }; - -``piece_index`` is the index of the piece in question. ``blocks_in_piece`` is the -number of blocks in this particular piece. This number will be the same for most pieces, but -the last piece may have fewer blocks than the standard pieces. - -``requested_blocks`` is a bitset with one bit per block in the piece. If a bit is set, it -means that that block has been requested, but not necessarily fully downloaded yet. To know -from whom the block has been requested, have a look in the ``peer`` array. The bit-index -in the ``requested_blocks`` and ``finished_blocks`` correspons to the array-index into -``peers`` and ``num_downloads``. The array of peers is contains the id of the -peer the piece was requested from. If a piece hasn't been requested (the bit in -``requested_blocks`` is not set) the peer array entry will be undefined. - -The ``finished_blocks`` is a bitset where each bit says if the block is fully downloaded -or not. And the ``num_downloads`` array says how many times that block has been downloaded. -When a piece fails a hash verification, single blocks may be redownloaded to see if the hash teast -may pass then. - - -get_peer_info() -~~~~~~~~~~~~~~~ - -``get_peer_info()`` takes a reference to a vector that will be cleared and filled -with one entry for each peer connected to this torrent, given the handle is valid. If the -torrent_handle_ is invalid, it will throw ``invalid_handle`` exception. Each entry in -the vector contains information about that particular peer. It contains the following -fields:: - - struct peer_info - { - enum - { - interesting = 0x1, - choked = 0x2, - remote_interested = 0x4, - remote_choked = 0x8, - supports_extensions = 0x10 - }; - unsigned int flags; - address ip; - float up_speed; - float down_speed; - unsigned int total_download; - unsigned int total_upload; - peer_id id; - std::vector pieces; - int upload_limit; - int upload_ceiling; - - int load_balancing; - - int downloading_piece_index; - int downloading_block_index; - int downloading_progress; - int downloading_total; - }; - -The ``flags`` attribute tells you in which state the peer is. It is set to -any combination of the four enums above. Where ``interesting`` means that we -are interested in pieces from this peer. ``choked`` means that **we** have -choked this peer. ``remote_interested`` and ``remote_choked`` means the -same thing but that the peer is interested in pieces from us and the peer has choked -**us**. ``support_extensions`` means that this peer supports the `extension protocol -as described by nolar`__. - -__ http://nolar.com/azureus/extended.htm - -The ``ip`` field is the IP-address to this peer. Its type is a wrapper around the -actual address and the port number. See address_ class. - -``up_speed`` and ``down_speed`` is the current upload and download speed -we have to and from this peer. These figures are updated aproximately once every second. - -``total_download`` and ``total_upload`` are the total number of bytes downloaded -from and uploaded to this peer. These numbers do not include the protocol chatter, but only -the payload data. - -``id`` is the peer's id as used in the bit torrent protocol. This id can be used to -extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer -is using. - -``pieces`` is a vector of booleans that has as many entries as there are pieces -in the torrent. Each boolean tells you if the peer has that piece (if it's set to true) -or if the peer miss that piece (set to false). - -``upload_limit`` is the number of bytes per second we are allowed to send to this -peer every second. It may be -1 if there's no limit. The upload limits of all peers -should sum up to the upload limit set by ``session::set_upload_limit``. - -``upload_ceiling`` is the current maximum allowed upload rate given the cownload -rate and share ratio. If the global upload rate is inlimited, the ``upload_limit`` -for every peer will be the same as their ``upload_ceiling``. - -``load_balancing`` is a measurment of the balancing of free download (that we get) -and free upload that we give. Every peer gets a certain amount of free upload, but -this member says how much *extra* free upload this peer has got. If it is a negative -number it means that this was a peer from which we have got this amount of free -download. - -You can know which piece, and which part of that piece, that is currently being -downloaded from a specific peer by looking at the next four members. -``downloading_piece_index`` is the index of the piece that is currently being downloaded. -This may be set to -1 if there's currently no piece downloading from this peer. If it is ->= 0, the other three members are valid. ``downloading_block_index`` is the index of the -block (or sub-piece) that is being downloaded. ``downloading_progress`` is the number -of bytes of this block we have received from the peer, and ``downloading_total`` is -the total number of bytes in this block. - - -get_torrent_info() -~~~~~~~~~~~~~~~~~~ - -Returns a const reference to the ``torrent_info`` object associated with this torrent. -This reference is valid as long as the torrent_handle_ is valid, no longer. If the -torrent_handle_ is invalid, invalid_handle_ exception will be thrown. - - -is_valid() -~~~~~~~~~~ - -Returns true if this handle refers to a valid torrent and false if it hasn't been initialized -or if the torrent it refers to has been aborted. - - -address -------- - -The ``address`` class represents a name of a network endpoint (usually referred to as -IP-address) and a port number. This is the same thing as a ``sockaddr_in`` would contain. -Its declaration looks like this:: - - class address - { - public: - address(); - address(unsigned char a - , unsigned char b - , unsigned char c - , unsigned char d - , unsigned short port); - address(unsigned int addr, unsigned short port); - address(const std::string& addr, unsigned short port); - address(const address& a); - ~address(); - - std::string as_string() const; - unsigned int ip() const; - unsigned short port() const; - - bool operator<(const address& a) const; - bool operator!=(const address& a) const; - bool operator==(const address& a) const; - }; - -It is less-than comparable to make it possible to use it as a key in a map. ``as_string()`` may block -while it does the DNS lookup, it returns a string that points to the address represented by the object. - -``ip()`` will return the 32-bit ip-address as an integer. ``port()`` returns the port number. - - - - -http_settings -------------- - -You have some control over tracker requests through the ``http_settings`` object. You -create it and fill it with your settings and the use ``session::set_http_settings()`` -to apply them. You have control over proxy and authorization settings and also the user-agent -that will be sent to the tracker. The user-agent is a good way to identify your client. - -:: - - struct http_settings - { - http_settings(); - std::string proxy_ip; - int proxy_port; - std::string proxy_login; - std::string proxy_password; - std::string user_agent; - int tracker_timeout; - int tracker_maximum_response_length; - }; - -``proxy_ip`` may be a hostname or ip to a http proxy to use. If this is -an empty string, no http proxy will be used. - -``proxy_port`` is the port on which the http proxy listens. If ``proxy_ip`` -is empty, this will be ignored. - -``proxy_login`` should be the login username for the http proxy, if this -empty, the http proxy will be trid to be used without authentication. - -``proxy_password`` the password string for the http proxy. - -``user_agent`` this is the client identification to the tracker. It will -be followed by the string "(libtorrent)" to identify that this library -is being used. This should be set to your client's name and version number. - -``tracker_timeout`` is the number of seconds the tracker connection will -wait until it considers the tracker to have timed-out. Default value is 10 -seconds. - -``tracker_maximum_response_length`` is the maximum number of bytes in a -tracker response. If a response size passes this number it will be rejected -and the connection will be closed. On gzipped responses this size is measured -on the uncompressed data. So, if you get 20 bytes of gzip response that'll -expand to 2 megs, it will be interrupted before the entire response has been -uncompressed (given your limit is lower than 2 megs). Default limit is -1 megabyte. - - -big_number ----------- - -Both the ``peer_id`` and ``sha1_hash`` types are typedefs of the class -``big_number``. It represents 20 bytes of data. Its synopsis follows:: - - class big_number - { - public: - bool operator==(const big_number& n) const; - bool operator!=(const big_number& n) const; - bool operator<(const big_number& n) const; - - const unsigned char* begin() const; - const unsigned char* end() const; - - unsigned char* begin(); - unsigned char* end(); - }; - -The iterators gives you access to individual bytes. - - - -hasher ------- - -This class creates sha1-hashes. Its declaration looks like this:: - - class hasher - { - public: - hasher(); - - void update(const char* data, unsigned int len); - sha1_hash final(); - void reset(); - }; - - -You use it by first instantiating it, then call ``update()`` to feed it -with data. i.e. you don't have to keep the entire buffer of which you want to -create the hash in memory. You can feed the hasher parts of it at a time. When -You have fed the hasher with all the data, you call ``final()`` and it -will return the sha1-hash of the data. - -If you want to reuse the hasher object once you have created a hash, you have to -call ``reset()`` to reinitialize it. - -The sha1-algorithm used was implemented by Steve Reid and released as public domain. -For more info, see ``src/sha1.c``. - - -fingerprint ------------ - -The fingerprint class represents information about a client and its version. It is used -to encode this information into the client's peer id. - -This is the class declaration:: - - struct fingerprint - { - fingerprint(const char* id_string, int major, int minor, int revision, int tag); - - std::string to_string() const; - - char id[2]; - char major_version; - char minor_version; - char revision_version; - char tag_version; - - }; - -The constructor takes a ``const char*`` that should point to a string constant containing -exactly two characters. These are the characters that should be unique for your client. Make -sure not to clash with anybody else. Here are some taken id's: - -+----------+-----------------------+ -| id chars | client | -+==========+=======================+ -| 'AZ' | Azureus | -+----------+-----------------------+ -| 'LT' | libtorrent (default) | -+----------+-----------------------+ -| 'BX' | BittorrentX | -+----------+-----------------------+ -| 'MT' | Moonlight Torrent | -+----------+-----------------------+ - - -The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the -version of your client. All these numbers must be within the range [0, 9]. - -``to_string()`` will generate the actual string put in the peer-id, and return it. - -alert ------ - -The ``alert`` class is used to pass messages of events from the libtorrent code -to the user. It is a base class that specific messages are derived from. This -is its synopsis:: - - class alert - { - public: - - enum severity_t { debug, info, warning, critital, fatal, none }; - - alert(severity_t severity, const std::string& msg); - - virtual ~alert() {} - - const std::string& msg() const; - severity_t severity() const; - - virtual std::auto_ptr clone() const = 0; - }; - - - - -exceptions ----------- - -There are a number of exceptions that can be thrown from different places in libtorrent, -here's a complete list with description. - - -invalid_handle -~~~~~~~~~~~~~~ - -This exception is thrown when querying information from a torrent_handle_ that hasn't -been initialized or that has become invalid. - -:: - - struct invalid_handle: std::exception - { - const char* what() const throw(); - }; - - -duplicate_torrent -~~~~~~~~~~~~~~~~~ - -This is thrown by ``session::add_torrent()`` if the torrent already has been added to -the session. - -:: - - struct duplicate_torrent: std::exception - { - const char* what() const throw(); - }; - - -invalid_encoding -~~~~~~~~~~~~~~~~ - -This is thrown by ``bdecode()`` if the input data is not a valid bencoding. - -:: - - struct invalid_encoding: std::exception - { - const char* what() const throw(); - }; - - -type_error -~~~~~~~~~~ - -This is thrown from the accessors of ``entry`` if the data type of the ``entry`` doesn't -match the type you want to extract from it. - -:: - - struct type_error: std::runtime_error - { - type_error(const char* error); - }; - - -invalid_torrent_file -~~~~~~~~~~~~~~~~~~~~ - -This exception is thrown from the constructor of ``torrent_info`` if the given bencoded information -doesn't meet the requirements on what information has to be present in a torrent file. - -:: - - struct invalid_torrent_file: std::exception - { - const char* what() const throw(); - }; - - -example usage -------------- - -dump_torrent -~~~~~~~~~~~~ - -This is an example of a program that will take a torrent-file as a parameter and -print information about it to std out:: - - #include - #include - #include - #include - #include - - #include "libtorrent/entry.hpp" - #include "libtorrent/bencode.hpp" - #include "libtorrent/torrent_info.hpp" - - - int main(int argc, char* argv[]) - { - using namespace libtorrent; - - if (argc != 2) - { - std::cerr << "usage: dump_torrent torrent-file\n"; - return 1; - } - - try - { - 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); - - // print info about torrent - std::cout << "\n\n----- torrent file info -----\n\n"; - std::cout << "trackers:\n"; - for (std::vector::const_iterator i = t.trackers().begin(); - i != t.trackers().end(); - ++i) - { - std::cout << i->tier << ": " << i->url << "\n"; - } - - std::cout << "number of pieces: " << t.num_pieces() << "\n"; - std::cout << "piece length: " << t.piece_length() << "\n"; - std::cout << "files:\n"; - for (torrent_info::file_iterator i = t.begin_files(); - i != t.end_files(); - ++i) - { - std::cout << " " << std::setw(11) << i->size - << " " << i->path << " " << i->filename << "\n"; - } - - } - catch (std::exception& e) - { - std::cout << e.what() << "\n"; - } - - return 0; - } - - -simple client -~~~~~~~~~~~~~ - -This is a simple client. It doesn't have much output to keep it simple:: - - #include - #include - #include - #include - - #include - #include - - #include "libtorrent/entry.hpp" - #include "libtorrent/bencode.hpp" - #include "libtorrent/session.hpp" - #include "libtorrent/http_settings.hpp" - - int main(int argc, char* argv[]) - { - using namespace libtorrent; - - if (argc != 2) - { - std::cerr << "usage: ./simple_cient torrent-file\n" - "to stop the client, press return.\n"; - return 1; - } - - try - { - session s(6881); - - 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, ""); - - // wait for the user to end - char a; - std::cin.unsetf(std::ios_base::skipws); - std::cin >> a; - } - catch (std::exception& e) - { - std::cout << e.what() << "\n"; - } - return 0; - } - - -fast resume ------------ - -The fast resume mechanism is a way to remember which pieces are downloaded and where they -are put between sessions. You can generate fast resume data by calling -``torrent_handle::write_resume_data()`` on torrent_handle_. You can then save this data -to disk and use it when resuming the torrent. libtorrent will not check the piece hashes -then, and rely on the information given in the fast-resume data. The fast-resume data -also contains information about which bocks in the unfinished pieces were downloaded, so -it will not have to start from scratch on the partially downloaded pieces. - -To use the fast-resume data you simply give it to ``session::add_torrent()``, and it -will skip the time consuming checks. It may have to do the checking anyway, if the -fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will -not trust the fast-resume data and just do the checking. - -file format -~~~~~~~~~~~ - -The format of the fast-resume data is as follows, given that all -4-byte integers are stored as big-endian:: - - 20 bytes, the info_hash for the torrent - 4 bytes, the number of allocated slots in the storage - for each slot - 4 bytes, piece index in this slot, - -1 means there's no storage for the slot - -2 means there's no piece at this slot, it's free - 4 bytes, the number of blocks per piece. - this must be piece_size / 16k or 1 if piece_size is < 16k - and can be 128 at max. - 4 bytes, the number of unfinished pieces - for each unfinished piece - 4 bytes, index of the unfinished piece - blocks_per_piece / 8 bytes, the bitmask describing which - blocks are finished in this piece. Feedback ======== diff --git a/docs/libtorrent_screen.png b/docs/libtorrent_screen.png new file mode 100755 index 0000000000000000000000000000000000000000..19d98526196d7256a7d496e772a0152bfd3bf1ca GIT binary patch literal 53067 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 literal 0 HcmV?d00001 diff --git a/docs/manual.html b/docs/manual.html new file mode 100755 index 000000000..683998724 --- /dev/null +++ b/docs/manual.html @@ -0,0 +1,1264 @@ + + + + + + +libtorrent manual + + + +
    +

    libtorrent manual

    + +
    +

    introduction

    +

    libtorrent is a C++ library that aims to be a good alternative to all the +other bittorrent implementations around. It is a +library and not a full featured client, although it comes with a working +example client.

    +

    The main goals of libtorrent are:

    +
    +
      +
    • to be cpu efficient
    • +
    • to be memory efficient
    • +
    • to be very easy to use
    • +
    +
    +

    libtorrent is not finished. It is an ongoing project (including this documentation). +The current state includes the following features:

    +
    +
      +
    • multitracker extension support (as described by TheShadow)
    • +
    • serves multiple torrents on a single port and a single thread
    • +
    • supports http proxies and proxy authentication
    • +
    • gzipped tracker-responses
    • +
    • piece picking on block-level (as opposed to piece-level) like in Azureus
    • +
    • queues torrents for file check, instead of checking all of them in parallel.
    • +
    • uses separate threads for checking files and for main downloader, with a fool-proof +thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).
    • +
    • can limit the upload bandwidth usage and the maximum number of unchoked peers
    • +
    • piece-wise file allocation
    • +
    • tries to maintain a 1:1 share ratio between all peers but also shifts free +download to peers as free upload. To maintain a global 1:1 ratio.
    • +
    • fast resume support, a way to get rid of the costly piece check at the start +of a resumed torrent. Saves the storage state in a separate fast-resume file.
    • +
    • The extension protocol described by Nolar. See extensions.
    • +
    +
    +

    Functions that are yet to be implemented:

    +
    +
      +
    • number of connections limit
    • +
    • better handling of peers that send bad data
    • +
    • ip-filters
    • +
    • file-level piece priority
    • +
    +
    +

    libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread, +boost.filesystem, boost.date_time and various other boost libraries as well as zlib.

    +

    libtorrent has been successfully compiled and tested on:

    +
    +
      +
    • Cygwin GCC 3.3.1
    • +
    • Windows 2000 vc7.1
    • +
    • Linux x86 (debian) GCC 3.0
    • +
    +
    +

    It does not compile on

    +
    +
      +
    • GCC 2.95
    • +
    +
    +
    +
    +

    building

    +

    To build libtorrent you need boost and bjam installed. +Then you can use bjam to build libtorrent.

    +

    To make bjam work, you need to set the environment variable BOOST_ROOT to the +path where boost is installed (e.g. c:boost_1_30_2 on windows). Then you can just run +bjam in the libtorrent directory.

    +

    The Jamfile doesn't work yet. On unix-systems you can use the makefile however. You +first have to build boost.thread and boost.filesystem. You do this by, in the directory +'boost-1.30.2/tools/build/jam_src' run the build script ./build.sh. This should +produce at least one folder with the 'bin' prefix (and the rest of the name describes +your platform). Put the files in that folder somewhere in your path.

    +

    You can then invoke bjam in the directories 'boost-1.30.2/libs/thread/build', +'boost-1.30.2/libs/date_time/build' and 'boost-1.30.2/libs/filesystem/build'. That will +produce the needed libraries. Put these libraries in the libtorrent root directory. +You then have to modify the makefile to use you prefered compiler and to have the +correct path to your boost istallation.

    +

    Then the makefile should be able to do the rest.

    +

    When building (with boost 1.30.2) on linux and solaris however, I found that I had to make the following +modifications to the boost.date-time library. In the file: +'boost-1.30.2/boost/date_time/gregorian_calendar.hpp' line 59. Prepend 'boost/date_time/' +to the include path.

    +

    And the second modification was in the file: +'boost-1.30.2/boost/date_time/microsec_time_clock.hpp' add the following include at the top +of the file:

    +
    +#include "boost/cstdint.hpp"
    +
    +

    In developer studio, you may have to set the compiler options "force conformance in for +loop scope" and "treat wchar_t as built-in type" to Yes.

    +

    TODO: more detailed build instructions.

    +
    +
    +

    using

    +

    The interface of libtorrent consists of a few classes. The main class is +the session, it contains the main loop that serves all torrents.

    +

    The basic usage is as follows:

    +
      +
    • conststruct a session

      +
    • +
    • parse .torrent-files and add them to the session

      +
    • +
    • +
      main loop
      +
        +
      • query the torrent_handles for progress
      • +
      • query the session for information
      • +
      • add and remove torrents from the session at run-time
      • +
      +
      +
      +
    • +
    • destruct all torrent_handles

      +
    • +
    • destruct session object

      +
    • +
    +

    Each class and function is described in this manual.

    +
    +
    +

    session

    +

    The session class has the following synopsis:

    +
    +class session: public boost::noncopyable
    +{
    +        session(int listen_port, const fingerprint& print);
    +        session(int listen_port);
    +
    +        torrent_handle add_torrent(
    +                const torrent_info& t
    +                , const std::string& save_path
    +                , const entry& resume_data = entry());
    +
    +        void remove_torrent(const torrent_handle& h);
    +
    +        void set_http_settings(const http_settings& settings);
    +        void set_upload_rate_limit(int bytes_per_second);
    +
    +        std::auto_ptr<alert> pop_alert();
    +        void set_severity_level(alert::severity_t s);
    +
    +};
    +
    +

    Once it's created, it will spawn the main thread that will do all the work. +The main thread will be idle as long it doesn't have any torrents to participate in. +You add torrents through the add_torrent()-function where you give an +object representing the information found in the torrent file and the path where you +want to save the files. The save_path will be prepended to the directory- +structure in the torrent-file. add_torrent will throw duplicate_torrent exception +if the torrent already exists in the session.

    +

    The optional last parameter, resume_data can be given if up to date fast-resume data +is available. The fast-resume data can be acquired from a running torrent by calling +torrent_handle::write_resume_data(). See fast resume.

    +

    remove_torrent() will close all peer connections associated with the torrent and tell +the tracker that we've stopped participating in the swarm.

    +

    If the torrent you are trying to add already exists in the session (is either queued +for checking, being checked or downloading) add_torrent() will throw +duplicate_torrent which derives from std::exception.

    +

    The difference between the two constructors is that one of them takes a fingerprint +as argument. If this is ommited, the client will get a default fingerprint stating +the version of libtorrent. The fingerprint is a short string that will be used in +the peer-id to identify the client and the client's version. For more details see the +fingerprint class.

    +

    set_upload_rate_limit() set the maximum number of bytes allowed to be +sent to peers per second. This bandwidth is distributed among all the peers. If +you don't want to limit upload rate, you can set this to -1 (the default).

    +

    The destructor of session will notify all trackers that our torrents has 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 +timeout can be set with set_http_settings().

    +

    How to parse a torrent file and create a torrent_info object is described below.

    +

    The torrent_handle returned by add_torrent can be used to retrieve information +about the torrent's progress, its peers etc. It is also used to abort a torrent.

    +

    The constructor takes a listen port as argument, if the given port is busy it will +increase the port number by one and try again. If it still fails it will continue +increasing the port number until it succeeds or has failed 9 ports. This will +change in the future to give more control of the listen-port.

    +

    For information about the pop_alert() function, see alerts.

    +
    +
    +

    parsing torrent files

    +

    The torrent files are bencoded. There are two functions in libtorrent that can encode and decode +bencoded data. They are:

    +
    +template<class InIt> entry bdecode(InIt start, InIt end);
    +template<class OutIt> void bencode(OutIt out, const entry& e);
    +
    +

    The entry class is the internal representation of the bencoded data +and it can be used to retreive information, an entry can also be build by +the program and given to bencode() to encode it into the OutIt +iterator.

    +

    The OutIt and InIt are iterators +(InputIterator_ and OutputIterator_ respectively). They +are templates and are usually instantiated as ostream_iterator_, +back_insert_iterator_ or istream_iterator_. These +functions will assume that the iterator refers to a character +(char). So, if you want to encode entry e into a buffer +in memory, you can do it like this:

    +
    +std::vector<char> buffer;
    +bencode(std::back_insert_iterator<std::vector<char> >(buf), e);
    +
    +

    If you want to decode a torrent file from a buffer in memory, you can do it like this:

    +
    +std::vector<char> buffer;
    +// ...
    +entry e = bdecode(buf.begin(), buf.end());
    +
    +

    Or, if you have a raw char buffer:

    +
    +const char* buf;
    +// ...
    +entry e = bdecode(buf, buf + data_size);
    +
    +

    Now we just need to know how to retrieve information from the entry.

    +

    If bdecode() encounters invalid encoded data in the range given to it +it will throw invalid_encoding.

    +
    +
    +

    entry

    +

    The entry class represents one node in a bencoded hierarchy. It works as a +variant type, it can be either a list, a dictionary (std::map), an integer +or a string. This is its synopsis:

    +
    +class entry
    +{
    +public:
    +
    +        typedef std::map<std::string, entry> dictionary_type;
    +        typedef std::string string_type;
    +        typedef std::vector<entry> list_type;
    +        typedef implementation-defined integer_type;
    +
    +        enum data_type
    +        {
    +                int_t,
    +                string_t,
    +                list_t,
    +                dictionary_t,
    +                undefined_t
    +        };
    +
    +        data_type type() const;
    +
    +        entry(const dictionary_type&);
    +        entry(const string_type&);
    +        entry(const list_type&);
    +        entry(const integer_type&);
    +
    +        entry();
    +        entry(data_type t);
    +        entry(const entry& e);
    +
    +        ~entry();
    +
    +        void operator=(const entry& e);
    +        void operator=(const dictionary_type&);
    +        void operator=(const string_type&);
    +        void operator=(const list_type&);
    +        void operator=(const integer_type&);
    +
    +        integer_type& integer()
    +        const integer_type& integer() const;
    +        string_type& string();
    +        const string_type& string() const;
    +        list_type& list();
    +        const list_type& list() const;
    +        dictionary_type& dict();
    +        const dictionary_type& dict() const;
    +
    +        void print(std::ostream& os, int indent = 0) const;
    +};
    +
    +

    The integer(), string(), list() and dict() functions +are accessorts that return the respecive type. If the entry object isn't of the +type you request, the accessor will throw type_error (which derives from +std::runtime_error). You can ask an entry for its type through the +type() function.

    +

    The print() function is there for debug purposes only.

    +

    If you want to create an entry you give it the type you want it to have in its +constructor, and then use one of the non-const accessors to get a reference which you then +can assign the value you want it to have.

    +

    The typical code to get info from a torrent file will then look like this:

    +
    +entry torrent_file;
    +// ...
    +
    +const entry::dictionary_type& dict = torrent_file.dict();
    +entry::dictionary_type::const_iterator i;
    +i = dict.find("announce");
    +if (i != dict.end())
    +{
    +        std::string tracker_url= i->second.string();
    +        std::cout << tracker_url << "\n";
    +}
    +
    +

    To make it easier to extract information from a torren file, the class torrent_info +exists.

    +
    +
    +

    torrent_info

    +

    The torrent_info has the following synopsis:

    +
    +class torrent_info
    +{
    +public:
    +
    +        torrent_info(const entry& torrent_file)
    +
    +        typedef std::vector>file>::const_iterator file_iterator;
    +        typedef std::vector<file>::const_reverse_iterator reverse_file_iterator;
    +
    +        file_iterator begin_files() const;
    +        file_iterator end_files() const;
    +        reverse_file_iterator rbegin_files() const;
    +        reverse_file_iterator rend_files() const;
    +
    +        std::size_t num_files() const;
    +        const file& file_at(int index) const;
    +
    +        const std::vector<announce_entry>& trackers() const;
    +
    +        int prioritize_tracker(int index);
    +
    +        entry::integer_type total_size() const;
    +        entry::integer_type piece_length() const;
    +        std::size_t num_pieces() const;
    +        const sha1_hash& info_hash() const;
    +        const std::stirng& name() const;
    +        const std::string& comment() const;
    +        boost::posiz_time::ptime creation_date() const;
    +
    +
    +        void print(std::ostream& os) const;
    +
    +        entry::integer_type piece_size(unsigned int index) const;
    +        const sha1_hash& hash_for_piece(unsigned int index) const;
    +};
    +
    +

    This class will need some explanation. First of all, to get a list of all files +in the torrent, you can use begin_files(), end_files(), +rbegin_files() and rend_files(). These will give you standard vector +iterators with the type file.

    +
    +struct file
    +{
    +        std::string path;
    +        std::string filename;
    +        entry::integer_type size;
    +};
    +
    +

    If you need index-access to files you can use the num_files() and file_at() +to access files using indices.

    +

    The print() function is there for debug purposes only. It will print the info from +the torrent file to the given outstream.

    +

    name() returns the name of the torrent.

    +

    The trackers() function will return a sorted vector of announce_entry. +Each announce entry contains a string, which is the tracker url, and a tier index. The +tier index is the high-level priority. No matter which trackers that works or not, the +ones with lower tier will always be tried before the one with higher tier number.

    +
    +struct announce_entry
    +{
    +        std::string url;
    +        int tier;
    +};
    +
    +

    The prioritize_tracker() is used internally to move a tracker to the front +of its tier group. i.e. It will never be moved pass a tracker with a different tier +number. For more information about how multiple trackers are dealt with, see the +specification.

    +

    total_size(), piece_length() and num_pieces() returns the total +number of bytes the torrent-file represents (all the files in it), the number of byte for +each piece and the total number of pieces, respectively. The difference between +piece_size() and piece_length() is that piece_size() takes +the piece index as argument and gives you the exact size of that piece. It will always +be the same as piece_length() except in the case of the last piece, which may +be smaller.

    +

    hash_for_piece() takes a piece-index and returns the 20-bytes sha1-hash for that +piece and info_hash() returns the 20-bytes sha1-hash for the info-section of the +torrent file. For more information on the sha1_hash, see the big_number class.

    +

    comment() returns the comment associated with the torrent. If there's no comment, +it will return an empty string. creation_date() returns a boost::posix_time::ptime +object, representing the time when this torrent file was created. If there's no timestamp +in the torrent file, this will return a date of january 1:st 1970.

    +
    +
    +

    torrent_handle

    +

    You will usually have to store your torrent handles somewhere, since it's the +object through which you retrieve infromation about the torrent and aborts the torrent. +Its declaration looks like this:

    +
    +struct torrent_handle
    +{
    +        torrent_handle();
    +
    +        torrent_status status();
    +        void get_download_queue(std::vector<partial_piece_info>& queue);
    +        void get_peer_info(std::vector<peer_info>& v);
    +        const torrent_info& get_torrent_info();
    +        bool is_valid();
    +
    +        entry write_resume_data();
    +
    +        boost::filsystem::path save_path() const;
    +
    +        void set_max_uploads(int max_uploads);
    +
    +        sha1_hash info_hash() const;
    +
    +        bool operator==(const torrent_handle&) const;
    +        bool operator!=(const torrent_handle&) const;
    +        bool operator<(const torrent_handle&) const;
    +};
    +
    +

    The default constructor will initialize the handle to an invalid state. Which means you cannot +perform any operation on it, unless you first assign it a valid handle. If you try to perform +any operation on an uninitialized handle, it will throw invalid_handle.

    +

    save_path() returns the path that was given to add_torrent() when this torrent +was started.

    +

    info_hash() returns the info hash for the torrent.

    +

    set_max_uploads() sets the maximum number of peers that's unchoked at the same time on this +torrent. If you set this to -1, there will be no limit.

    +

    write_resume_data() generates fast-resume data and returns it as an entry. This entry +is suitable for being bencoded. For more information about how fast-resume works, see fast resume. +It may throw invalid_handle if the torrent handle is invalid.

    +
    +

    status()

    +

    status() will return a structure with information about the status of this +torrent. If the torrent_handle is invalid, it will throw invalid_handle exception. +It contains the following fields:

    +
    +struct torrent_status
    +{
    +        enum state_t
    +        {
    +                invalid_handle,
    +                queued_for_checking,
    +                checking_files,
    +                connecting_to_tracker,
    +                downloading,
    +                seeding
    +        };
    +
    +        state_t state;
    +        float progress;
    +        boost::posix_time::time_duration next_announce;
    +
    +        std::size_t total_download;
    +        std::size_t total_upload;
    +
    +        std::size_t total_payload_download;
    +        std::size_t total_payload_upload;
    +
    +        float download_rate;
    +        float upload_rate;
    +
    +        std::vector<bool> pieces;
    +        std::size_t total_done;
    +};
    +
    +

    progress is a value in the range [0, 1], that represents the progress of the +torrent's current task. It may be checking files or downloading. The torrent's +current task is in the state member, it will be one of the following:

    + ++++ + + + + + + + + + + + + + + + + + +
    queued_for_checkingThe torrent is in the queue for being checked. But there +currently is another torrent that are being checked. +This torrent will wait for its turn.
    checking_filesThe torrent has not started its download yet, and is +currently checking existing files.
    connecting_to_trackerThe torrent has sent a request to the tracker and is +currently waiting for a response
    downloadingThe torrent is being downloaded. This is the state +most torrents will be in most of the time. The progress +meter will tell how much of the files that has been +downloaded.
    seedingIn this state the torrent has finished downloading and +is a pure seeder.
    +

    next_announce is the time until the torrent will announce itself to the tracker.

    +

    total_download and total_upload is the number of bytes downloaded and +uploaded to all peers, accumulated, this session only.

    +

    total_payload_download and total_payload_upload counts the amount of bytes +send and received this session, but only the actual oayload data (i.e the interesting +data), these counters ignore any protocol overhead.

    +

    pieces is the bitmask that representw which pieces we have (set to true) and +the pieces we don't have.

    +

    download_rate and upload_rate are the total rates for all peers for this +torrent. These will usually have better precision than summing the rates from +all peers. The rates are given as the number of bytes per second.

    +

    total_done is the total number of bytes of the file(s) that we have.

    +
    +
    +

    get_download_queue()

    +

    get_download_queue() takes a non-const reference to a vector which it will fill +information about pieces that are partially downloaded or not downloaded at all but partially +requested. The entry in the vector (partial_piece_info) looks like this:

    +
    +struct partial_piece_info
    +{
    +        enum { max_blocks_per_piece };
    +        int piece_index;
    +        int blocks_in_piece;
    +        std::bitset<max_blocks_per_piece> requested_blocks;
    +        std::bitset<max_blocks_per_piece> finished_blocks;
    +        peer_id peer[max_blocks_per_piece];
    +        int num_downloads[max_blocks_per_piece];
    +};
    +
    +

    piece_index is the index of the piece in question. blocks_in_piece is the +number of blocks in this particular piece. This number will be the same for most pieces, but +the last piece may have fewer blocks than the standard pieces.

    +

    requested_blocks is a bitset with one bit per block in the piece. If a bit is set, it +means that that block has been requested, but not necessarily fully downloaded yet. To know +from whom the block has been requested, have a look in the peer array. The bit-index +in the requested_blocks and finished_blocks correspons to the array-index into +peers and num_downloads. The array of peers is contains the id of the +peer the piece was requested from. If a piece hasn't been requested (the bit in +requested_blocks is not set) the peer array entry will be undefined.

    +

    The finished_blocks is a bitset where each bit says if the block is fully downloaded +or not. And the num_downloads array says how many times that block has been downloaded. +When a piece fails a hash verification, single blocks may be redownloaded to see if the hash teast +may pass then.

    +
    +
    +

    get_peer_info()

    +

    get_peer_info() takes a reference to a vector that will be cleared and filled +with one entry for each peer connected to this torrent, given the handle is valid. If the +torrent_handle is invalid, it will throw invalid_handle exception. Each entry in +the vector contains information about that particular peer. It contains the following +fields:

    +
    +struct peer_info
    +{
    +        enum
    +        {
    +                interesting = 0x1,
    +                choked = 0x2,
    +                remote_interested = 0x4,
    +                remote_choked = 0x8,
    +                supports_extensions = 0x10
    +        };
    +        unsigned int flags;
    +        address ip;
    +        float up_speed;
    +        float down_speed;
    +        unsigned int total_download;
    +        unsigned int total_upload;
    +        peer_id id;
    +        std::vector<bool> pieces;
    +        int upload_limit;
    +        int upload_ceiling;
    +
    +        int load_balancing;
    +
    +        int downloading_piece_index;
    +        int downloading_block_index;
    +        int downloading_progress;
    +        int downloading_total;
    +};
    +
    +

    The flags attribute tells you in which state the peer is. It is set to +any combination of the four enums above. Where interesting means that we +are interested in pieces from this peer. choked means that we have +choked this peer. remote_interested and remote_choked means the +same thing but that the peer is interested in pieces from us and the peer has choked +us. support_extensions means that this peer supports the extension protocol +as described by nolar.

    +

    The ip field is the IP-address to this peer. Its type is a wrapper around the +actual address and the port number. See address class.

    +

    up_speed and down_speed is the current upload and download speed +we have to and from this peer. These figures are updated aproximately once every second.

    +

    total_download and total_upload are the total number of bytes downloaded +from and uploaded to this peer. These numbers do not include the protocol chatter, but only +the payload data.

    +

    id is the peer's id as used in the bit torrent protocol. This id can be used to +extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer +is using.

    +

    pieces is a vector of booleans that has as many entries as there are pieces +in the torrent. Each boolean tells you if the peer has that piece (if it's set to true) +or if the peer miss that piece (set to false).

    +

    upload_limit is the number of bytes per second we are allowed to send to this +peer every second. It may be -1 if there's no limit. The upload limits of all peers +should sum up to the upload limit set by session::set_upload_limit.

    +

    upload_ceiling is the current maximum allowed upload rate given the cownload +rate and share ratio. If the global upload rate is inlimited, the upload_limit +for every peer will be the same as their upload_ceiling.

    +

    load_balancing is a measurment of the balancing of free download (that we get) +and free upload that we give. Every peer gets a certain amount of free upload, but +this member says how much extra free upload this peer has got. If it is a negative +number it means that this was a peer from which we have got this amount of free +download.

    +

    You can know which piece, and which part of that piece, that is currently being +downloaded from a specific peer by looking at the next four members. +downloading_piece_index is the index of the piece that is currently being downloaded. +This may be set to -1 if there's currently no piece downloading from this peer. If it is +>= 0, the other three members are valid. downloading_block_index is the index of the +block (or sub-piece) that is being downloaded. downloading_progress is the number +of bytes of this block we have received from the peer, and downloading_total is +the total number of bytes in this block.

    +
    +
    +

    get_torrent_info()

    +

    Returns a const reference to the torrent_info object associated with this torrent. +This reference is valid as long as the torrent_handle is valid, no longer. If the +torrent_handle is invalid, invalid_handle exception will be thrown.

    +
    +
    +

    is_valid()

    +

    Returns true if this handle refers to a valid torrent and false if it hasn't been initialized +or if the torrent it refers to has been aborted.

    +
    +
    +
    +

    address

    +

    The address class represents a name of a network endpoint (usually referred to as +IP-address) and a port number. This is the same thing as a sockaddr_in would contain. +Its declaration looks like this:

    +
    +class address
    +{
    +public:
    +        address();
    +        address(unsigned char a
    +                , unsigned char b
    +                , unsigned char c
    +                , unsigned char d
    +                , unsigned short  port);
    +        address(unsigned int addr, unsigned short port);
    +        address(const std::string& addr, unsigned short port);
    +        address(const address& a);
    +        ~address();
    +
    +        std::string as_string() const;
    +        unsigned int ip() const;
    +        unsigned short port() const;
    +
    +        bool operator<(const address& a) const;
    +        bool operator!=(const address& a) const;
    +        bool operator==(const address& a) const;
    +};
    +
    +

    It is less-than comparable to make it possible to use it as a key in a map. as_string() may block +while it does the DNS lookup, it returns a string that points to the address represented by the object.

    +

    ip() will return the 32-bit ip-address as an integer. port() returns the port number.

    +
    +
    +

    http_settings

    +

    You have some control over tracker requests through the http_settings object. You +create it and fill it with your settings and the use session::set_http_settings() +to apply them. You have control over proxy and authorization settings and also the user-agent +that will be sent to the tracker. The user-agent is a good way to identify your client.

    +
    +struct http_settings
    +{
    +        http_settings();
    +        std::string proxy_ip;
    +        int proxy_port;
    +        std::string proxy_login;
    +        std::string proxy_password;
    +        std::string user_agent;
    +        int tracker_timeout;
    +        int tracker_maximum_response_length;
    +};
    +
    +

    proxy_ip may be a hostname or ip to a http proxy to use. If this is +an empty string, no http proxy will be used.

    +

    proxy_port is the port on which the http proxy listens. If proxy_ip +is empty, this will be ignored.

    +

    proxy_login should be the login username for the http proxy, if this +empty, the http proxy will be trid to be used without authentication.

    +

    proxy_password the password string for the http proxy.

    +

    user_agent this is the client identification to the tracker. It will +be followed by the string "(libtorrent)" to identify that this library +is being used. This should be set to your client's name and version number.

    +

    tracker_timeout is the number of seconds the tracker connection will +wait until it considers the tracker to have timed-out. Default value is 10 +seconds.

    +

    tracker_maximum_response_length is the maximum number of bytes in a +tracker response. If a response size passes this number it will be rejected +and the connection will be closed. On gzipped responses this size is measured +on the uncompressed data. So, if you get 20 bytes of gzip response that'll +expand to 2 megs, it will be interrupted before the entire response has been +uncompressed (given your limit is lower than 2 megs). Default limit is +1 megabyte.

    +
    +
    +

    big_number

    +

    Both the peer_id and sha1_hash types are typedefs of the class +big_number. It represents 20 bytes of data. Its synopsis follows:

    +
    +class big_number
    +{
    +public:
    +        bool operator==(const big_number& n) const;
    +        bool operator!=(const big_number& n) const;
    +        bool operator<(const big_number& n) const;
    +
    +        const unsigned char* begin() const;
    +        const unsigned char* end() const;
    +
    +        unsigned char* begin();
    +        unsigned char* end();
    +};
    +
    +

    The iterators gives you access to individual bytes.

    +
    +
    +

    hasher

    +

    This class creates sha1-hashes. Its declaration looks like this:

    +
    +class hasher
    +{
    +public:
    +        hasher();
    +
    +        void update(const char* data, unsigned int len);
    +        sha1_hash final();
    +        void reset();
    +};
    +
    +

    You use it by first instantiating it, then call update() to feed it +with data. i.e. you don't have to keep the entire buffer of which you want to +create the hash in memory. You can feed the hasher parts of it at a time. When +You have fed the hasher with all the data, you call final() and it +will return the sha1-hash of the data.

    +

    If you want to reuse the hasher object once you have created a hash, you have to +call reset() to reinitialize it.

    +

    The sha1-algorithm used was implemented by Steve Reid and released as public domain. +For more info, see src/sha1.c.

    +
    +
    +

    fingerprint

    +

    The fingerprint class represents information about a client and its version. It is used +to encode this information into the client's peer id.

    +

    This is the class declaration:

    +
    +struct fingerprint
    +{
    +        fingerprint(const char* id_string, int major, int minor, int revision, int tag);
    +
    +        std::string to_string() const;
    +
    +        char id[2];
    +        char major_version;
    +        char minor_version;
    +        char revision_version;
    +        char tag_version;
    +
    +};
    +
    +

    The constructor takes a const char* that should point to a string constant containing +exactly two characters. These are the characters that should be unique for your client. Make +sure not to clash with anybody else. Here are some taken id's:

    + ++++ + + + + + + + + + + + + + + + + + + + +
    id charsclient
    'AZ'Azureus
    'LT'libtorrent (default)
    'BX'BittorrentX
    'MT'Moonlight Torrent
    +

    The major, minor, revision and tag parameters are used to identify the +version of your client. All these numbers must be within the range [0, 9].

    +

    to_string() will generate the actual string put in the peer-id, and return it.

    +
    +
    +

    alerts

    +

    The pop_alert() function on session is the interface for retrieving +alerts, warnings, messages and errors from libtorrent. If there hasn't +occured any errors (matching your severity level) pop_alert() will +return a zero pointer. If there has been some error, it will return a pointer +to an alert object describing it. You can then use the alert object and query +it for information about the error or message. To retrieve any alerts, you have +to select a severity level using session::set_severity_level(). It defaults to +alert::none, which means that you don't get any messages at all, ever. +You have the following levels to select among:

    + ++++ + + + + + + + + + + + + + + + + + + + + +
    noneNo alert will ever have this severity level, which +effectively filters all messages.
    fatalFatal errors will have this severity level. Examples can +be disk full or something else that will make it +impossible to continue normal execution.
    criticalSignals errors that requires user interaction or +messages that almost never should be ignored. For +example, a chat message received from another peer is +announced as severity critical.
    warningMessages with the warning severity can be a tracker that +times out or responds with invalid data. It will be +retried automatically, and the possible next tracker in +a multitracker sequence will be tried. It does not +require any user interaction.
    infoEvents that can be considered normal, but still deserves +an event. This could be a piece hash that fails.
    debugThis will include alot of debug events that can be used +both for debugging libtorrent but also when debugging +other clients that are connected to libtorrent. It will +report strange behaviors among the connected peers.
    +

    When setting a severity level, you will receive messages of that severity and all +messages that are more sever. If you set alert::none (the default) you will not recieve +any events at all.

    +

    When you set a severuty level other than none, you have the responsibility to call +pop_alert() from time to time. If you don't do that, the alert queue will just grow.

    +

    When you get an alert, you can use typeid() or dynamic_cast<> to get more detailed +information on exactly which type it is. i.e. what kind of error it is. You can also use a +dispatcher mechanism that's available in libtorrent.

    +

    The alert class is the base class that specific messages are derived from. This +is its synopsis:

    +
    +class alert
    +{
    +public:
    +
    +        enum severity_t { debug, info, warning, critital, fatal, none };
    +
    +        alert(severity_t severity, const std::string& msg);
    +        virtual ~alert();
    +
    +        const std::string& 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 +user in different ways.

    +

    The specific alerts, that all derives from alert, are:

    +
    +

    tracker_alert

    +

    This alert is generated on tracker time outs, premature disconnects, invalid response or +a HTTP response other than "200 OK". From the alert you can get the handle to the torrent +the tracker belongs to. This alert is generated as severity level warning.

    +
    +struct tracker_alert: alert
    +{
    +        tracker_alert(const torrent_handle& h, const std::string& msg);
    +        virtual std::auto_ptr<alert> clone() const;
    +
    +        torrent_handle handle;
    +};
    +
    +
    +
    +

    hash_failed_alert

    +

    This alert is generated when a finished piece fails its hash check. You can get the handle +to the torrent which got the failed piece and the index of the piece itself from the alert. +This alert is generated as severity level info.

    +
    +struct hash_failed_alert: alert
    +{
    +        hash_failed_alert(
    +                const torrent_handle& h
    +                , int index
    +                , const std::string& msg);
    +
    +        virtual std::auto_ptr<alert> clone() const;
    +
    +        torrent_handle handle;
    +        int piece_index;
    +};
    +
    +
    +
    +

    peer_error_alert

    +

    This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer +will be disconnected, but you get its peer-id from the alert. This alert is generated +as severity level debug.

    +
    +struct peer_error_alert: alert
    +{
    +        peer_error_alert(const peer_id& pid, const std::string& msg);
    +        virtual std::auto_ptr<alert> clone() const;
    +
    +        peer_id id;
    +};
    +
    +
    +
    +

    chat_message_alert

    +

    This alert is generated when you receive a chat message from another peer. Chat messages +are supported as an extension ("chat"). It is generated as severity level critical, +even though it doesn't necessarily require any user intervention, it's high priority +since you would almost never want to ignore such a message. The alert class contain +a torrent_handle to the torrent in which the sender-peer is a member and the peer_id +of the sending peer.

    +
    +struct chat_message_alert: alert
    +{
    +        chat_message_alert(const torrent_handle& h
    +                , const peer_id& sender
    +                , const std::string& msg);
    +
    +        virtual std::auto_ptr<alert> clone() const;
    +
    +        torrent_handle handle;
    +        peer_id sender;
    +};
    +
    +
    +
    +

    dispatcher

    +

    TODO: describe the dispatcher mechanism

    +
    +
    +
    +

    exceptions

    +

    There are a number of exceptions that can be thrown from different places in libtorrent, +here's a complete list with description.

    +
    +

    invalid_handle

    +

    This exception is thrown when querying information from a torrent_handle that hasn't +been initialized or that has become invalid.

    +
    +struct invalid_handle: std::exception
    +{
    +        const char* what() const throw();
    +};
    +
    +
    +
    +

    duplicate_torrent

    +

    This is thrown by session::add_torrent() if the torrent already has been added to +the session.

    +
    +struct duplicate_torrent: std::exception
    +{
    +        const char* what() const throw();
    +};
    +
    +
    +
    +

    invalid_encoding

    +

    This is thrown by bdecode() if the input data is not a valid bencoding.

    +
    +struct invalid_encoding: std::exception
    +{
    +        const char* what() const throw();
    +};
    +
    +
    +
    +

    type_error

    +

    This is thrown from the accessors of entry if the data type of the entry doesn't +match the type you want to extract from it.

    +
    +struct type_error: std::runtime_error
    +{
    +        type_error(const char* error);
    +};
    +
    +
    +
    +

    invalid_torrent_file

    +

    This exception is thrown from the constructor of torrent_info if the given bencoded information +doesn't meet the requirements on what information has to be present in a torrent file.

    +
    +struct invalid_torrent_file: std::exception
    +{
    +        const char* what() const throw();
    +};
    +
    +
    +
    +
    +

    examples

    +
    +

    dump_torrent

    +

    This is an example of a program that will take a torrent-file as a parameter and +print information about it to std out:

    +
    +#include <iostream>
    +#include <fstream>
    +#include <iterator>
    +#include <exception>
    +#include <iomanip>
    +
    +#include "libtorrent/entry.hpp"
    +#include "libtorrent/bencode.hpp"
    +#include "libtorrent/torrent_info.hpp"
    +
    +
    +int main(int argc, char* argv[])
    +{
    +        using namespace libtorrent;
    +
    +        if (argc != 2)
    +        {
    +                std::cerr << "usage: dump_torrent torrent-file\n";
    +                return 1;
    +        }
    +
    +        try
    +        {
    +                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);
    +
    +                // print info about torrent
    +                std::cout << "\n\n----- torrent file info -----\n\n";
    +                std::cout << "trackers:\n";
    +                for (std::vector<announce_entry>::const_iterator i = t.trackers().begin();
    +                        i != t.trackers().end();
    +                        ++i)
    +                {
    +                        std::cout << i->tier << ": " << i->url << "\n";
    +                }
    +
    +                std::cout << "number of pieces: " << t.num_pieces() << "\n";
    +                std::cout << "piece length: " << t.piece_length() << "\n";
    +                std::cout << "files:\n";
    +                for (torrent_info::file_iterator i = t.begin_files();
    +                        i != t.end_files();
    +                        ++i)
    +                {
    +                        std::cout << "  " << std::setw(11) << i->size
    +                        << "  " << i->path << " " << i->filename << "\n";
    +                }
    +                
    +        }
    +        catch (std::exception& e)
    +        {
    +                std::cout << e.what() << "\n";
    +        }
    +
    +        return 0;
    +}
    +
    +
    +
    +

    simple client

    +

    This is a simple client. It doesn't have much output to keep it simple:

    +
    +#include <iostream>
    +#include <fstream>
    +#include <iterator>
    +#include <exception>
    +
    +#include <boost/format.hpp>
    +#include <boost/date_time/posix_time/posix_time.hpp>
    +
    +#include "libtorrent/entry.hpp"
    +#include "libtorrent/bencode.hpp"
    +#include "libtorrent/session.hpp"
    +#include "libtorrent/http_settings.hpp"
    +
    +int main(int argc, char* argv[])
    +{
    +        using namespace libtorrent;
    +
    +        if (argc != 2)
    +        {
    +                std::cerr << "usage: ./simple_cient torrent-file\n"
    +                        "to stop the client, press return.\n";
    +                return 1;
    +        }
    +
    +        try
    +        {
    +                session s(6881);
    +
    +                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, "");
    +                        
    +                // wait for the user to end
    +                char a;
    +                std::cin.unsetf(std::ios_base::skipws);
    +                std::cin >> a;
    +        }
    +        catch (std::exception& e)
    +        {
    +                std::cout << e.what() << "\n";
    +        }
    +        return 0;
    +}
    +
    +
    +
    +
    +

    fast resume

    +

    The fast resume mechanism is a way to remember which pieces are downloaded and where they +are put between sessions. You can generate fast resume data by calling +torrent_handle::write_resume_data() on torrent_handle. You can then save this data +to disk and use it when resuming the torrent. libtorrent will not check the piece hashes +then, and rely on the information given in the fast-resume data. The fast-resume data +also contains information about which blocks, in the unfinished pieces, were downloaded, +so it will not have to start from scratch on the partially downloaded pieces.

    +

    To use the fast-resume data you simply give it to session::add_torrent(), and it +will skip the time consuming checks. It may have to do the checking anyway, if the +fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will +not trust the fast-resume data and just do the checking.

    +
    +
    +

    file format

    +

    TODO: describe the file format

    +
    +
    +

    extensions

    +

    These extensions all operates within the extension protocol. The +name of the extension is the name used in the extension-list packets, +and the payload is the data in the extended message (not counting the +length-prefix, message-id nor extension-id).

    +

    These are the extensions that are currently implemented.

    +
    +

    chat messages

    +

    Extension name: "chat"

    +

    The payload in the packet is a bencoded dictionary with any +combination of the following entries:

    + ++++ + + + + + + + + +
    "msg"This is a string that contains a message that +should be displayed to the user.
    "ctrl"This is a control string that can tell a client that +it is ignored (to make the user aware of that) and +it can also tell a client that it is no longer ignored. +These notifications are encoded as the strings: +"ignored" and "not ignored". +Any unrecognized strings should be ignored.
    +
    +
    +
    +

    Aknowledgements

    +

    Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003

    +

    Contributions by Magnus Jonsson

    +

    Thanks to Reimond Retz for bugfixes, suggestions and testing

    +

    Project is hosted by sourceforge.

    +

    sf_logo

    +
    +
    + + diff --git a/docs/manual.rst b/docs/manual.rst new file mode 100755 index 000000000..7265e3334 --- /dev/null +++ b/docs/manual.rst @@ -0,0 +1,1353 @@ +================= +libtorrent manual +================= + +.. contents:: + +introduction +============ + +libtorrent is a C++ library that aims to be a good alternative to all the +`other bittorrent implementations`__ around. It is a +library and not a full featured client, although it comes with a working +example client. + +__ links.html + +The main goals of libtorrent are: + + * to be cpu efficient + * to be memory efficient + * to be very easy to use + +libtorrent is not finished. It is an ongoing project (including this documentation). +The current state includes the following features: + + * multitracker extension support (as `described by TheShadow`__) + * serves multiple torrents on a single port and a single thread + * supports http proxies and proxy authentication + * gzipped tracker-responses + * piece picking on block-level (as opposed to piece-level) like in Azureus_ + * queues torrents for file check, instead of checking all of them in parallel. + * uses separate threads for checking files and for main downloader, with a fool-proof + thread-safe library interface. (i.e. There's no way for the user to cause a deadlock). + * can limit the upload bandwidth usage and the maximum number of unchoked peers + * piece-wise file allocation + * tries to maintain a 1:1 share ratio between all peers but also shifts free + download to peers as free upload. To maintain a global 1:1 ratio. + * fast resume support, a way to get rid of the costly piece check at the start + of a resumed torrent. Saves the storage state in a separate fast-resume file. + * The extension protocol `described by Nolar`__. See extensions_. + +__ http://home.elp.rr.com/tur/multitracker-spec.txt +.. _Azureus: http://azureus.sourceforge.net +__ http://nolar.com/azureus/extended.htm + +Functions that are yet to be implemented: + + * number of connections limit + * better handling of peers that send bad data + * ip-filters + * file-level piece priority + +libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread, +boost.filesystem, boost.date_time and various other boost libraries as well as zlib. + +libtorrent has been successfully compiled and tested on: + + * Cygwin GCC 3.3.1 + * Windows 2000 vc7.1 + * Linux x86 (debian) GCC 3.0 + +It does not compile on + + * GCC 2.95 + +building +======== + +To build libtorrent you need boost_ and bjam installed. +Then you can use ``bjam`` to build libtorrent. + +.. _boost: http://www.boost.org + +To make bjam work, you need to set the environment variable ``BOOST_ROOT`` to the +path where boost is installed (e.g. c:\boost_1_30_2 on windows). Then you can just run +``bjam`` in the libtorrent directory. + +The Jamfile doesn't work yet. On unix-systems you can use the makefile however. You +first have to build boost.thread and boost.filesystem. You do this by, in the directory +'boost-1.30.2/tools/build/jam_src' run the build script ``./build.sh``. This should +produce at least one folder with the 'bin' prefix (and the rest of the name describes +your platform). Put the files in that folder somewhere in your path. + +You can then invoke ``bjam`` in the directories 'boost-1.30.2/libs/thread/build', +'boost-1.30.2/libs/date_time/build' and 'boost-1.30.2/libs/filesystem/build'. That will +produce the needed libraries. Put these libraries in the libtorrent root directory. +You then have to modify the makefile to use you prefered compiler and to have the +correct path to your boost istallation. + +Then the makefile should be able to do the rest. + +When building (with boost 1.30.2) on linux and solaris however, I found that I had to make the following +modifications to the boost.date-time library. In the file: +'boost-1.30.2/boost/date_time/gregorian_calendar.hpp' line 59. Prepend 'boost/date_time/' +to the include path. + +And the second modification was in the file: +'boost-1.30.2/boost/date_time/microsec_time_clock.hpp' add the following include at the top +of the file:: + + #include "boost/cstdint.hpp" + +In developer studio, you may have to set the compiler options "force conformance in for +loop scope" and "treat wchar_t as built-in type" to Yes. + +TODO: more detailed build instructions. + + + + + +using +===== + +The interface of libtorrent consists of a few classes. The main class is +the ``session``, it contains the main loop that serves all torrents. + +The basic usage is as follows: + +* conststruct a session +* parse .torrent-files and add them to the session +* main loop + * query the torrent_handles for progress + * query the session for information + * add and remove torrents from the session at run-time +* destruct all torrent_handles +* destruct session object + +Each class and function is described in this manual. + + + +session +======= + +The ``session`` class has the following synopsis:: + + class session: public boost::noncopyable + { + session(int listen_port, const fingerprint& print); + session(int listen_port); + + torrent_handle add_torrent( + const torrent_info& t + , const std::string& save_path + , const entry& resume_data = entry()); + + void remove_torrent(const torrent_handle& h); + + void set_http_settings(const http_settings& settings); + void set_upload_rate_limit(int bytes_per_second); + + std::auto_ptr pop_alert(); + void set_severity_level(alert::severity_t s); + + }; + +Once it's created, it will spawn the main thread that will do all the work. +The main thread will be idle as long it doesn't have any torrents to participate in. +You add torrents through the ``add_torrent()``-function where you give an +object representing the information found in the torrent file and the path where you +want to save the files. The ``save_path`` will be prepended to the directory- +structure in the torrent-file. ``add_torrent`` will throw ``duplicate_torrent`` exception +if the torrent already exists in the session. + +The optional last parameter, ``resume_data`` can be given if up to date fast-resume data +is available. The fast-resume data can be acquired from a running torrent by calling +``torrent_handle::write_resume_data()``. See `fast resume`_. + +``remove_torrent()`` will close all peer connections associated with the torrent and tell +the tracker that we've stopped participating in the swarm. + +If the torrent you are trying to add already exists in the session (is either queued +for checking, being checked or downloading) ``add_torrent()`` will throw +``duplicate_torrent`` which derives from ``std::exception``. + +The difference between the two constructors is that one of them takes a fingerprint +as argument. If this is ommited, the client will get a default fingerprint stating +the version of libtorrent. The fingerprint is a short string that will be used in +the peer-id to identify the client and the client's version. For more details see the +fingerprint class. + +``set_upload_rate_limit()`` set the maximum number of bytes allowed to be +sent to peers per second. This bandwidth is distributed among all the peers. If +you don't want to limit upload rate, you can set this to -1 (the default). + +The destructor of session will notify all trackers that our torrents has 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 +timeout can be set with ``set_http_settings()``. + +How to parse a torrent file and create a ``torrent_info`` object is described below. + +The torrent_handle_ returned by ``add_torrent`` can be used to retrieve information +about the torrent's progress, its peers etc. It is also used to abort a torrent. + +The constructor takes a listen port as argument, if the given port is busy it will +increase the port number by one and try again. If it still fails it will continue +increasing the port number until it succeeds or has failed 9 ports. *This will +change in the future to give more control of the listen-port.* + +For information about the ``pop_alert()`` function, see alerts_. + + + +parsing torrent files +===================== + +The torrent files are bencoded__. There are two functions in libtorrent that can encode and decode +bencoded data. They are:: + + template entry bdecode(InIt start, InIt end); + template void bencode(OutIt out, const entry& e); + +__ http://bitconjurer.org/BitTorrent/protocol.html + + +The entry_ class is the internal representation of the bencoded data +and it can be used to retreive information, an entry_ can also be build by +the program and given to ``bencode()`` to encode it into the ``OutIt`` +iterator. + +The ``OutIt`` and ``InIt`` are iterators +(``InputIterator_`` and ``OutputIterator_`` respectively). They +are templates and are usually instantiated as ``ostream_iterator_``, +``back_insert_iterator_`` or ``istream_iterator_``. These +functions will assume that the iterator refers to a character +(``char``). So, if you want to encode entry ``e`` into a buffer +in memory, you can do it like this:: + + std::vector buffer; + bencode(std::back_insert_iterator >(buf), e); + +.. _InputIterator: http://www.sgi.com/tech/stl/InputIterator.html +.. _OutputIterator: http://www.sgi.com/tech/stl/OutputIterator.html +.. _ostream_iterator: http://www.sgi.com/tech/stl/ostream_iterator.html +.. _back_insert_iterator: http://www.sgi.com/tech/stl/back_insert_iterator.html +.. _istream_iterator: http://www.sgi.com/tech/stl/istream_iterator.html + + +If you want to decode a torrent file from a buffer in memory, you can do it like this:: + + std::vector buffer; + // ... + entry e = bdecode(buf.begin(), buf.end()); + +Or, if you have a raw char buffer:: + + const char* buf; + // ... + entry e = bdecode(buf, buf + data_size); + +Now we just need to know how to retrieve information from the entry_. + +If ``bdecode()`` encounters invalid encoded data in the range given to it +it will throw invalid_encoding_. + + + +entry +===== + +The ``entry`` class represents one node in a bencoded hierarchy. It works as a +variant type, it can be either a list, a dictionary (``std::map``), an integer +or a string. This is its synopsis:: + + class entry + { + public: + + typedef std::map dictionary_type; + typedef std::string string_type; + typedef std::vector list_type; + typedef implementation-defined integer_type; + + enum data_type + { + int_t, + string_t, + list_t, + dictionary_t, + undefined_t + }; + + data_type type() const; + + entry(const dictionary_type&); + entry(const string_type&); + entry(const list_type&); + entry(const integer_type&); + + entry(); + entry(data_type t); + entry(const entry& e); + + ~entry(); + + void operator=(const entry& e); + void operator=(const dictionary_type&); + void operator=(const string_type&); + void operator=(const list_type&); + void operator=(const integer_type&); + + integer_type& integer() + const integer_type& integer() const; + string_type& string(); + const string_type& string() const; + list_type& list(); + const list_type& list() const; + dictionary_type& dict(); + const dictionary_type& dict() const; + + void print(std::ostream& os, int indent = 0) const; + }; + +The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions +are accessorts that return the respecive type. If the ``entry`` object isn't of the +type you request, the accessor will throw type_error_ (which derives from +``std::runtime_error``). You can ask an ``entry`` for its type through the +``type()`` function. + +The ``print()`` function is there for debug purposes only. + +If you want to create an ``entry`` you give it the type you want it to have in its +constructor, and then use one of the non-const accessors to get a reference which you then +can assign the value you want it to have. + +The typical code to get info from a torrent file will then look like this:: + + entry torrent_file; + // ... + + const entry::dictionary_type& dict = torrent_file.dict(); + entry::dictionary_type::const_iterator i; + i = dict.find("announce"); + if (i != dict.end()) + { + std::string tracker_url= i->second.string(); + std::cout << tracker_url << "\n"; + } + +To make it easier to extract information from a torren file, the class ``torrent_info`` +exists. + + + +torrent_info +============ + +The ``torrent_info`` has the following synopsis:: + + class torrent_info + { + public: + + torrent_info(const entry& torrent_file) + + typedef std::vector>file>::const_iterator file_iterator; + typedef std::vector::const_reverse_iterator reverse_file_iterator; + + file_iterator begin_files() const; + file_iterator end_files() const; + reverse_file_iterator rbegin_files() const; + reverse_file_iterator rend_files() const; + + std::size_t num_files() const; + const file& file_at(int index) const; + + const std::vector& trackers() const; + + int prioritize_tracker(int index); + + entry::integer_type total_size() const; + entry::integer_type piece_length() const; + std::size_t num_pieces() const; + const sha1_hash& info_hash() const; + const std::stirng& name() const; + const std::string& comment() const; + boost::posiz_time::ptime creation_date() const; + + + void print(std::ostream& os) const; + + entry::integer_type piece_size(unsigned int index) const; + const sha1_hash& hash_for_piece(unsigned int index) const; + }; + +This class will need some explanation. First of all, to get a list of all files +in the torrent, you can use ``begin_files()``, ``end_files()``, +``rbegin_files()`` and ``rend_files()``. These will give you standard vector +iterators with the type ``file``. + +:: + + struct file + { + std::string path; + std::string filename; + entry::integer_type size; + }; + +If you need index-access to files you can use the ``num_files()`` and ``file_at()`` +to access files using indices. + +The ``print()`` function is there for debug purposes only. It will print the info from +the torrent file to the given outstream. + +``name()`` returns the name of the torrent. + +The ``trackers()`` function will return a sorted vector of ``announce_entry``. +Each announce entry contains a string, which is the tracker url, and a tier index. The +tier index is the high-level priority. No matter which trackers that works or not, the +ones with lower tier will always be tried before the one with higher tier number. + +:: + + struct announce_entry + { + std::string url; + int tier; + }; + +The ``prioritize_tracker()`` is used internally to move a tracker to the front +of its tier group. i.e. It will never be moved pass a tracker with a different tier +number. For more information about how multiple trackers are dealt with, see the +specification_. + +.. _specification: http://home.elp.rr.com/tur/multitracker-spec.txt + + +``total_size()``, ``piece_length()`` and ``num_pieces()`` returns the total +number of bytes the torrent-file represents (all the files in it), the number of byte for +each piece and the total number of pieces, respectively. The difference between +``piece_size()`` and ``piece_length()`` is that ``piece_size()`` takes +the piece index as argument and gives you the exact size of that piece. It will always +be the same as ``piece_length()`` except in the case of the last piece, which may +be smaller. + +``hash_for_piece()`` takes a piece-index and returns the 20-bytes sha1-hash for that +piece and ``info_hash()`` returns the 20-bytes sha1-hash for the info-section of the +torrent file. For more information on the ``sha1_hash``, see the big_number_ class. + +``comment()`` returns the comment associated with the torrent. If there's no comment, +it will return an empty string. ``creation_date()`` returns a `boost::posix_time::ptime`__ +object, representing the time when this torrent file was created. If there's no timestamp +in the torrent file, this will return a date of january 1:st 1970. + +__ http://www.boost.org/libs/date_time/doc/class_ptime.html + + + + +torrent_handle +============== + +You will usually have to store your torrent handles somewhere, since it's the +object through which you retrieve infromation about the torrent and aborts the torrent. +Its declaration looks like this:: + + struct torrent_handle + { + torrent_handle(); + + torrent_status status(); + void get_download_queue(std::vector& queue); + void get_peer_info(std::vector& v); + const torrent_info& get_torrent_info(); + bool is_valid(); + + entry write_resume_data(); + + boost::filsystem::path save_path() const; + + void set_max_uploads(int max_uploads); + + sha1_hash info_hash() const; + + bool operator==(const torrent_handle&) const; + bool operator!=(const torrent_handle&) const; + bool operator<(const torrent_handle&) const; + }; + +The default constructor will initialize the handle to an invalid state. Which means you cannot +perform any operation on it, unless you first assign it a valid handle. If you try to perform +any operation on an uninitialized handle, it will throw ``invalid_handle``. + +``save_path()`` returns the path that was given to ``add_torrent()`` when this torrent +was started. + +``info_hash()`` returns the info hash for the torrent. + +``set_max_uploads()`` sets the maximum number of peers that's unchoked at the same time on this +torrent. If you set this to -1, there will be no limit. + +``write_resume_data()`` generates fast-resume data and returns it as an entry. This entry +is suitable for being bencoded. For more information about how fast-resume works, see `fast resume`_. +It may throw invalid_handle_ if the torrent handle is invalid. + +status() +-------- + +``status()`` will return a structure with information about the status of this +torrent. If the torrent_handle_ is invalid, it will throw invalid_handle_ exception. +It contains the following fields:: + + struct torrent_status + { + enum state_t + { + invalid_handle, + queued_for_checking, + checking_files, + connecting_to_tracker, + downloading, + seeding + }; + + state_t state; + float progress; + boost::posix_time::time_duration next_announce; + + std::size_t total_download; + std::size_t total_upload; + + std::size_t total_payload_download; + std::size_t total_payload_upload; + + float download_rate; + float upload_rate; + + std::vector pieces; + std::size_t total_done; + }; + +``progress`` is a value in the range [0, 1], that represents the progress of the +torrent's current task. It may be checking files or downloading. The torrent's +current task is in the ``state`` member, it will be one of the following: + ++--------------------------+----------------------------------------------------------+ +|``queued_for_checking`` |The torrent is in the queue for being checked. But there | +| |currently is another torrent that are being checked. | +| |This torrent will wait for its turn. | +| | | ++--------------------------+----------------------------------------------------------+ +|``checking_files`` |The torrent has not started its download yet, and is | +| |currently checking existing files. | +| | | ++--------------------------+----------------------------------------------------------+ +|``connecting_to_tracker`` |The torrent has sent a request to the tracker and is | +| |currently waiting for a response | +| | | ++--------------------------+----------------------------------------------------------+ +|``downloading`` |The torrent is being downloaded. This is the state | +| |most torrents will be in most of the time. The progress | +| |meter will tell how much of the files that has been | +| |downloaded. | +| | | ++--------------------------+----------------------------------------------------------+ +|``seeding`` |In this state the torrent has finished downloading and | +| |is a pure seeder. | +| | | ++--------------------------+----------------------------------------------------------+ + +``next_announce`` is the time until the torrent will announce itself to the tracker. + +``total_download`` and ``total_upload`` is the number of bytes downloaded and +uploaded to all peers, accumulated, *this session* only. + +``total_payload_download`` and ``total_payload_upload`` counts the amount of bytes +send and received this session, but only the actual oayload data (i.e the interesting +data), these counters ignore any protocol overhead. + +``pieces`` is the bitmask that representw which pieces we have (set to true) and +the pieces we don't have. + +``download_rate`` and ``upload_rate`` are the total rates for all peers for this +torrent. These will usually have better precision than summing the rates from +all peers. The rates are given as the number of bytes per second. + +``total_done`` is the total number of bytes of the file(s) that we have. + +get_download_queue() +-------------------- + +``get_download_queue()`` takes a non-const reference to a vector which it will fill +information about pieces that are partially downloaded or not downloaded at all but partially +requested. The entry in the vector (``partial_piece_info``) looks like this:: + + struct partial_piece_info + { + enum { max_blocks_per_piece }; + int piece_index; + int blocks_in_piece; + std::bitset requested_blocks; + std::bitset finished_blocks; + peer_id peer[max_blocks_per_piece]; + int num_downloads[max_blocks_per_piece]; + }; + +``piece_index`` is the index of the piece in question. ``blocks_in_piece`` is the +number of blocks in this particular piece. This number will be the same for most pieces, but +the last piece may have fewer blocks than the standard pieces. + +``requested_blocks`` is a bitset with one bit per block in the piece. If a bit is set, it +means that that block has been requested, but not necessarily fully downloaded yet. To know +from whom the block has been requested, have a look in the ``peer`` array. The bit-index +in the ``requested_blocks`` and ``finished_blocks`` correspons to the array-index into +``peers`` and ``num_downloads``. The array of peers is contains the id of the +peer the piece was requested from. If a piece hasn't been requested (the bit in +``requested_blocks`` is not set) the peer array entry will be undefined. + +The ``finished_blocks`` is a bitset where each bit says if the block is fully downloaded +or not. And the ``num_downloads`` array says how many times that block has been downloaded. +When a piece fails a hash verification, single blocks may be redownloaded to see if the hash teast +may pass then. + + +get_peer_info() +--------------- + +``get_peer_info()`` takes a reference to a vector that will be cleared and filled +with one entry for each peer connected to this torrent, given the handle is valid. If the +torrent_handle_ is invalid, it will throw invalid_handle_ exception. Each entry in +the vector contains information about that particular peer. It contains the following +fields:: + + struct peer_info + { + enum + { + interesting = 0x1, + choked = 0x2, + remote_interested = 0x4, + remote_choked = 0x8, + supports_extensions = 0x10 + }; + unsigned int flags; + address ip; + float up_speed; + float down_speed; + unsigned int total_download; + unsigned int total_upload; + peer_id id; + std::vector pieces; + int upload_limit; + int upload_ceiling; + + int load_balancing; + + int downloading_piece_index; + int downloading_block_index; + int downloading_progress; + int downloading_total; + }; + +The ``flags`` attribute tells you in which state the peer is. It is set to +any combination of the four enums above. Where ``interesting`` means that we +are interested in pieces from this peer. ``choked`` means that **we** have +choked this peer. ``remote_interested`` and ``remote_choked`` means the +same thing but that the peer is interested in pieces from us and the peer has choked +**us**. ``support_extensions`` means that this peer supports the `extension protocol +as described by nolar`__. + +__ http://nolar.com/azureus/extended.htm + +The ``ip`` field is the IP-address to this peer. Its type is a wrapper around the +actual address and the port number. See address_ class. + +``up_speed`` and ``down_speed`` is the current upload and download speed +we have to and from this peer. These figures are updated aproximately once every second. + +``total_download`` and ``total_upload`` are the total number of bytes downloaded +from and uploaded to this peer. These numbers do not include the protocol chatter, but only +the payload data. + +``id`` is the peer's id as used in the bit torrent protocol. This id can be used to +extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer +is using. + +``pieces`` is a vector of booleans that has as many entries as there are pieces +in the torrent. Each boolean tells you if the peer has that piece (if it's set to true) +or if the peer miss that piece (set to false). + +``upload_limit`` is the number of bytes per second we are allowed to send to this +peer every second. It may be -1 if there's no limit. The upload limits of all peers +should sum up to the upload limit set by ``session::set_upload_limit``. + +``upload_ceiling`` is the current maximum allowed upload rate given the cownload +rate and share ratio. If the global upload rate is inlimited, the ``upload_limit`` +for every peer will be the same as their ``upload_ceiling``. + +``load_balancing`` is a measurment of the balancing of free download (that we get) +and free upload that we give. Every peer gets a certain amount of free upload, but +this member says how much *extra* free upload this peer has got. If it is a negative +number it means that this was a peer from which we have got this amount of free +download. + +You can know which piece, and which part of that piece, that is currently being +downloaded from a specific peer by looking at the next four members. +``downloading_piece_index`` is the index of the piece that is currently being downloaded. +This may be set to -1 if there's currently no piece downloading from this peer. If it is +>= 0, the other three members are valid. ``downloading_block_index`` is the index of the +block (or sub-piece) that is being downloaded. ``downloading_progress`` is the number +of bytes of this block we have received from the peer, and ``downloading_total`` is +the total number of bytes in this block. + + +get_torrent_info() +------------------ + +Returns a const reference to the ``torrent_info`` object associated with this torrent. +This reference is valid as long as the torrent_handle_ is valid, no longer. If the +torrent_handle_ is invalid, invalid_handle_ exception will be thrown. + + +is_valid() +---------- + +Returns true if this handle refers to a valid torrent and false if it hasn't been initialized +or if the torrent it refers to has been aborted. + + + +address +======= + +The ``address`` class represents a name of a network endpoint (usually referred to as +IP-address) and a port number. This is the same thing as a ``sockaddr_in`` would contain. +Its declaration looks like this:: + + class address + { + public: + address(); + address(unsigned char a + , unsigned char b + , unsigned char c + , unsigned char d + , unsigned short port); + address(unsigned int addr, unsigned short port); + address(const std::string& addr, unsigned short port); + address(const address& a); + ~address(); + + std::string as_string() const; + unsigned int ip() const; + unsigned short port() const; + + bool operator<(const address& a) const; + bool operator!=(const address& a) const; + bool operator==(const address& a) const; + }; + +It is less-than comparable to make it possible to use it as a key in a map. ``as_string()`` may block +while it does the DNS lookup, it returns a string that points to the address represented by the object. + +``ip()`` will return the 32-bit ip-address as an integer. ``port()`` returns the port number. + + + + +http_settings +============= + +You have some control over tracker requests through the ``http_settings`` object. You +create it and fill it with your settings and the use ``session::set_http_settings()`` +to apply them. You have control over proxy and authorization settings and also the user-agent +that will be sent to the tracker. The user-agent is a good way to identify your client. + +:: + + struct http_settings + { + http_settings(); + std::string proxy_ip; + int proxy_port; + std::string proxy_login; + std::string proxy_password; + std::string user_agent; + int tracker_timeout; + int tracker_maximum_response_length; + }; + +``proxy_ip`` may be a hostname or ip to a http proxy to use. If this is +an empty string, no http proxy will be used. + +``proxy_port`` is the port on which the http proxy listens. If ``proxy_ip`` +is empty, this will be ignored. + +``proxy_login`` should be the login username for the http proxy, if this +empty, the http proxy will be trid to be used without authentication. + +``proxy_password`` the password string for the http proxy. + +``user_agent`` this is the client identification to the tracker. It will +be followed by the string "(libtorrent)" to identify that this library +is being used. This should be set to your client's name and version number. + +``tracker_timeout`` is the number of seconds the tracker connection will +wait until it considers the tracker to have timed-out. Default value is 10 +seconds. + +``tracker_maximum_response_length`` is the maximum number of bytes in a +tracker response. If a response size passes this number it will be rejected +and the connection will be closed. On gzipped responses this size is measured +on the uncompressed data. So, if you get 20 bytes of gzip response that'll +expand to 2 megs, it will be interrupted before the entire response has been +uncompressed (given your limit is lower than 2 megs). Default limit is +1 megabyte. + + + +big_number +========== + +Both the ``peer_id`` and ``sha1_hash`` types are typedefs of the class +``big_number``. It represents 20 bytes of data. Its synopsis follows:: + + class big_number + { + public: + bool operator==(const big_number& n) const; + bool operator!=(const big_number& n) const; + bool operator<(const big_number& n) const; + + const unsigned char* begin() const; + const unsigned char* end() const; + + unsigned char* begin(); + unsigned char* end(); + }; + +The iterators gives you access to individual bytes. + + + +hasher +====== + +This class creates sha1-hashes. Its declaration looks like this:: + + class hasher + { + public: + hasher(); + + void update(const char* data, unsigned int len); + sha1_hash final(); + void reset(); + }; + + +You use it by first instantiating it, then call ``update()`` to feed it +with data. i.e. you don't have to keep the entire buffer of which you want to +create the hash in memory. You can feed the hasher parts of it at a time. When +You have fed the hasher with all the data, you call ``final()`` and it +will return the sha1-hash of the data. + +If you want to reuse the hasher object once you have created a hash, you have to +call ``reset()`` to reinitialize it. + +The sha1-algorithm used was implemented by Steve Reid and released as public domain. +For more info, see ``src/sha1.c``. + + +fingerprint +=========== + +The fingerprint class represents information about a client and its version. It is used +to encode this information into the client's peer id. + +This is the class declaration:: + + struct fingerprint + { + fingerprint(const char* id_string, int major, int minor, int revision, int tag); + + std::string to_string() const; + + char id[2]; + char major_version; + char minor_version; + char revision_version; + char tag_version; + + }; + +The constructor takes a ``const char*`` that should point to a string constant containing +exactly two characters. These are the characters that should be unique for your client. Make +sure not to clash with anybody else. Here are some taken id's: + ++----------+-----------------------+ +| id chars | client | ++==========+=======================+ +| 'AZ' | Azureus | ++----------+-----------------------+ +| 'LT' | libtorrent (default) | ++----------+-----------------------+ +| 'BX' | BittorrentX | ++----------+-----------------------+ +| 'MT' | Moonlight Torrent | ++----------+-----------------------+ + + +The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the +version of your client. All these numbers must be within the range [0, 9]. + +``to_string()`` will generate the actual string put in the peer-id, and return it. + +alerts +====== + +The ``pop_alert()`` function on session is the interface for retrieving +alerts, warnings, messages and errors from libtorrent. If there hasn't +occured any errors (matching your severity level) ``pop_alert()`` will +return a zero pointer. If there has been some error, it will return a pointer +to an alert object describing it. You can then use the alert object and query +it for information about the error or message. To retrieve any alerts, you have +to select a severity level using ``session::set_severity_level()``. It defaults to +``alert::none``, which means that you don't get any messages at all, ever. +You have the following levels to select among: + ++--------------+----------------------------------------------------------+ +| ``none`` | No alert will ever have this severity level, which | +| | effectively filters all messages. | +| | | ++--------------+----------------------------------------------------------+ +| ``fatal`` | Fatal errors will have this severity level. Examples can | +| | be disk full or something else that will make it | +| | impossible to continue normal execution. | +| | | ++--------------+----------------------------------------------------------+ +| ``critical`` | Signals errors that requires user interaction or | +| | messages that almost never should be ignored. For | +| | example, a chat message received from another peer is | +| | announced as severity ``critical``. | +| | | ++--------------+----------------------------------------------------------+ +| ``warning`` | Messages with the warning severity can be a tracker that | +| | times out or responds with invalid data. It will be | +| | retried automatically, and the possible next tracker in | +| | a multitracker sequence will be tried. It does not | +| | require any user interaction. | +| | | ++--------------+----------------------------------------------------------+ +| ``info`` | Events that can be considered normal, but still deserves | +| | an event. This could be a piece hash that fails. | +| | | ++--------------+----------------------------------------------------------+ +| ``debug`` | This will include alot of debug events that can be used | +| | both for debugging libtorrent but also when debugging | +| | other clients that are connected to libtorrent. It will | +| | report strange behaviors among the connected peers. | +| | | ++--------------+----------------------------------------------------------+ + +When setting a severity level, you will receive messages of that severity and all +messages that are more sever. If you set ``alert::none`` (the default) you will not recieve +any events at all. + +When you set a severuty level other than ``none``, you have the responsibility to call +``pop_alert()`` from time to time. If you don't do that, the alert queue will just grow. + +When you get an alert, you can use ``typeid()`` or ``dynamic_cast<>`` to get more detailed +information on exactly which type it is. i.e. what kind of error it is. You can also use a +dispatcher_ mechanism that's available in libtorrent. + +The ``alert`` class is the base class that specific messages are derived from. This +is its synopsis:: + + class alert + { + public: + + enum severity_t { debug, info, warning, critital, fatal, none }; + + alert(severity_t severity, const std::string& msg); + virtual ~alert(); + + const std::string& 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 +user in different ways. + +The specific alerts, that all derives from ``alert``, are: + + +tracker_alert +------------- + +This alert is generated on tracker time outs, premature disconnects, invalid response or +a HTTP response other than "200 OK". From the alert you can get the handle to the torrent +the tracker belongs to. This alert is generated as severity level ``warning``. + +:: + + struct tracker_alert: alert + { + tracker_alert(const torrent_handle& h, const std::string& msg); + virtual std::auto_ptr clone() const; + + torrent_handle handle; + }; + + +hash_failed_alert +----------------- + +This alert is generated when a finished piece fails its hash check. You can get the handle +to the torrent which got the failed piece and the index of the piece itself from the alert. +This alert is generated as severity level ``info``. + +:: + + struct hash_failed_alert: alert + { + hash_failed_alert( + const torrent_handle& h + , int index + , const std::string& msg); + + virtual std::auto_ptr clone() const; + + torrent_handle handle; + int piece_index; + }; + + +peer_error_alert +---------------- + +This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer +will be disconnected, but you get its peer-id from the alert. This alert is generated +as severity level ``debug``. + +:: + + struct peer_error_alert: alert + { + peer_error_alert(const peer_id& pid, const std::string& msg); + virtual std::auto_ptr clone() const; + + peer_id id; + }; + + + +chat_message_alert +------------------ + +This alert is generated when you receive a chat message from another peer. Chat messages +are supported as an extension ("chat"). It is generated as severity level ``critical``, +even though it doesn't necessarily require any user intervention, it's high priority +since you would almost never want to ignore such a message. The alert class contain +a torrent_handle_ to the torrent in which the sender-peer is a member and the peer_id +of the sending peer. + +:: + + struct chat_message_alert: alert + { + chat_message_alert(const torrent_handle& h + , const peer_id& sender + , const std::string& msg); + + virtual std::auto_ptr clone() const; + + torrent_handle handle; + peer_id sender; + }; + + +dispatcher +---------- + +TODO: describe the dispatcher mechanism + + + +exceptions +========== + +There are a number of exceptions that can be thrown from different places in libtorrent, +here's a complete list with description. + + +invalid_handle +-------------- + +This exception is thrown when querying information from a torrent_handle_ that hasn't +been initialized or that has become invalid. + +:: + + struct invalid_handle: std::exception + { + const char* what() const throw(); + }; + + +duplicate_torrent +----------------- + +This is thrown by ``session::add_torrent()`` if the torrent already has been added to +the session. + +:: + + struct duplicate_torrent: std::exception + { + const char* what() const throw(); + }; + + +invalid_encoding +---------------- + +This is thrown by ``bdecode()`` if the input data is not a valid bencoding. + +:: + + struct invalid_encoding: std::exception + { + const char* what() const throw(); + }; + + +type_error +---------- + +This is thrown from the accessors of ``entry`` if the data type of the ``entry`` doesn't +match the type you want to extract from it. + +:: + + struct type_error: std::runtime_error + { + type_error(const char* error); + }; + + +invalid_torrent_file +-------------------- + +This exception is thrown from the constructor of ``torrent_info`` if the given bencoded information +doesn't meet the requirements on what information has to be present in a torrent file. + +:: + + struct invalid_torrent_file: std::exception + { + const char* what() const throw(); + }; + + +examples +======== + +dump_torrent +------------ + +This is an example of a program that will take a torrent-file as a parameter and +print information about it to std out:: + + #include + #include + #include + #include + #include + + #include "libtorrent/entry.hpp" + #include "libtorrent/bencode.hpp" + #include "libtorrent/torrent_info.hpp" + + + int main(int argc, char* argv[]) + { + using namespace libtorrent; + + if (argc != 2) + { + std::cerr << "usage: dump_torrent torrent-file\n"; + return 1; + } + + try + { + 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); + + // print info about torrent + std::cout << "\n\n----- torrent file info -----\n\n"; + std::cout << "trackers:\n"; + for (std::vector::const_iterator i = t.trackers().begin(); + i != t.trackers().end(); + ++i) + { + std::cout << i->tier << ": " << i->url << "\n"; + } + + std::cout << "number of pieces: " << t.num_pieces() << "\n"; + std::cout << "piece length: " << t.piece_length() << "\n"; + std::cout << "files:\n"; + for (torrent_info::file_iterator i = t.begin_files(); + i != t.end_files(); + ++i) + { + std::cout << " " << std::setw(11) << i->size + << " " << i->path << " " << i->filename << "\n"; + } + + } + catch (std::exception& e) + { + std::cout << e.what() << "\n"; + } + + return 0; + } + + +simple client +------------- + +This is a simple client. It doesn't have much output to keep it simple:: + + #include + #include + #include + #include + + #include + #include + + #include "libtorrent/entry.hpp" + #include "libtorrent/bencode.hpp" + #include "libtorrent/session.hpp" + #include "libtorrent/http_settings.hpp" + + int main(int argc, char* argv[]) + { + using namespace libtorrent; + + if (argc != 2) + { + std::cerr << "usage: ./simple_cient torrent-file\n" + "to stop the client, press return.\n"; + return 1; + } + + try + { + session s(6881); + + 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, ""); + + // wait for the user to end + char a; + std::cin.unsetf(std::ios_base::skipws); + std::cin >> a; + } + catch (std::exception& e) + { + std::cout << e.what() << "\n"; + } + return 0; + } + + +fast resume +=========== + +The fast resume mechanism is a way to remember which pieces are downloaded and where they +are put between sessions. You can generate fast resume data by calling +``torrent_handle::write_resume_data()`` on torrent_handle_. You can then save this data +to disk and use it when resuming the torrent. libtorrent will not check the piece hashes +then, and rely on the information given in the fast-resume data. The fast-resume data +also contains information about which blocks, in the unfinished pieces, were downloaded, +so it will not have to start from scratch on the partially downloaded pieces. + +To use the fast-resume data you simply give it to ``session::add_torrent()``, and it +will skip the time consuming checks. It may have to do the checking anyway, if the +fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will +not trust the fast-resume data and just do the checking. + +file format +=========== + +TODO: describe the file format + +extensions +========== + +These extensions all operates within the `extension protocol`__. The +name of the extension is the name used in the extension-list packets, +and the payload is the data in the extended message (not counting the +length-prefix, message-id nor extension-id). + +__ http://nolar.com/azureus/extended.html + +These are the extensions that are currently implemented. + +chat messages +------------- + +Extension name: "chat" + +The payload in the packet is a bencoded dictionary with any +combination of the following entries: + ++----------+--------------------------------------------------------+ +| "msg" | This is a string that contains a message that | +| | should be displayed to the user. | ++----------+--------------------------------------------------------+ +| "ctrl" | This is a control string that can tell a client that | +| | it is ignored (to make the user aware of that) and | +| | it can also tell a client that it is no longer ignored.| +| | These notifications are encoded as the strings: | +| | "ignored" and "not ignored". | +| | Any unrecognized strings should be ignored. | ++----------+--------------------------------------------------------+ + + + +Aknowledgements +=============== + +Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003 + +Contributions by Magnus Jonsson + +Thanks to Reimond Retz for bugfixes, suggestions and testing + +Project is hosted by sourceforge. + +|sf_logo|__ + +.. |sf_logo| image:: http://sourceforge.net/sflogo.php?group_id=7994 +__ http://sourceforge.net + + diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 6352e2858..366d22db1 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -206,10 +206,8 @@ int main(int argc, char* argv[]) std::ifstream resume_file("test.fastresume", std::ios_base::binary); resume_file.unsetf(std::ios_base::skipws); - std::vector resume_data; - std::copy(std::istream_iterator(resume_file) - , std::istream_iterator() - , std::back_inserter(resume_data)); + entry resume_data = bdecode(std::istream_iterator(resume_file) + , std::istream_iterator()); handles.push_back(ses.add_torrent(t, "", resume_data)); handles.back().set_max_uploads(40); @@ -230,12 +228,11 @@ int main(int argc, char* argv[]) { if (c == 'q') { - std::vector data; - handles.front().write_resume_data(data); + entry data = handles.front().write_resume_data(); std::ofstream out("test.fastresume", std::ios_base::binary); out.unsetf(std::ios_base::skipws); - std::copy(data.begin(), data.end(), std::ostream_iterator(out)); + bencode(std::ostream_iterator(out), data); break; } } @@ -313,8 +310,7 @@ int main(int argc, char* argv[]) << "(" << add_suffix(i->total_download) << ") " << "u: " << add_suffix(i->up_speed) << "/s " << "(" << add_suffix(i->total_upload) << ") " -// << "df: " << add_suffix((int)i->total_download - (int)i->total_upload) << " " - << "q: " << i->download_queue_length << " " + << "df: " << add_suffix((int)i->total_download - (int)i->total_upload) << " " << "f: " << static_cast((i->flags & peer_info::interesting)?"I":"_") << static_cast((i->flags & peer_info::choked)?"C":"_") @@ -336,13 +332,8 @@ int main(int argc, char* argv[]) if (progress > j) out << "#"; else out << "-"; } - out << " "; + out << "\n"; } - else - { - for (int i = 0; i < 19; ++i) out << " "; - } - out << identify_client(i->id) << "\n"; } out << "___________________________________\n"; diff --git a/examples/dump_torrent.cpp b/examples/dump_torrent.cpp index 5d2b4d9ca..a3a5a0445 100755 --- a/examples/dump_torrent.cpp +++ b/examples/dump_torrent.cpp @@ -55,7 +55,6 @@ 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); @@ -63,6 +62,8 @@ int main(int argc, char* argv[]) e.print(std::cout); + torrent_info t(e); + // print info about torrent std::cout << "\n\n----- torrent file info -----\n\n"; std::cout << "trackers:\n"; diff --git a/include/libtorrent/alert.hpp b/include/libtorrent/alert.hpp index e9cecf7d3..00665a44d 100755 --- a/include/libtorrent/alert.hpp +++ b/include/libtorrent/alert.hpp @@ -51,7 +51,7 @@ namespace libtorrent { class alert { public: - enum severity_t { debug, info, warning, critital, fatal, none }; + enum severity_t { debug, info, warning, critical, fatal, none }; alert(severity_t severity, const std::string& msg) : m_msg(msg) diff --git a/include/libtorrent/entry.hpp b/include/libtorrent/entry.hpp index 24e7cdd75..6991f61c6 100755 --- a/include/libtorrent/entry.hpp +++ b/include/libtorrent/entry.hpp @@ -129,6 +129,11 @@ namespace libtorrent data_type type() const { return m_type; } + entry(const dictionary_type&); + entry(const string_type&); + entry(const list_type&); + entry(const integer_type&); + entry(): m_type(undefined_t) {} entry(data_type t): m_type(t) { construct(t); } entry(const entry& e) { copy(e); } @@ -140,6 +145,11 @@ namespace libtorrent copy(e); } + void operator=(const dictionary_type&); + void operator=(const string_type&); + void operator=(const list_type&); + void operator=(const integer_type&); + integer_type& integer() { if (m_type != int_t) throw type_error("invalid typ requested from entry"); diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 69553f356..5164066af 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include @@ -50,6 +51,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/piece_picker.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/debug.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" // TODO: each time a block is 'taken over' // from another peer. That peer must be given @@ -135,6 +138,23 @@ namespace libtorrent protocol_error(const std::string& msg): std::runtime_error(msg) {}; }; + struct chat_message_alert: alert + { + chat_message_alert(const torrent_handle& h + , const peer_id& send + , const std::string& msg) + : alert(alert::critical, msg) + , handle(h) + , sender(send) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new chat_message_alert(*this)); } + + torrent_handle handle; + peer_id sender; + }; + struct peer_request { int piece; @@ -336,6 +356,7 @@ namespace libtorrent void send_have(int index); void send_handshake(); void send_extensions(); + void send_chat_message(const std::string& msg); // is used during handshake enum state @@ -519,7 +540,7 @@ namespace libtorrent enum extension_index { - gzip_piece, + extended_chat_message, num_supported_extensions }; static const char* extension_names[num_supported_extensions]; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 10ab4aab1..3e257f489 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -116,7 +116,7 @@ namespace libtorrent sha1_hash info_hash; void parse_resume_data( - const std::vector* rd + const entry& rd , const torrent_info& info); std::vector piece_map; std::vector unfinished_pieces; @@ -225,20 +225,10 @@ namespace libtorrent ~session(); // 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) - { - return add_torrent_impl(ti, save_path, 0); - } - torrent_handle add_torrent( const torrent_info& ti , const boost::filesystem::path& save_path - , const std::vector& resume_data) - { - return add_torrent_impl(ti, save_path, &resume_data); - } + , const entry& resume_data = entry()); void remove_torrent(const torrent_handle& h); @@ -252,11 +242,6 @@ namespace libtorrent private: - torrent_handle add_torrent_impl( - const torrent_info& ti - , const boost::filesystem::path& save_path - , const std::vector* resume_data); - // data shared between the main thread // and the working thread detail::session_impl m_impl; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 6543f834e..614c24dc0 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -247,6 +247,9 @@ namespace libtorrent boost::filesystem::path save_path() const { return m_storage.save_path(); } + alert_manager& alerts() const; + torrent_handle get_handle() const; + // DEBUG #ifndef NDEBUG logger* spawn_logger(const char* title); diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 6ca46e103..5f2459cdb 100755 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -133,7 +133,7 @@ namespace libtorrent const torrent_info& get_torrent_info() const; bool is_valid() const; - void write_resume_data(std::vector& buf); + entry write_resume_data(); // TODO: add force reannounce diff --git a/src/entry.cpp b/src/entry.cpp index da37e6c77..631f0d54a 100755 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -49,127 +49,183 @@ namespace } } -void libtorrent::entry::construct(data_type t) +namespace libtorrent { - m_type = t; - switch(m_type) - { - case int_t: - new(data) integer_type; - break; - case string_t: - new(data) string_type; - break; - case list_t: - new(data) list_type; - break; - case dictionary_t: - new (data) dictionary_type; - break; - default: - m_type = undefined_t; - } -} -void libtorrent::entry::copy(const entry& e) -{ - m_type = e.m_type; - switch(m_type) + entry::entry(const dictionary_type& v) { - case int_t: - new(data) integer_type(e.integer()); - break; - case string_t: - new(data) string_type(e.string()); - break; - case list_t: - new(data) list_type(e.list()); - break; - case dictionary_t: - new (data) dictionary_type(e.dict()); - break; - default: - m_type = undefined_t; + new(data) dictionary_type(v); + m_type = dictionary_t; } -} -void libtorrent::entry::destruct() -{ - switch(m_type) + entry::entry(const string_type& v) { - case int_t: - call_destructor(reinterpret_cast(data)); - break; - case string_t: - call_destructor(reinterpret_cast(data)); - break; - case list_t: - call_destructor(reinterpret_cast(data)); - break; - case dictionary_t: - call_destructor(reinterpret_cast(data)); - break; - default: - break; + new(data) string_type(v); + m_type = string_t; } -} -void libtorrent::entry::print(std::ostream& os, int indent) const -{ - for (int i = 0; i < indent; ++i) os << " "; - switch (m_type) + entry::entry(const list_type& v) { - case int_t: - os << integer() << "\n"; - break; - case string_t: + new(data) list_type(v); + m_type = list_t; + } + + entry::entry(const integer_type& v) + { + new(data) integer_type(v); + m_type = int_t; + } + + void entry::operator=(const dictionary_type& v) + { + destruct(); + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + void entry::operator=(const string_type& v) + { + destruct(); + new(data) string_type(v); + m_type = string_t; + } + + void entry::operator=(const list_type& v) + { + destruct(); + new(data) list_type(v); + m_type = list_t; + } + + void entry::operator=(const integer_type& v) + { + destruct(); + new(data) integer_type(v); + m_type = int_t; + } + + + void entry::construct(data_type t) + { + m_type = t; + switch(m_type) { - bool binary_string = false; - for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + case int_t: + new(data) integer_type; + break; + case string_t: + new(data) string_type; + break; + case list_t: + new(data) list_type; + break; + case dictionary_t: + new (data) dictionary_type; + break; + default: + m_type = undefined_t; + } + } + + void entry::copy(const entry& e) + { + m_type = e.m_type; + switch(m_type) + { + case int_t: + new(data) integer_type(e.integer()); + break; + case string_t: + new(data) string_type(e.string()); + break; + case list_t: + new(data) list_type(e.list()); + break; + case dictionary_t: + new (data) dictionary_type(e.dict()); + break; + default: + m_type = undefined_t; + } + } + + void entry::destruct() + { + switch(m_type) + { + case int_t: + call_destructor(reinterpret_cast(data)); + break; + case string_t: + call_destructor(reinterpret_cast(data)); + break; + case list_t: + call_destructor(reinterpret_cast(data)); + break; + case dictionary_t: + call_destructor(reinterpret_cast(data)); + break; + default: + break; + } + } + + void entry::print(std::ostream& os, int indent) const + { + for (int i = 0; i < indent; ++i) os << " "; + switch (m_type) + { + case int_t: + os << integer() << "\n"; + break; + case string_t: { - if (!std::isprint(static_cast(*i))) - { - binary_string = true; - break; - } - } - if (binary_string) - { - os.unsetf(std::ios_base::dec); - os.setf(std::ios_base::hex); + bool binary_string = false; for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) - os << static_cast((unsigned char)*i); - os.unsetf(std::ios_base::hex); - os.setf(std::ios_base::dec); - os << "\n"; - } - else + { + if (!std::isprint(static_cast(*i))) + { + binary_string = true; + break; + } + } + if (binary_string) + { + os.unsetf(std::ios_base::dec); + os.setf(std::ios_base::hex); + for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + os << static_cast((unsigned char)*i); + os.unsetf(std::ios_base::hex); + os.setf(std::ios_base::dec); + os << "\n"; + } + else + { + os << string() << "\n"; + } + } break; + case list_t: { - os << string() << "\n"; - } - } break; - case list_t: - { - os << "list\n"; - for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) + os << "list\n"; + for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) + { + i->print(os, indent+1); + } + } break; + case dictionary_t: { - i->print(os, indent+1); - } - } break; - case dictionary_t: - { - os << "dictionary\n"; - for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) - { - for (int j = 0; j < indent+1; ++j) os << " "; - os << "[" << i->first << "]"; - if (i->second.type() != entry::string_t && i->second.type() != entry::int_t) os << "\n"; - else os << " "; - i->second.print(os, indent+2); - } - } break; - default: - os << "\n"; + os << "dictionary\n"; + for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) + { + for (int j = 0; j < indent+1; ++j) os << " "; + os << "[" << i->first << "]"; + if (i->second.type() != entry::string_t && i->second.type() != entry::int_t) os << "\n"; + else os << " "; + i->second.print(os, indent+2); + } + } break; + default: + os << "\n"; + } } } - diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 3e6d6cab8..862f95316 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -52,7 +52,7 @@ namespace libtorrent // the names of the extensions to look for in // the extensions-message const char* peer_connection::extension_names[] = - { "gzip" }; + { "chat" }; const peer_connection::message_handler peer_connection::m_message_handler[] = { @@ -728,7 +728,57 @@ namespace libtorrent void peer_connection::on_extended(int received) { - + m_statistics.received_bytes(0, received); + if (m_packet_size < 5) + throw protocol_error("'extended' message smaller than 5 bytes"); + + if (m_torrent == 0) + throw protocol_error("'extended' message sent before proper handshake"); + + + if (m_recv_pos < 5) return; + + const char* ptr = &m_recv_buffer[1]; + + int extended_id = detail::read_int(ptr); + + switch (extended_id) + { + case extended_chat_message: + { + if (m_packet_size > 2 * 1024) + if (m_recv_pos < m_packet_size) return; + throw protocol_error("CHAT message larger than 2 kB"); + try + { + entry d = bdecode(m_recv_buffer.begin()+5, m_recv_buffer.end()); + entry::dictionary_type::const_iterator i = d.dict().find("msg"); + if (i == d.dict().end()) + throw protocol_error("CHAT message did not contain any 'msg'"); + + const std::string& str = i->second.string(); + + if (m_torrent->alerts().should_post(alert::critical)) + { + m_torrent->alerts() + .post_alert(chat_message_alert(m_torrent->get_handle(), m_peer_id, str)); + } + + } + catch (invalid_encoding& e) + { + throw protocol_error("invalid bencoding in CHAT message"); + } + catch (type_error& e) + { + throw protocol_error("invalid types in bencoded CHAT message"); + } + return; + } + default: + throw protocol_error("unknown extended message id"); + + }; } @@ -746,6 +796,7 @@ namespace libtorrent { assert(m_recv_pos >= received); assert(m_recv_pos > 0); + assert(m_torrent); int packet_type = m_recv_buffer[0]; if (packet_type < 0 @@ -853,6 +904,25 @@ namespace libtorrent send_buffer_updated(); } + void peer_connection::send_chat_message(const std::string& msg) + { + assert(msg.length() <= 1 * 1024); + if (m_extension_messages[extended_chat_message] == 0) return; + + entry e(entry::dictionary_t); + e.dict()["msg"] = msg; + + std::vector message; + bencode(std::back_inserter(message), e); + std::back_insert_iterator > ptr(m_send_buffer); + + detail::write_uint(1 + 4 + message.size(), ptr); + detail::write_uchar(msg_extended, ptr); + detail::write_int(m_extension_messages[extended_chat_message], ptr); + std::copy(message.begin(), message.end(), ptr); + send_buffer_updated(); + } + void peer_connection::send_bitfield() { #ifndef NDEBUG @@ -884,9 +954,7 @@ namespace libtorrent for (int i = 0; i < num_supported_extensions; ++i) { - entry msg_index(entry::int_t); - msg_index.integer() = i; - extension_list.dict()[extension_names[i]] = msg_index; + extension_list.dict()[extension_names[i]] = i; } // make room for message size diff --git a/src/session.cpp b/src/session.cpp index b45287147..d44639ab5 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -53,6 +53,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/entry.hpp" #include "libtorrent/session.hpp" #include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" #if defined(_MSC_VER) && _MSC_VER < 1300 namespace std @@ -751,10 +752,10 @@ namespace libtorrent // TODO: add a check to see if filenames are accepted on the // current platform. // if the torrent already exists, this will throw duplicate_torrent - torrent_handle session::add_torrent_impl( + torrent_handle session::add_torrent( const torrent_info& ti , const boost::filesystem::path& save_path - , const std::vector* resume_data) + , const entry& resume_data) { { @@ -886,6 +887,97 @@ namespace libtorrent } // TODO: store resume data as an entry instead + void detail::piece_checker_data::parse_resume_data( + const entry& resume_data + , const torrent_info& info) + { + // if we don't have any resume data, return + if (resume_data.type() == entry::undefined_t) return; + + std::vector tmp_pieces; + std::vector tmp_unfinished; + + entry rd = resume_data; + + try + { + if (rd.dict()["file-format"].string() != "libtorrent resume file") + return; + + if (rd.dict()["file-version"].integer() != 1) + return; + + // verify info_hash + const std::string &hash = rd.dict()["info-hash"].string(); + std::string real_hash(info.info_hash().begin(), info.info_hash().end()); + if (hash != real_hash) + return; + + // read piece map + const entry::list_type& slots = rd.dict()["slots"].list(); + if (slots.size() > info.num_pieces()) + return; + + tmp_pieces.reserve(slots.size()); + for (entry::list_type::const_iterator i = slots.begin(); + i != slots.end(); + ++i) + { + int index = i->integer(); + if (index >= info.num_pieces() || index < -2) + return; + tmp_pieces.push_back(index); + } + + + int num_blocks_per_piece = rd.dict()["blocks per piece"].integer(); + if (num_blocks_per_piece > 128 || num_blocks_per_piece < 1) + return; + + const entry::list_type& unfinished = rd.dict()["unfinished"].list(); + + tmp_unfinished.reserve(unfinished.size()); + for (entry::list_type::const_iterator i = unfinished.begin(); + i != unfinished.end(); + ++i) + { + piece_picker::downloading_piece p; + if (i->list().size() < 2) return; + + p.index = i->list()[0].integer(); + if (p.index < 0 || p.index >= info.num_pieces()) + return; + + const std::string& bitmask = i->list()[1].string(); + + const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1); + if (bitmask.size() != num_bitmask_bytes) return; + for (int j = 0; j < num_bitmask_bytes; ++j) + { + unsigned char bits = bitmask[j]; + for (int k = 0; k < 8; ++k) + { + const int bit = j * 8 + k; + if (bits & (1 << k)) + p.finished_blocks[bit] = true; + } + } + tmp_unfinished.push_back(p); + } + + piece_map.swap(tmp_pieces); + unfinished_pieces.swap(tmp_unfinished); + } + catch (invalid_encoding) + { + return; + } + catch (type_error) + { + return; + } + } +/* void detail::piece_checker_data::parse_resume_data( const std::vector* rd , const torrent_info& info) @@ -958,4 +1050,5 @@ namespace libtorrent piece_map.swap(tmp_pieces); unfinished_pieces.swap(tmp_unfinished); } +*/ } diff --git a/src/torrent.cpp b/src/torrent.cpp index d0db58dc0..4e105648f 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -303,8 +303,7 @@ namespace libtorrent { std::stringstream s; s << "hash for piece " << index << " failed"; - torrent_handle self(&m_ses, 0, m_torrent_file.info_hash()); - m_ses.m_alerts.post_alert(hash_failed_alert(self, index, s.str())); + m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str())); } std::vector downloaders; m_picker.get_downloaders(downloaders, index); @@ -577,6 +576,18 @@ namespace libtorrent #endif } + alert_manager& torrent::alerts() const + { + return m_ses.m_alerts; + } + + torrent_handle torrent::get_handle() const + { + return torrent_handle(&m_ses, 0, m_torrent_file.info_hash()); + } + + + #ifndef NDEBUG void torrent::check_invariant() { @@ -699,8 +710,7 @@ namespace libtorrent s << "tracker: \"" << m_torrent_file.trackers()[m_currently_trying_tracker].url << "\" timed out"; - torrent_handle self(&m_ses, 0, m_torrent_file.info_hash()); - m_ses.m_alerts.post_alert(tracker_alert(self, s.str())); + m_ses.m_alerts.post_alert(tracker_alert(get_handle(), s.str())); } // TODO: increase the retry_delay for // each failed attempt on the same tracker! @@ -726,8 +736,7 @@ namespace libtorrent s << "tracker: \"" << m_torrent_file.trackers()[m_currently_trying_tracker].url << "\" " << str; - torrent_handle self(&m_ses, 0, m_torrent_file.info_hash()); - m_ses.m_alerts.post_alert(tracker_alert(self, s.str())); + m_ses.m_alerts.post_alert(tracker_alert(get_handle(), s.str())); } diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index f74090c07..0a21a4966 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -170,43 +170,30 @@ namespace libtorrent return false; } - void torrent_handle::write_resume_data(std::vector& buf) + entry torrent_handle::write_resume_data() { - buf.clear(); std::vector piece_index; - if (m_ses == 0) return; + 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); - if (t == 0) return; + if (t == 0) + throw invalid_handle(); t->filesystem().export_piece_map(piece_index); - std::back_insert_iterator > out(buf); + entry ret(entry::dictionary_t); - // TODO: write file header - // TODO: write modification-dates for all files + ret.dict()["file-format"] = "libtorrent resume file"; + ret.dict()["file-version"] = 1; - for (sha1_hash::const_iterator i = m_info_hash.begin(); - i != m_info_hash.end(); - ++i) - { - detail::write_uchar(*i, out); - } + const sha1_hash& info_hash = t->torrent_file().info_hash(); + ret.dict()["info-hash"] = std::string(info_hash.begin(), info_hash.end()); - // number of slots - int num_slots = piece_index.size(); - detail::write_int(num_slots, out); - - // the piece indices for each slot (-1 means no index assigned) - for (std::vector::iterator i = piece_index.begin(); - i != piece_index.end(); - ++i) - { - detail::write_int(*i, out); - assert(*i >= -2); - assert(*i < t->torrent_file().num_pieces()); - } + ret.dict()["slots"] = entry(entry::list_t); + entry::list_type& slots = ret.dict()["slots"].list(); + std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots)); const piece_picker& p = t->picker(); @@ -216,11 +203,12 @@ namespace libtorrent // blocks per piece int num_blocks_per_piece = t->torrent_file().piece_length() / t->block_size(); - detail::write_int(num_blocks_per_piece, out); + ret.dict()["blocks per piece"] = num_blocks_per_piece; // num unfinished pieces int num_unfinished = q.size(); - detail::write_int(num_unfinished, out); + ret.dict()["unfinished"] = entry(entry::list_t); + entry::list_type& up = ret.dict()["unfinished"].list(); // info for each unfinished piece for (std::vector::const_iterator i @@ -228,20 +216,28 @@ namespace libtorrent i != q.end(); ++i) { - // the unsinished piece's index - detail::write_int(i->index, out); + entry piece_struct(entry::list_t); - // TODO: write the bitmask in correct byteorder - // TODO: make sure to read it in the correct order too + // the unfinished piece's index + piece_struct.list().push_back(i->index); + + std::string bitmask; const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1); for (int j = 0; j < num_bitmask_bytes; ++j) { unsigned char v = 0; for (int k = 0; k < 8; ++k) v |= i->finished_blocks[j*8+k]?(1 << k):0; - detail::write_uchar(v, out); + bitmask.push_back(v); } + piece_struct.list().push_back(bitmask); + + // TODO: add a hash to piece_struct + + // push the struct onto the unfinished-piece list + up.push_back(piece_struct); } + return ret; }