{ package Bittorrent::TrackerLib; $VERSION = 0.04; use strict; use warnings; use Bittorrent::TrackerLib::Backend; use Bittorrent::TrackerLib::Protocol; our $errstr; # new # Usage: # my $tracker = new Bt::TrackerLib( # { name => value } ); # OPTIONAL: Configuration # Returns: # Tracker object sub new { my $class = shift; my $config = shift; my $self = {}; bless $self, $class; my $backend = new Bittorrent::TrackerLib::Backend $self; my $protocol = new Bittorrent::TrackerLib::Protocol $self; $self->{config} = $config; $self->{config}->{interval} = $config->{interval} || 3600; $self->{config}->{max_peers} = $config->{max_peers} || 50; $self->{backend} = $backend; $self->{protocol} = $protocol; return $self; } # announce # Periodic client update to get a list of peers. # Usage: # $bencoded_peerlist = $tracker->announce( # "info_hash", # REQUIRED: File info hash # { peer_id => value, # OPTIONAL: Peer data # event => value, # numwant => value, ... } ); sub announce { my $self = shift; return $self->protocol->announce(@_); } # backend # Sets or gets the current tracker backend. # Usage: # my $backend = Bittorrent::TrackerLib::Backend::*->new; # $backend = $tracker->backend($backend); sub backend { my $self = shift; my $backend = shift; $self->{backend} = $backend if $backend; return $self->{backend}; } # config # Get or set tracker configuration. # Usage: # my %config = $tracker->config; # $tracker->config( interval => 500, max_peers => 50, ... ); # my $interval = $tracker->config("interval"); # Returns: # Scalar of last passed config value if odd number of parameters # Hash of entire config list if even number of parameters sub config { my $self = shift; while (my $name = shift) { my $value = shift; $self->{config}->{$name} = $value if $value; return $self->{config}->{$name} if !$value; } return %{$self->{config}}; } # error # Get or set the last error message. Only child classes should need to # set the error message. The error message will be erased after the next # method is called. # Usage: # die $tracker->error if $tracker->error; sub error { my $self = shift; my $error = shift; $self->{error} = $error if $error; return $error; } # protocol # Gets or sets the current protocol # Usage: # $protocol = $tracker->protocol; # $tracker->protocol($protocol); sub protocol { my $self = shift; my $protocol = shift; $self->{protocol} = $protocol if $protocol; return $self->{protocol}; } # scrape # Creates a bencoded scrape responce to be sent to a client # Usage: # $tracker->scrape(@info_hashes) # OPTIONAL: List of files sub scrape { my $self = shift; return $self->protocol->scrape(@_); } 1; } =pod =head1 NAME Bittorrent::TrackerLib - Library to create a Bittorrent tracker. =head1 SYNOPSIS use Bittorrent::TrackerLib $tracker = new Bittorrent::TrackerLib \%config; $info_hash = $tracker->protocol->decode_hash($packed_info_hash) $bencoded_data = $tracker->announce($info_hash, $peer_id, \%peer_data); $bencoded_data = $tracker->scrape(@info_hashes); $tracker->config($column => $value); $value = $tracker->config($column); %config = $tracker->config; $tracker->backend->save_config(to_file => $config_file); $tracker->backend->load_config(from_file => $config_file); #** Only the most necessary functions are described above. =head1 DESCRIPTION This module is an object-oriented interface to make a BitTorrent tracker. The BitTorrent tracker keeps account of all the users that want a file and sends out lists of sources to download the file from. BitTorrent clients periodically announce themselves to the tracker, sending it a small amount of data. The tracker then sends the client a random list of peers. The scrape interface allows remote users to get some statistics from the tracker. For more information, read the method descriptions (especially C, C, and C), and look at the SEE ALSO section for links to information on the BitTorrent protocol. =head1 METHODS =head2 new =over 4 $tracker = new Bittorrent::TrackerLib; # OR $tracker = new Bittorrent::TrackerLib \%config; =back Creates a new tracker object. The first argument is an optional Bittorrent::Tracker::Backend object, see perldoc Bittorrent::Tracker::Backend for more information. The second argument is an optional configuration hashref, see C for more information. If you are specifying a backend, it MUST be FIRST. Otherwise, the default backend is Bittorrent::Tracker::Backend::None, and you can still provide the configuration hashref. If successful, C returns the object. If the creation fails (somehow), it returns false and places a string describing the error in $Bittorrent::TrackerLib::errstr. Once the object is created, any error that occurs will be placed in $tracker->error. =head2 all_peerlists =over 4 @active_files = $tracker->backend->all_peerlists; =back Returns a list of all peerlist info hashes. These can be matched with info hashes from the file summary table. =head2 announce =over 4 $bencoded_peerlist = $tracker->announce($info_hash,\%peer_data); =back An announce is basically set_peer and peerlist. The first argument to announce should be an info hash, the second argument is a hashref of peerdata. Announce returns a bencoded peerlist (the same as peerlist). Some keys in the peer data have special meaning to announce. The B key can be one of the following values: "started" means this client is joining or rejoining the swarm, "completed" means this client is done downloading and is now seeding, "stopped" means this client is leaving the swarm, and a null value (the most common) is a normal announce. The module will automatically delete the peer from the peerlist when event is "stopped," but it will still return a peerlist. The original tracker still sends the client a peerlist when event=stopped, but it's probably not necessary. The B key is how many peers the client wants. Not all clients send numwant, so it defaults to the max_peers config value. If numwant is greater than max_peers, we'll use max_peers. =head2 backend =over 4 $backend = $tracker->backend; $tracker->backend($backend); =back Gets or sets the current backend. See perldoc Bittorrent::Tracker::Backend for more information. =head2 client_error =over 4 $bencoded_error = $tracker->protocol->client_error($error_msg); =back Returns a bencoded error message that clients can understand. See the SEE ALSO area, Bittorrent Protocol section for more information. Also sets the Tracker->error message for the benefit of the tracker. This method makes it safe for programmers to say Cannounce(...)> or Cscrape(...)>. =head2 config =over 4 $value = $tracker->config("name"); # Get one config value $tracker->config(name => "value", name => "value"); # Set config %config = $tracker->config; # Get all config values =back The config method can be used to set and get config values. If config is passed an odd number of parameters, it will return the value of the last named config key. If passed an even number of parameters, it will return the entire config hash. Some necessary config keys: =over 4 =item interval The amount of time, in seconds, that a client should wait before re-announcing =item max_peers The maximum amount of peers to send to a client. =back Some optional built-in config keys: =over 4 =item die_error If 1, will die when an error occurs. Default is 0. =item warn_error If 1, will warn when an error occurs. Default is 0. =back Feel free to add your own configuration keys and values =head2 create_filelist =over 4 $tracker->backend->create_filelist =back Creates the file summary table. This function is called automatically when the tracker object is created. =head2 create_peerlist =over 4 $tracker->backend->create_peerlist($info_hash); =back Creates a database table for a peerlist named $info_hash. Uses the current prototypes specified using add_file_column and drop_file_column. Normally you shouldn't need to call this function, as the set_peer function will call it automatically if the peerlist doesn't exist. =head2 decode_hash =over 4 $tracker->protocol->decode_hash($info_hash); =back Unpacks the binary string that BitTorrent clients send into a 40-character hexidecimal string that's easier to use. Only a packed hash will be unpacked. If an unpacked hash is passed, will return the same value. ** QUESTION: Should the module call this function automatically, since it is necessary to do? or should we require the programmer to call it explicitly? ** =head2 delete_file =over 4 $tracker->backend->delete_file($info_hash); =back Removes a file from the file summary table. =head2 delete_peer =over 4 $tracker->backend->delete_peer($info_hash,$peer_id); =back Removes a peer from a peerlist. =head2 drop_filelist =over 4 $tracker->backend->drop_filelist =back Drops the file summary table. Any data in the table is deleted, so be very sure before you use this. =head2 drop_peerlist =over 4 $tracker->backend->drop_peerlist($info_hash) =back Drops a peerlist table. Any data in the table is deleted. This function is called automatically when the last peer in the list is deleted. =head2 error =over 4 die $_ if $tracker->error; =back Gets or sets the current error string (if any). Only this and child modules should need to set the error string. This error string will be destroyed at the very beginning of the next tracker method call. =head2 encode_hash =over 4 $tracker->protocol->encode_hash($info_hash) =back Packs a 40-character hexidecimal string into a 20-byte binary structure to be sent to BitTorrent clients. C, C, and C all call this function automatically, so you shouldn't need to. =head2 find_file =over 4 @files = $tracker->backend->find_file( { where => "column <= 23 && column eq 'value'", order_by => "columns [ASC|DESC]", limit => 2 } ); # OPTIONAL: SQL info =back Find a list of files from the file summary table. The first argument is a reference to a hash with the following keys: =over 4 =item where An string of the form C where C is the column name, C is an operator (below), C is the value to test, and C is an optional conjunction (below) for multiple columns. String operators: eq - Equals ne - Not equals gt - Greater than lt - Less than ge - Greater than or equal to le - Less than or equal to Numeric operators (when in doubt, use a string operator): == - Equals != - Not equals > - Greater than < - Less than >= - Greater than or equal to <= - Less than or equal to Conjunctions: && - and || - or =item order_by An string of the form C is the column name, C means to sort descending, and C means to sort ascending (which is the default action and redundant to specify). You can specify multiple columns by separating them with commas, most important column first. =item limit An string of the form C where C is the maximum number of rows to return and C is the row number to start from. =back =head2 find_peer =over 4 @files = $tracker->backend->find_peer( $info_hash, # REQUIRED: File info hash { where => "column <= 'value' && column = 'value'", order_by => "columns [ASC|DESC]", limit => 2 } ); # OPTIONAL: SQL info =back Find a list of peers from a peerlist. The first argument is a file info_hash. The second argument is a reference to a hash. See the find_file function (above) for details about this hashref. =head2 get_file =over 4 %file_data = $tracker->backend->get_file( $info_hash, # REQUIRED: File info hash \@columns ) # OPTIONAL: Columns to return =back Get a row of data from the file summary table. The first argument is the file info hash to get. The optional second argument is a reference to an array of column names to return. =head2 get_peer =over 4 %peer_data = $tracker->backend->get_peer( $info_hash, # REQUIRED: File info hash $peer_id, # REQUIRED: Peer ID \@columns ) # OPTIONAL: Columns to return =back Get a row of data from a peerlist. The first argument is the file info hash. The second argument is the peer id to get. The optional third argument is a reference to an array of columns names to return. =head2 load_config =over 4 $tracker->backend->load_config( from_file => $filename ); # Read from a file =back Loads config from a more permanent location. The first argument is either "from_file" or some other string, specifying where to load the config values from. The second argument is the path/filename or the table name to load from. The config values can then be accessed with the C method. Read the documentation for your backend for more information about this method. =head2 peerlist =over 4 $bencoded_peerlist = $tracker->protocol->peerlist($info_hash, $num_peers); =back Peerlist gathers a random list of peers for a file. The first argument is an info_hash, the second (optional) argument is the number of peers to return. If the second argument is missing, it defaults to the max_peers config value. The randomness is determined by picking ($num_peers)/10 random start points and taking 10 peers from each start point. Peerlist also deletes any lost peers. A peer is considered "lost" if they haven't announced in C seconds. =head2 protocol =over 4 $tracker->protocol($protocol); $protocol = $tracker->protocol; =back Gets or sets the current protocol. See perldoc Bittorrent::Tracker::Protocol for more information. =head2 save_config =over 4 $tracker->backend->save_config( to_file => $filename ); # Write to a file =back Saves config to a more permanent location. The first argument is either "to_file" or some other string, specifying where to save the config values to (this varies, see the documentation with your backend). The second argument is either the path/filename or some identifier to save to. =head2 scrape =over 4 $bencoded_scrape = $tracker->scrape; # Scrape all files $bencoded_scrape = $tracker->scrape(@info_hashes); # Scrape list of files =back Scrape gathers information about the files currently being tracked. The method accepts a list of info hashes as an optional argument. Scrape automatically updates two columns in the file summary table: C, the number of completed peers, and C, the number of incomplete peers. =head2 set_file =over 4 $tracker->backend->set_file($info_hash, \%file_data); =back Sets columns in the file summary table. The first argument is the info_hash of the file to be set. The second argument is a hash reference of data to set. =head2 set_peer =over 4 $tracker->backend->set_peer($info_hash, $peer_id, \%peer_data); =back Sets columns in the peerlist table. The first argument is the info_hash of the peerlist. The second argument is the peer_id of the peer to be set. The third argument is a hash reference of data to set. =head1 BUGS None known. Report any to =head1 TO DO Change TrackerLib, Protocol and Backend from a @ISA relationship to a more standalone style. (Calling the tracker from the protocol to reach the backend and vice versa.) Protocol: Add client_error and error checking. Backend: Add error checking to call Tracker->error. Tracker: Add die_error and warn_error config values. =head1 SEE ALSO The BitTorrent protocol. =head1 COPYRIGHT Copyright (c)2004, Doug Bell. This module is free software and can be modified/distributed under the same terms as Perl itself. =head1 AUTHORS Doug Bell . Please e-mail with any problems, suggestions, bugs, flames, rants, raves, inaccuracies, ideosyncrasies, idiocies, wedding proposals, or other. Doug would like to thank: Jean-Michel Hiver from the code-review-ladder for his advice Stone Willy's for their pizza =head1 CHANGES Only program-altering changes need apply. =head1 HISTORY v0.04 - 05/23/2004 - Bittorrent::Tracker is now TrackerLib, more fitting name. v0.03 - 02/17/2004 - Updated docs, added error method, added die_error, warn_error config keys, improved Tracker<->Backend<->Protocol relationship... v0.02 - 02/15/2004 - Semi-stable release v0.01 - 02/02/2004 - Released to comp.lang.perl for testing and feedback =cut