diff --git a/NEWS b/NEWS
index 91b5d1232..cd496ab0b 100644
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,6 @@ NEWS file for Transmission
- Ban peers that send too much bad data
- Only report downloaded, verified good pieces in tracker `download' field
- Improved compliance with BitTorrent spec
- - Multiscrape support
- Significant rewrite of the libtransmission back-end
- OS X:
+ Per-torrent action menu
diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c
index b2ea14831..dc7213a0a 100644
--- a/libtransmission/torrent.c
+++ b/libtransmission/torrent.c
@@ -146,8 +146,8 @@ tr_torrentGetSpeedLimit( const tr_torrent * tor,
static void
onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
{
- tr_torrent * tor = (tr_torrent *) user_data;
- tr_tracker_event_t * event = (tr_tracker_event_t *) vevent;
+ tr_torrent * tor = user_data;
+ tr_tracker_event * event = vevent;
switch( event->messageType )
{
diff --git a/libtransmission/tracker.c b/libtransmission/tracker.c
index f7ec5400f..74cb50c05 100644
--- a/libtransmission/tracker.c
+++ b/libtransmission/tracker.c
@@ -12,13 +12,12 @@
#include
#include /* isalnum */
-#include /* INT_MAX */
#include /* snprintf */
#include
#include /* strcmp, strchr */
-#include /* for evhttp */
-#include /* for evhttp */
+#include /* libevent needs this */
+#include /* libevent needs this */
#include
#include
@@ -26,83 +25,61 @@
#include "bencode.h"
#include "completion.h"
#include "net.h"
-#include "ptrarray.h"
#include "publish.h"
#include "shared.h"
#include "tracker.h"
#include "trevent.h"
#include "utils.h"
-#define MINUTES_TO_MSEC(N) ((N) * 60 * 1000)
+enum
+{
+ /* unless the tracker says otherwise, rescrape this frequently */
+ DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 15),
-/* we follow uTorrent 1.8's lead of allowing a manual reannounce
- * every MAX( 60 seconds, min_interval ) */
-#define DEFAULT_MANUAL_ANNOUNCE_INTERVAL_SEC (60)
+ /* unless the tracker says otherwise, this is the announce interval */
+ DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 4),
-/* unless the tracker tells us otherwise, rescrape this frequently */
-#define DEFAULT_SCRAPE_INTERVAL_SEC (60*15)
+ /* unless the tracker says otherwise, this is the announce min_interval */
+ DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
-/* unless the tracker tells us otherwise, this is the announce interval */
-#define DEFAULT_ANNOUNCE_INTERVAL_SEC (240)
+ /* this is how long we'll leave a request hanging before timeout */
+ TIMEOUT_INTERVAL_SEC = 45,
-/* unless the tracker tells us otherwise, this is the announce min_interval */
-#define DEFAULT_MIN_ANNOUNCE_INTERVAL_SEC (30)
+ /* this is how long we'll leave a 'stop' request hanging before timeout.
+ we wait less time for this so it doesn't slow down shutdowns */
+ STOP_TIMEOUT_INTERVAL_SEC = 5,
-/* this is how long we'll leave a request hanging before timeout */
-#define TIMEOUT_INTERVAL_SEC 40
-#define STOPPING_TIMEOUT_INTERVAL_SEC 8
-
-/* the value of the 'numwant' argument passed in tracker requests.
- * this should be big, but if it's *too* big trackers will ignore it */
-#define NUMWANT 256
-
-/* the length of the 'key' argument passed in tracker requests */
-#define TR_KEY_LEN 10
+ /* the value of the 'numwant' argument passed in tracker requests. */
+ NUMWANT = 200,
+ /* the length of the 'key' argument passed in tracker requests */
+ KEYLEN = 10
+};
/**
***
**/
-typedef struct
+struct tr_tracker
{
tr_handle * handle;
- tr_ptrArray * torrents;
- tr_ptrArray * scraping;
- tr_ptrArray * scrapeQueue;
-
/* these are set from the latest scrape or tracker response */
int announceIntervalSec;
- int minAnnounceIntervalSec;
+ int announceMinIntervalSec;
int scrapeIntervalSec;
- /* calculated when we get fewer scrapes
- back than we asked for */
- int multiscrapeMax;
-
tr_tracker_info * redirect;
tr_tracker_info * addresses;
int addressIndex;
int addressCount;
int * tierFronts;
- char * primaryAddress;
-
/* sent as the "key" argument in tracker requests
to verify us if our IP address changes.
This is immutable for the life of the tracker object. */
- char key_param[TR_KEY_LEN+1];
+ char key_param[KEYLEN+1];
- tr_timer * scrapeTimer;
-}
-Tracker;
-
-/* this is the Torrent struct, but since it's the pointer
- passed around in the public API of this tracker module,
- its *public* name is tr_tracker... wheee */
-typedef struct tr_tracker
-{
tr_publisher_t * publisher;
/* torrent hash string */
@@ -117,11 +94,10 @@ typedef struct tr_tracker
was missed to ensure that we didn't think someone was cheating. */
char peer_id[TR_ID_LEN + 1];
- /* these are set from the latest scrape or tracker response...
- -1 means 'unknown' */
+ /* these are set from the latest tracker response... -1 is 'unknown' */
int timesDownloaded;
- int seeders;
- int leechers;
+ int seederCount;
+ int leecherCount;
char * trackerID;
/* the last tracker request we sent. (started, stopped, etc.)
@@ -131,33 +107,14 @@ typedef struct tr_tracker
time_t manualAnnounceAllowedAt;
- Tracker * tracker;
-
tr_timer * scrapeTimer;
tr_timer * reannounceTimer;
unsigned int isRunning : 1;
-}
-Torrent;
-
-static int
-trackerCompare( const void * va, const void * vb )
-{
- const Tracker * a = ( const Tracker * ) va;
- const Tracker * b = ( const Tracker * ) vb;
- return strcmp( a->primaryAddress, b->primaryAddress );
-}
-
-static int
-torrentCompare( const void * va, const void * vb )
-{
- const Torrent * a = (const Torrent*) va;
- const Torrent * b = (const Torrent*) vb;
- return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH );
-}
+};
/***
-****
+**** Connections that know how to clean up after themselves
***/
static int
@@ -170,14 +127,17 @@ freeConnection( void * evcon )
static void
connectionClosedCB( struct evhttp_connection * evcon, void * handle )
{
+ /* libevent references evcon right after calling this function,
+ so we can't free it yet... defer it to after this call chain
+ has played out */
tr_timerNew( handle, freeConnection, evcon, 100 );
}
static struct evhttp_connection*
-getConnection( Tracker * tracker, const char * address, int port )
+getConnection( tr_tracker * t, const char * address, int port )
{
struct evhttp_connection * c = evhttp_connection_new( address, port );
- evhttp_connection_set_closecb( c, connectionClosedCB, tracker->handle );
+ evhttp_connection_set_closecb( c, connectionClosedCB, t->handle );
return c;
}
@@ -185,159 +145,63 @@ getConnection( Tracker * tracker, const char * address, int port )
**** PUBLISH
***/
-static const tr_tracker_event_t emptyEvent = { 0, NULL, NULL, NULL, 0 };
+static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0 };
static void
-publishMessage( Torrent * tor, const char * msg, int type )
+publishMessage( tr_tracker * t, const char * msg, int type )
{
- tr_tracker_event_t event = emptyEvent;
- event.hash = tor->hash;
+ tr_tracker_event event = emptyEvent;
+ event.hash = t->hash;
event.messageType = type;
event.text = msg;
- tr_publisherPublish( tor->publisher, tor, &event );
+ tr_publisherPublish( t->publisher, t, &event );
}
static void
-publishErrorClear( Torrent * tor )
+publishErrorClear( tr_tracker * t )
{
- publishMessage( tor, NULL, TR_TRACKER_ERROR_CLEAR );
+ publishMessage( t, NULL, TR_TRACKER_ERROR_CLEAR );
}
static void
-publishErrorMessage( Torrent * tor, const char * msg )
+publishErrorMessage( tr_tracker * t, const char * msg )
{
- publishMessage( tor, msg, TR_TRACKER_ERROR );
+ publishMessage( t, msg, TR_TRACKER_ERROR );
}
static void
-publishWarningMessage( Torrent * tor, const char * msg )
+publishWarning( tr_tracker * t, const char * msg )
{
- publishMessage( tor, msg, TR_TRACKER_WARNING );
+ publishMessage( t, msg, TR_TRACKER_WARNING );
}
static void
-publishNewPeers( Torrent * tor, int count, uint8_t * peers )
+publishNewPeers( tr_tracker * t, int count, uint8_t * peers )
{
- tr_tracker_event_t event = emptyEvent;
- event.hash = tor->hash;
+ tr_tracker_event event = emptyEvent;
+ event.hash = t->hash;
event.messageType = TR_TRACKER_PEERS;
event.peerCount = count;
event.peerCompact = peers;
- tr_inf( "Torrent \"%s\" got %d new peers", tor->name, count );
- tr_publisherPublish( tor->publisher, tor, &event );
+ tr_inf( "Torrent \"%s\" got %d new peers", t->name, count );
+ tr_publisherPublish( t->publisher, t, &event );
}
/***
**** LIFE CYCLE
***/
-static tr_ptrArray *
-getTrackerLookupTable( void )
-{
- static tr_ptrArray * myTrackers = NULL;
- if( !myTrackers )
- myTrackers = tr_ptrArrayNew( );
- return myTrackers;
-}
-
static void
generateKeyParam( char * msg, int len )
{
int i;
const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
+ const int poolSize = strlen( pool );
for( i=0; iscraping ) )
- return;
-
- if( !t->scrapeTimer )
- t->scrapeTimer = tr_timerNew( t->handle, onTrackerScrapeNow, t, 5000 );
-}
-
-static Tracker*
-getExistingTracker( const char * primaryAddress )
-{
- tr_ptrArray * trackers = getTrackerLookupTable( );
- Tracker tmp;
- assert( primaryAddress && *primaryAddress );
- tmp.primaryAddress = (char*) primaryAddress;
- return tr_ptrArrayFindSorted( trackers, &tmp, trackerCompare );
-}
-
-static Tracker*
-tr_trackerGet( const tr_torrent * tor )
-{
- const tr_info * info = &tor->info;
- Tracker * t = getExistingTracker( info->primaryAddress );
-
- if( t == NULL ) /* no such tracker.... create one */
- {
- int i, j, sum, *iwalk;
- tr_tracker_info * nwalk;
- tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
-
- t = tr_new0( Tracker, 1 );
- t->handle = tor->handle;
- t->primaryAddress = tr_strdup( info->primaryAddress );
- t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
- t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
- t->minAnnounceIntervalSec = DEFAULT_MIN_ANNOUNCE_INTERVAL_SEC;
- t->multiscrapeMax = INT_MAX;
- t->torrents = tr_ptrArrayNew( );
- t->scraping = tr_ptrArrayNew( );
- t->scrapeQueue = tr_ptrArrayNew( );
- generateKeyParam( t->key_param, TR_KEY_LEN );
-
- for( sum=i=0; itrackerTiers; ++i )
- sum += info->trackerList[i].count;
- t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
- t->addressIndex = 0;
- t->addressCount = sum;
- t->tierFronts = iwalk = tr_new0( int, sum );
-
- for( i=0; itrackerTiers; ++i )
- {
- const int tierFront = nwalk - t->addresses;
-
- for( j=0; jtrackerList[i].count; ++j )
- {
- const tr_tracker_info * src = &info->trackerList[i].list[j];
- nwalk->address = tr_strdup( src->address );
- nwalk->port = src->port;
- nwalk->announce = tr_strdup( src->announce );
- nwalk->scrape = tr_strdup( src->scrape );
- ++nwalk;
-
- *iwalk++ = tierFront;
- }
- }
-
- assert( nwalk - t->addresses == sum );
- assert( iwalk - t->tierFronts == sum );
-
- tr_ptrArrayInsertSorted( getTrackerLookupTable( ), t, trackerCompare );
- }
-
- return t;
-}
-
-static Torrent *
-getExistingTorrent( Tracker * t, const uint8_t hash[SHA_DIGEST_LENGTH] )
-{
- Torrent tmp;
- memcpy( tmp.hash, hash, SHA_DIGEST_LENGTH );
- return tr_ptrArrayFindSorted( t->torrents, &tmp, torrentCompare );
-}
-
static void
escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
{
@@ -350,79 +214,98 @@ escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
*out = '\0';
}
-static void
-onTorrentFreeNow( void * vtor )
+static void scrapeNow( tr_tracker * );
+
+tr_tracker *
+tr_trackerNew( const tr_torrent * torrent )
{
- Torrent * tor = (Torrent *) vtor;
- Tracker * t = tor->tracker;
+ const tr_info * info = &torrent->info;
+ int i, j, sum, *iwalk;
+ tr_tracker_info * nwalk;
+ tr_tracker * t;
- tr_ptrArrayRemoveSorted( t->torrents, tor, torrentCompare );
- tr_ptrArrayRemoveSorted( t->scrapeQueue, tor, torrentCompare );
- tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
+ tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
- tr_timerFree( &tor->scrapeTimer );
- tr_timerFree( &tor->reannounceTimer );
- tr_publisherFree( &tor->publisher );
- tr_free( tor->name );
- tr_free( tor->trackerID );
- tr_free( tor->lastRequest );
- tr_free( tor );
+ t = tr_new0( tr_tracker, 1 );
+ t->handle = torrent->handle;
+ t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
+ t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
+ t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
+ generateKeyParam( t->key_param, KEYLEN );
- if( tr_ptrArrayEmpty( t->torrents ) ) /* last one.. free the tracker too */
+ t->publisher = tr_publisherNew( );
+ t->timesDownloaded = -1;
+ t->seederCount = -1;
+ t->leecherCount = -1;
+ t->manualAnnounceAllowedAt = ~(time_t)0;
+ t->name = tr_strdup( info->name );
+ memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
+ escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
+
+ for( sum=i=0; itrackerTiers; ++i )
+ sum += info->trackerList[i].count;
+ t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
+ t->addressIndex = 0;
+ t->addressCount = sum;
+ t->tierFronts = iwalk = tr_new0( int, sum );
+
+ for( i=0; itrackerTiers; ++i )
{
- int i;
- tr_ptrArrayRemoveSorted( getTrackerLookupTable( ), t, trackerCompare );
+ const int tierFront = nwalk - t->addresses;
- tr_ptrArrayFree( t->torrents, NULL );
- tr_ptrArrayFree( t->scrapeQueue, NULL );
- tr_ptrArrayFree( t->scraping, NULL );
+ for( j=0; jtrackerList[i].count; ++j )
+ {
+ const tr_tracker_info * src = &info->trackerList[i].list[j];
+ nwalk->address = tr_strdup( src->address );
+ nwalk->port = src->port;
+ nwalk->announce = tr_strdup( src->announce );
+ nwalk->scrape = tr_strdup( src->scrape );
+ ++nwalk;
- for( i=0; iaddressCount; ++i )
- tr_trackerInfoClear( &t->addresses[i] );
-
- if( t->redirect ) {
- tr_trackerInfoClear( t->redirect );
- tr_free( t->redirect );
+ *iwalk++ = tierFront;
}
-
- tr_timerFree( &t->scrapeTimer );
-
- tr_free( t->primaryAddress );
- tr_free( t->addresses );
- tr_free( t->tierFronts );
- tr_free( t );
}
+
+ assert( nwalk - t->addresses == sum );
+ assert( iwalk - t->tierFronts == sum );
+
+ scrapeNow( t );
+fprintf( stderr, "sizeof(tr_tracker) is %d\n", (int)sizeof(tr_tracker) );
+ return t;
+}
+
+static void
+onTrackerFreeNow( void * vt )
+{
+ int i;
+ tr_tracker * t = vt;
+
+ tr_timerFree( &t->scrapeTimer );
+ tr_timerFree( &t->reannounceTimer );
+ tr_publisherFree( &t->publisher );
+ tr_free( t->name );
+ tr_free( t->trackerID );
+ tr_free( t->lastRequest );
+
+ for( i=0; iaddressCount; ++i )
+ tr_trackerInfoClear( &t->addresses[i] );
+
+ if( t->redirect ) {
+ tr_trackerInfoClear( t->redirect );
+ tr_free( t->redirect );
+ }
+
+ tr_timerFree( &t->scrapeTimer );
+
+ tr_free( t->addresses );
+ tr_free( t->tierFronts );
+ tr_free( t );
}
void
-tr_trackerFree( Torrent * tor )
+tr_trackerFree( tr_tracker * t )
{
- tr_runInEventThread( tor->tracker->handle, onTorrentFreeNow, tor );
-}
-
-Torrent*
-tr_trackerNew( tr_torrent * torrent )
-{
- Torrent * tor;
- Tracker * t = tr_trackerGet( torrent );
- assert( getExistingTorrent( t, torrent->info.hash ) == NULL );
-
- /* create a new Torrent and queue it for scraping */
- tor = tr_new0( Torrent, 1 );
- tor->publisher = tr_publisherNew( );
- tor->tracker = t;
- /*tor->torrent = torrent;*/
- tor->timesDownloaded = -1;
- tor->seeders = -1;
- tor->leechers = -1;
- tor->manualAnnounceAllowedAt = ~0;
- tor->name = tr_strdup( torrent->info.name );
- memcpy( tor->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
- escape( tor->escaped, torrent->info.hash, SHA_DIGEST_LENGTH );
- tr_ptrArrayInsertSorted( t->torrents, tor, torrentCompare );
- tr_ptrArrayInsertSorted( t->scrapeQueue, tor, torrentCompare );
- tr_trackerScrapeSoon( t );
- return tor;
+ tr_runInEventThread( t->handle, onTrackerFreeNow, t );
}
/***
@@ -440,18 +323,19 @@ parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
for( i=0; ret && iaddresses != NULL );
assert( t->addressIndex >= 0 );
@@ -516,7 +400,7 @@ getCurrentAddress( const Tracker * t )
return &t->addresses[t->addressIndex];
}
static int
-trackerSupportsScrape( const Tracker * t )
+trackerSupportsScrape( const tr_tracker * t )
{
const tr_tracker_info * info = getCurrentAddress( t );
@@ -527,11 +411,11 @@ trackerSupportsScrape( const Tracker * t )
static void
-addCommonHeaders( const Tracker * t,
+addCommonHeaders( const tr_tracker * t,
struct evhttp_request * req )
{
char buf[1024];
- tr_tracker_info * address = getCurrentAddress( t );
+ const tr_tracker_info * address = getCurrentAddress( t );
snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
evhttp_add_header( req->output_headers, "Host", buf );
evhttp_add_header( req->output_headers, "Connection", "close" );
@@ -551,18 +435,19 @@ struct torrent_hash
};
static struct torrent_hash*
-torrentHashNew( Torrent * tor )
+torrentHashNew( tr_tracker * t )
{
struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
- data->handle = tor->tracker->handle;
- memcpy( data->hash, tor->hash, SHA_DIGEST_LENGTH );
+ data->handle = t->handle;
+ memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
return data;
}
-tr_torrent*
-findTorrentFromHash( struct torrent_hash * data )
+tr_tracker *
+findTrackerFromHash( struct torrent_hash * data )
{
- return tr_torrentFindFromHash( data->handle, data->hash );
+ tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
+ return torrent ? torrent->tracker : NULL;
}
/***
@@ -572,45 +457,28 @@ findTorrentFromHash( struct torrent_hash * data )
***/
static int
-onTorrentScrapeNow( void * vhash )
-{
- tr_torrent * torrent = findTorrentFromHash( vhash );
- tr_free( vhash );
-
- if( torrent != NULL )
- {
- Torrent * tor = torrent->tracker;
- if( trackerSupportsScrape( tor->tracker ) )
- {
- if( tr_ptrArrayFindSorted( tor->tracker->scrapeQueue, tor, torrentCompare) == NULL )
- tr_ptrArrayInsertSorted( tor->tracker->scrapeQueue, tor, torrentCompare );
- tr_trackerScrapeSoon( tor->tracker );
- }
- tor->scrapeTimer = NULL;
- }
- return FALSE;
-}
+onScrapeNow( void * vt );
static void
-onScrapeResponse( struct evhttp_request * req, void * primaryAddress )
+onScrapeResponse( struct evhttp_request * req, void * vhash )
{
- char * errmsg;
- Tracker * t;
+ const char * warning;
+ time_t nextScrapeSec = 60;
+ tr_tracker * t;
- tr_inf( "Got scrape response from '%s': %s",
- primaryAddress,
- ( ( req && req->response_code_line ) ? req->response_code_line : "(null)") );
-
- t = getExistingTracker( primaryAddress );
- tr_free( primaryAddress );
- if( t == NULL ) /* tracker has been closed... */
+ t = findTrackerFromHash( vhash );
+ tr_free( vhash );
+ if( t == NULL ) /* tracker's been closed... */
return;
+ tr_inf( "Got scrape response for '%s': %s",
+ t->name,
+ ( ( req && req->response_code_line ) ? req->response_code_line
+ : "(null)") );
+
if( req && ( req->response_code == HTTP_OK ) )
{
- int numResponses = 0;
benc_val_t benc, *files;
- const int n_scraping = tr_ptrArraySize( t->scraping );
const int bencLoaded = !parseBencResponse( req, &benc );
if( bencLoaded
@@ -621,140 +489,74 @@ onScrapeResponse( struct evhttp_request * req, void * primaryAddress )
for( i=0; ival.l.count; i+=2 )
{
const uint8_t* hash =
- (const uint8_t*) files->val.l.vals[i].val.s.s;
+ (const uint8_t*) files->val.l.vals[i].val.s.s;
benc_val_t *tmp, *flags;
benc_val_t *tordict = &files->val.l.vals[i+1];
- Torrent * tor = getExistingTorrent( t, hash );
- ++numResponses;
-
- if( !tor )
+ if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
continue;
- publishErrorClear( tor );
+ publishErrorClear( t );
if(( tmp = tr_bencDictFind( tordict, "complete" )))
- tor->seeders = tmp->val.i;
+ t->seederCount = tmp->val.i;
if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
- tor->leechers = tmp->val.i;
+ t->leecherCount = tmp->val.i;
if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
- tor->timesDownloaded = tmp->val.i;
+ t->timesDownloaded = tmp->val.i;
if(( flags = tr_bencDictFind( tordict, "flags" )))
if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
t->scrapeIntervalSec = tmp->val.i;
- tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
-
- tr_timerFree( &tor->scrapeTimer );
- tor->scrapeTimer = tr_timerNew( t->handle, onTorrentScrapeNow, torrentHashNew(tor), t->scrapeIntervalSec*1000 );
tr_dbg( "Torrent '%s' scrape successful."
" Rescraping in %d seconds",
- tor->name, t->scrapeIntervalSec );
- }
+ t->name, t->scrapeIntervalSec );
- if( !files->val.l.count )
- {
- /* got an empty files dictionary! This probably means the
- torrents we're scraping have expired from the tracker,
- so make sure they're stopped. It also means any previous
- changes to multiscrapeMax are suspect, so reset that. */
-
- int n;
- Torrent ** torrents = (Torrent**)
- tr_ptrArrayPeek( t->scraping, &n );
- for( i=0; iscraping );
-
- t->multiscrapeMax = INT_MAX;
+ nextScrapeSec = t->scrapeIntervalSec;
}
}
if( bencLoaded )
tr_bencFree( &benc );
-
- /* if the tracker gave us back fewer torrents than we
- thought we should get, maybe our multiscrape string
- is too big... limit it based on how many we got back */
- if( ( 0 < numResponses ) && ( numResponses < n_scraping ) )
- t->multiscrapeMax = numResponses;
}
- if (( errmsg = updateAddresses( t, req ) ))
- tr_err( errmsg );
-
- if( !tr_ptrArrayEmpty( t->scraping ) )
- {
- int i, n;
- Torrent ** torrents =
- (Torrent**) tr_ptrArrayPeek( t->scraping, &n );
- for( i=0; iscraping );
+ if (( warning = updateAddresses( t, req ) )) {
+ tr_err( warning );
+ publishWarning( t, warning );
}
- tr_free( errmsg );
- if( !tr_ptrArrayEmpty( t->scrapeQueue ) )
- tr_trackerScrapeSoon( t );
+ tr_timerFree( &t->scrapeTimer );
+
+ t->scrapeTimer = tr_timerNew( t->handle,
+ onScrapeNow, t,
+ nextScrapeSec*1000 );
}
static int
-onTrackerScrapeNow( void * vt )
+onScrapeNow( void * vt )
{
- Tracker * t = (Tracker*) vt;
+ tr_tracker * t = vt;
const tr_tracker_info * address = getCurrentAddress( t );
- assert( tr_ptrArrayEmpty( t->scraping ) );
-
- if( trackerSupportsScrape( t ) && !tr_ptrArrayEmpty( t->scrapeQueue ) )
+ if( trackerSupportsScrape( t ) )
{
- int i, n, len, addr_len, ask_n;
- char *march, *uri;
- Torrent ** torrents =
- (Torrent**) tr_ptrArrayPeek( t->scrapeQueue, &n );
+ char * uri;
struct evhttp_connection * evcon;
struct evhttp_request *req;
+ struct evbuffer * buf = evbuffer_new( );
- ask_n = n;
- if( ask_n > t->multiscrapeMax )
- ask_n = t->multiscrapeMax;
+ evbuffer_add_printf( buf, "%s?info_hash=%s", address->scrape, t->escaped );
+ uri = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
+ evbuffer_free( buf );
- /**
- *** Build the scrape request
- **/
-
- len = addr_len = strlen( address->scrape );
- for( i=0; iescaped);
- ++len; /* for nul */
- uri = march = tr_new( char, len );
- memcpy( march, address->scrape, addr_len ); march += addr_len;
- for( i=0; iescaped );
- *march++ = i?'&':'?';
- memcpy( march, "info_hash=", 10); march += 10;
- memcpy( march, torrents[i]->escaped, elen ); march += elen;
- }
- *march++ = '\0';
- assert( march - uri == len );
-
- /* move the first n_ask torrents from scrapeQueue to scraping */
- for( i=0; iscraping, torrents[i], torrentCompare );
- tr_ptrArrayErase( t->scrapeQueue, 0, ask_n );
-
- /* ping the tracker */
tr_inf( "Sending scrape to tracker %s:%d: %s",
address->address, address->port, uri );
+
evcon = getConnection( t, address->address, address->port );
evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
- req = evhttp_request_new( onScrapeResponse, tr_strdup(t->primaryAddress) );
- assert( req );
+ req = evhttp_request_new( onScrapeResponse, torrentHashNew( t ) );
addCommonHeaders( t, req );
tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
}
@@ -763,70 +565,74 @@ onTrackerScrapeNow( void * vt )
return FALSE;
}
+static void
+scrapeNow( tr_tracker * t )
+{
+ onScrapeNow( t );
+}
+
/***
****
**** TRACKER REQUESTS
****
***/
-static int
-torrentIsRunning( const Torrent * tor )
-{
- return tor && tor->isRunning;
-}
-
static char*
-buildTrackerRequestURI( const Torrent * tor,
+buildTrackerRequestURI( const tr_tracker * t,
const tr_torrent * torrent,
const char * eventName )
{
const int isStopping = !strcmp( eventName, "stopped" );
const int numwant = isStopping ? 0 : NUMWANT;
- char buf[4096];
+ struct evbuffer * buf = evbuffer_new( );
+ char * ret;
- snprintf( buf, sizeof(buf), "%s"
- "?info_hash=%s"
- "&peer_id=%s"
- "&port=%d"
- "&uploaded=%"PRIu64
- "&downloaded=%"PRIu64
- "&corrupt=%"PRIu64
- "&left=%"PRIu64
- "&compact=1"
- "&numwant=%d"
- "&key=%s"
- "%s%s"
- "%s%s",
- getCurrentAddress(tor->tracker)->announce,
- tor->escaped,
- tor->peer_id,
- tr_sharedGetPublicPort( torrent->handle->shared ),
+ evbuffer_add_printf( buf, "%s"
+ "?info_hash=%s"
+ "&peer_id=%s"
+ "&port=%d"
+ "&uploaded=%"PRIu64
+ "&downloaded=%"PRIu64
+ "&corrupt=%"PRIu64
+ "&left=%"PRIu64
+ "&compact=1"
+ "&numwant=%d"
+ "&key=%s"
+ "%s%s"
+ "%s%s",
+ getCurrentAddress(t)->announce,
+ t->escaped,
+ t->peer_id,
+ tr_sharedGetPublicPort( t->handle->shared ),
torrent->uploadedCur,
torrent->downloadedCur,
torrent->corruptCur,
tr_cpLeftUntilComplete( torrent->completion ),
numwant,
- tor->tracker->key_param,
+ t->key_param,
( ( eventName && *eventName ) ? "&event=" : "" ),
( ( eventName && *eventName ) ? eventName : "" ),
- ( ( tor->trackerID && *tor->trackerID ) ? "&trackerid=" : "" ),
- ( ( tor->trackerID && *tor->trackerID ) ? tor->trackerID : "" ) );
+ ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
+ ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
- return tr_strdup( buf );
+ ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
+ evbuffer_free( buf );
+ return ret;
}
/* Convert to compact form */
static uint8_t *
-parseOldPeers( benc_val_t * bePeers, int * peerCount )
+parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
{
- int i, count;
- uint8_t * compact;
+ int i;
+ uint8_t *compact, *walk;
+ const int peerCount = bePeers->val.l.count;
assert( bePeers->type == TYPE_LIST );
- compact = tr_new( uint8_t, 6 * bePeers->val.l.count );
+ compact = tr_new( uint8_t, peerCount*6 );
- for( i=count=0; ival.l.count; ++i )
+ for( i=0, walk=compact; itype!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
continue;
- memcpy( &compact[6 * count], &addr, 4 );
+ memcpy( walk, &addr, 4 );
+ walk += 4;
val = tr_bencDictFind( peer, "port" );
if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
continue;
port = htons( val->val.i );
- memcpy( &compact[6 * count + 4], &port, 2 );
- ++count;
+ memcpy( walk, &port, 2 );
+ walk += 2;
}
- *peerCount = count;
+ *setmePeerCount = peerCount;
return compact;
}
-/* handle braindead trackers whose minimums is higher
- than the interval. */
-static void
-setAnnounceInterval( Tracker * t,
- int minimum,
- int interval )
-{
- assert( t != NULL );
-
- if( minimum > 0 )
- t->minAnnounceIntervalSec = minimum;
-
- if( interval > 0 )
- t->announceIntervalSec = interval;
-
- if( t->announceIntervalSec < t->minAnnounceIntervalSec )
- t->announceIntervalSec = t->minAnnounceIntervalSec;
-}
-
static int onReannounceNow( void * vtor );
static void
@@ -878,33 +666,19 @@ onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
{
}
-static int
-getManualReannounceIntervalSecs( const Tracker * tracker )
-{
- return tracker
- ? MAX( tracker->minAnnounceIntervalSec, DEFAULT_MANUAL_ANNOUNCE_INTERVAL_SEC )
- : DEFAULT_MANUAL_ANNOUNCE_INTERVAL_SEC;
-}
-
static void
onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
{
- char * errmsg;
- Torrent * tor;
- int isStopped;
- int reannounceIntervalSecs;
- tr_torrent * t;
+ const char * warning;
+ tr_tracker * t;
+ int err = 0;
- t = findTorrentFromHash( torrent_hash );
- tr_free( torrent_hash );
- if( t == NULL ) /* torrent has been closed */
+ t = findTrackerFromHash( torrent_hash );
+ if( t == NULL ) /* tracker has been closed */
return;
- tor = t->tracker;
- isStopped = !torrentIsRunning( tor );
-
tr_inf( "Torrent \"%s\" tracker response: %s",
- tor->name,
+ t->name,
( req ? req->response_code_line : "(null)") );
if( req && ( req->response_code == HTTP_OK ) )
@@ -912,32 +686,32 @@ onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
benc_val_t benc;
const int bencLoaded = !parseBencResponse( req, &benc );
- publishErrorClear( tor );
+ publishErrorClear( t );
if( bencLoaded && benc.type==TYPE_DICT )
{
benc_val_t * tmp;
if(( tmp = tr_bencDictFind( &benc, "failure reason" )))
- publishErrorMessage( tor, tmp->val.s.s );
+ publishErrorMessage( t, tmp->val.s.s );
if(( tmp = tr_bencDictFind( &benc, "warning message" )))
- publishWarningMessage( tor, tmp->val.s.s );
+ publishWarning( t, tmp->val.s.s );
if(( tmp = tr_bencDictFind( &benc, "interval" )))
- setAnnounceInterval( tor->tracker, -1, tmp->val.i * 1000 );
+ t->announceIntervalSec = tmp->val.i;
if(( tmp = tr_bencDictFind( &benc, "min interval" )))
- setAnnounceInterval( tor->tracker, tmp->val.i * 1000, -1 );
+ t->announceMinIntervalSec = tmp->val.i;
if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
- tor->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
+ t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
if(( tmp = tr_bencDictFind( &benc, "complete" )))
- tor->seeders = tmp->val.i;
+ t->seederCount = tmp->val.i;
if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
- tor->leechers = tmp->val.i;
+ t->leecherCount = tmp->val.i;
if(( tmp = tr_bencDictFind( &benc, "peers" )))
{
@@ -959,55 +733,56 @@ onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
}
}
- publishNewPeers( tor, peerCount, peerCompact );
+ publishNewPeers( t, peerCount, peerCompact );
tr_free( peerCompact );
}
}
- reannounceIntervalSecs = isStopped
- ? -1
- : tor->tracker->announceIntervalSec;
-
if( bencLoaded )
tr_bencFree( &benc );
}
else
{
- tr_inf( "Bad response from tracker '%s' on request '%s' "
- "for torrent '%s'... trying again in 30 seconds",
- tor->tracker->primaryAddress,
- tor->lastRequest,
- tor->name );
+ tr_inf( "Bad response for torrent '%s' on request '%s' "
+ "... trying again in 30 seconds",
+ t->name, t->lastRequest );
- reannounceIntervalSecs = 30;
+ err = 1;
}
- if (( errmsg = updateAddresses( tor->tracker, req ) )) {
- publishErrorMessage( tor, errmsg );
- tr_err( errmsg );
- tr_free( errmsg );
+ if (( warning = updateAddresses( t, req ) )) {
+ publishWarning( t, warning );
+ tr_err( warning );
}
- if( !isStopped && reannounceIntervalSecs>0 ) {
+ /* set reannounce times */
+ tr_timerFree( &t->reannounceTimer );
+ t->reannounceTimer = NULL;
+ t->manualAnnounceAllowedAt = ~(time_t)0;
+ if( t->isRunning )
+ {
tr_dbg( "torrent '%s' reannouncing in %d seconds",
- tor->name, (reannounceIntervalSecs) );
- tr_timerFree( &tor->reannounceTimer );
- tor->reannounceTimer = tr_timerNew( tor->tracker->handle, onReannounceNow, tor, reannounceIntervalSecs*1000 );
- tor->manualAnnounceAllowedAt = time(NULL) + getManualReannounceIntervalSecs( tor->tracker );
+ t->name, t->announceIntervalSec );
+
+ t->reannounceTimer = tr_timerNew( t->handle,
+ onReannounceNow, t,
+ t->announceIntervalSec * 1000 );
+
+ t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
}
}
static int
sendTrackerRequest( void * vt, const char * eventName )
{
- Torrent * t = (Torrent *) vt;
+ tr_tracker * t = vt;
const int isStopping = eventName && !strcmp( eventName, "stopped" );
- const tr_tracker_info * address = getCurrentAddress( t->tracker );
+ const tr_tracker_info * address = getCurrentAddress( t );
char * uri;
struct evhttp_connection * evcon;
const tr_torrent * tor;
- tor = tr_torrentFindFromHash( t->tracker->handle, t->hash );
+ tor = tr_torrentFindFromHash( t->handle, t->hash );
if( tor == NULL )
return FALSE;
@@ -1022,35 +797,35 @@ sendTrackerRequest( void * vt, const char * eventName )
/* kill any pending requests */
tr_timerFree( &t->reannounceTimer );
- evcon = getConnection( t->tracker, address->address, address->port );
+ evcon = getConnection( t, address->address, address->port );
if ( !evcon ) {
tr_err( "Can't make a connection to %s:%d", address->address, address->port );
tr_free( uri );
} else {
- struct evhttp_request * httpReq;
+ struct evhttp_request * req;
tr_free( t->lastRequest );
t->lastRequest = tr_strdup( eventName );
if( isStopping ) {
- evhttp_connection_set_timeout( evcon, STOPPING_TIMEOUT_INTERVAL_SEC );
- httpReq = evhttp_request_new( onStoppedResponse, t->tracker->handle );
+ evhttp_connection_set_timeout( evcon, STOP_TIMEOUT_INTERVAL_SEC );
+ req = evhttp_request_new( onStoppedResponse, t->handle );
} else {
evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
- httpReq = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
+ req = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
}
- addCommonHeaders( t->tracker, httpReq );
- tr_evhttp_make_request( t->tracker->handle, evcon,
- httpReq, EVHTTP_REQ_GET, uri );
+ addCommonHeaders( t, req );
+ tr_evhttp_make_request( t->handle, evcon,
+ req, EVHTTP_REQ_GET, uri );
}
return FALSE;
}
static int
-onReannounceNow( void * vtor )
+onReannounceNow( void * vt )
{
- Torrent * tor = (Torrent *) vtor;
- sendTrackerRequest( tor, "" );
- tor->reannounceTimer = NULL;
+ tr_tracker * t = vt;
+ sendTrackerRequest( t, "" );
+ t->reannounceTimer = NULL;
return FALSE;
}
@@ -1059,88 +834,86 @@ onReannounceNow( void * vtor )
***/
tr_publisher_tag
-tr_trackerSubscribe( Torrent * tor,
+tr_trackerSubscribe( tr_tracker * t,
tr_delivery_func func,
void * user_data )
{
- return tr_publisherSubscribe( tor->publisher, func, user_data );
+ return tr_publisherSubscribe( t->publisher, func, user_data );
}
void
-tr_trackerUnsubscribe( Torrent * tor,
+tr_trackerUnsubscribe( tr_tracker * t,
tr_publisher_tag tag )
{
- tr_publisherUnsubscribe( tor->publisher, tag );
+ tr_publisherUnsubscribe( t->publisher, tag );
}
const tr_tracker_info *
-tr_trackerGetAddress( const Torrent * tor )
+tr_trackerGetAddress( const tr_tracker * t )
{
- return getCurrentAddress( tor->tracker );
+ return getCurrentAddress( t );
}
int
-tr_trackerCanManualAnnounce ( const Torrent * tor )
+tr_trackerCanManualAnnounce ( const tr_tracker * t)
{
- /* return true if this torrent's currently running
- and it's been long enough since the last announce */
- return ( torrentIsRunning( tor ) )
- && ( time(NULL) >= tor->manualAnnounceAllowedAt );
+ return t->isRunning
+ && ( time(NULL) >= t->manualAnnounceAllowedAt );
}
void
-tr_trackerGetCounts( const Torrent * tor,
- int * setme_completedCount,
- int * setme_leecherCount,
- int * setme_seederCount )
+tr_trackerGetCounts( const tr_tracker * t,
+ int * setme_completedCount,
+ int * setme_leecherCount,
+ int * setme_seederCount )
{
if( setme_completedCount )
- *setme_completedCount = tor->timesDownloaded;
+ *setme_completedCount = t->timesDownloaded;
if( setme_leecherCount )
- *setme_leecherCount = tor->leechers;
+ *setme_leecherCount = t->leecherCount;
if( setme_seederCount )
- *setme_seederCount = tor->seeders;
+ *setme_seederCount = t->seederCount;
}
void
-tr_trackerStart( Torrent * tor )
+tr_trackerStart( tr_tracker * t )
{
- tr_peerIdNew( tor->peer_id, sizeof(tor->peer_id) );
+ tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
- if( !tor->reannounceTimer && !tor->isRunning )
+ if( !t->reannounceTimer && !t->isRunning )
{
- tor->isRunning = 1;
- sendTrackerRequest( tor, "started" );
+ t->isRunning = 1;
+ sendTrackerRequest( t, "started" );
}
}
void
-tr_trackerReannounce( Torrent * tor )
+tr_trackerReannounce( tr_tracker * t )
{
- sendTrackerRequest( tor, "started" );
+ sendTrackerRequest( t, "started" );
}
void
-tr_trackerCompleted( Torrent * tor )
+tr_trackerCompleted( tr_tracker * t )
{
- sendTrackerRequest( tor, "completed" );
+ sendTrackerRequest( t, "completed" );
}
void
-tr_trackerStop( Torrent * tor )
+tr_trackerStop( tr_tracker * t )
{
- if( tor->isRunning )
+ if( t->isRunning )
{
- tor->isRunning = 0;
- sendTrackerRequest( tor, "stopped" );
+ t->isRunning = 0;
+ sendTrackerRequest( t, "stopped" );
}
}
void
-tr_trackerChangeMyPort( Torrent * tor )
+tr_trackerChangeMyPort( tr_tracker * t )
{
- if( torrentIsRunning( tor ) )
- tr_trackerReannounce( tor );
+ if( t->isRunning )
+ tr_trackerReannounce( t );
}
diff --git a/libtransmission/tracker.h b/libtransmission/tracker.h
index 720478fd4..7e953b54e 100644
--- a/libtransmission/tracker.h
+++ b/libtransmission/tracker.h
@@ -21,9 +21,11 @@
*** Locating a tracker
**/
-struct tr_tracker * tr_trackerNew( tr_torrent * );
+typedef struct tr_tracker tr_tracker;
-void tr_trackerFree ( struct tr_tracker * );
+tr_tracker * tr_trackerNew( const tr_torrent * );
+
+void tr_trackerFree ( tr_tracker * );
/**
*** Tracker Publish / Subscribe
@@ -53,7 +55,7 @@ typedef struct
const uint8_t * peerCompact;
int peerCount;
}
-tr_tracker_event_t;
+tr_tracker_event;
tr_publisher_tag tr_trackerSubscribe ( struct tr_tracker * tag,
tr_delivery_func func,