merge multitracker branch:

Adds announce list, support for code 301/302 redirect, improvements to mac inspector, better queueing with errors, random extra wait of up to a minute between announces, and other changes.
This commit is contained in:
Mitchell Livingston 2006-12-17 16:36:27 +00:00
parent f79b141ab1
commit 3374ce9a32
17 changed files with 677 additions and 175 deletions

View File

@ -1081,6 +1081,7 @@
GCC_VERSION_i386 = 4.0;
GCC_VERSION_ppc = 4.0;
GCC_WARN_ABOUT_RETURN_TYPE = NO;
GCC_WARN_SIGN_COMPARE = NO;
MACOSX_DEPLOYMENT_TARGET_i386 = 10.4;
MACOSX_DEPLOYMENT_TARGET_ppc = 10.4;
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;

View File

@ -121,6 +121,8 @@ int main( int argc, char ** argv )
{
tr_info_t * info = tr_torrentInfo( tor );
s = tr_torrentStat( tor );
/* Print torrent info (quite à la btshowmetainfo) */
printf( "hash: " );
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
@ -129,8 +131,8 @@ int main( int argc, char ** argv )
}
printf( "\n" );
printf( "tracker: %s:%d\n",
info->trackerAddress, info->trackerPort );
printf( "announce: %s\n", info->trackerAnnounce );
s->trackerAddress, s->trackerPort );
printf( "announce: %s\n", s->trackerAnnounce );
printf( "size: %"PRIu64" (%"PRIu64" * %d + %"PRIu64")\n",
info->totalSize, info->totalSize / info->pieceSize,
info->pieceSize, info->totalSize % info->pieceSize );

View File

@ -572,13 +572,13 @@ makeinfowind(GtkWindow *parent, TrTorrent *tor) {
INFOSEP(table, ii);
if(80 == in->trackerPort)
if(80 == sb->trackerPort)
INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
in->trackerAddress));
sb->trackerAddress));
else
INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
in->trackerAddress, in->trackerPort));
INFOLINE(table, ii, _("Announce:"), in->trackerAnnounce);
sb->trackerAddress, sb->trackerPort));
INFOLINE(table, ii, _("Announce:"), sb->trackerAnnounce);
INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
INFOLINEA(table, ii, _("Total Size:"), readablesize(in->totalSize));

View File

@ -36,9 +36,11 @@ int tr_httpRequestType( const char * data, int len,
/* Return the HTTP status code for the response, or -1 for parse error */
int tr_httpResponseCode( const char * data, int len );
#define TR_HTTP_STATUS_OK( st ) ( 200 <= (st) && 299 >= (st) )
#define TR_HTTP_STATUS_REDIRECT( st ) ( 300 <= (st) && 399 >= (st) )
#define TR_HTTP_STATUS_FAIL( st ) ( 400 <= (st) && 599 >= (st) )
#define TR_HTTP_STATUS_OK( st ) ( 200 <= (st) && 299 >= (st) )
#define TR_HTTP_STATUS_REDIRECT( st ) ( 300 <= (st) && 399 >= (st) )
#define TR_HTTP_STATUS_FAIL( st ) ( 400 <= (st) && 599 >= (st) )
#define TR_HTTP_STATUS_FAIL_CLIENT( st ) ( 400 <= (st) && 499 >= (st) )
#define TR_HTTP_STATUS_FAIL_SERVER( st ) ( 500 <= (st) && 599 >= (st) )
/*
Parse an HTTP request or response, locating specified headers and

View File

@ -160,8 +160,6 @@ struct tr_torrent_s
/* An escaped string used to include the hash in HTTP queries */
char escapedHashString[3*SHA_DIGEST_LENGTH+1];
char scrape[MAX_PATH_LENGTH];
/* Where to download */
char * destination;

View File

@ -29,6 +29,7 @@
/***********************************************************************
* Local prototypes
**********************************************************************/
static int getannounce( tr_info_t * inf, benc_val_t * meta );
#define strcatUTF8( dst, src) _strcatUTF8( (dst), sizeof( dst ) - 1, (src) )
static void _strcatUTF8( char *, int, char * );
@ -43,15 +44,14 @@ int tr_metainfoParse( tr_info_t * inf, const char * path,
FILE * file;
char * buf;
benc_val_t meta, * beInfo, * list, * val;
char * s, * s2, * s3;
int i;
struct stat sb;
struct stat sb;
assert( NULL == path || NULL == savedHash );
/* if savedHash isn't null, saveCopy should be false */
assert( NULL == savedHash || !saveCopy );
if ( NULL != savedHash )
if( NULL != savedHash )
{
snprintf( inf->torrent, MAX_PATH_LENGTH, "%s/%s",
tr_getTorrentsDirectory(), savedHash );
@ -147,50 +147,7 @@ int tr_metainfoParse( tr_info_t * inf, const char * path,
/* We won't need this anymore */
free( buf );
if( !( val = tr_bencDictFind( &meta, "announce" ) ) )
{
tr_err( "No \"announce\" entry" );
tr_bencFree( &meta );
return 1;
}
/* Skip spaces */
s3 = val->val.s.s;
while( *s3 && *s3 == ' ' )
{
s3++;
}
/* Parse announce URL */
if( strncmp( s3, "http://", 7 ) )
{
tr_err( "Invalid announce URL (%s)", inf->trackerAddress );
tr_bencFree( &meta );
return 1;
}
s = strchr( s3 + 7, ':' );
s2 = strchr( s3 + 7, '/' );
if( s && s < s2 )
{
memcpy( inf->trackerAddress, s3 + 7,
(long) s - (long) s3 - 7 );
inf->trackerPort = atoi( s + 1 );
}
else if( s2 )
{
memcpy( inf->trackerAddress, s3 + 7,
(long) s2 - (long) s3 - 7 );
inf->trackerPort = 80;
}
else
{
tr_err( "Invalid announce URL (%s)", inf->trackerAddress );
tr_bencFree( &meta );
return 1;
}
snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, "%s", s2 );
/* Comment info */
if( ( val = tr_bencDictFind( &meta, "comment.utf-8" ) ) || ( val = tr_bencDictFind( &meta, "comment" ) ) )
{
@ -212,7 +169,13 @@ int tr_metainfoParse( tr_info_t * inf, const char * path,
{
inf->dateCreated = 0;
}
/* Private torrent */
if( ( val = tr_bencDictFind( beInfo, "private" ) ) && TYPE_INT == val->type )
{
inf->privateTorrent = val->val.i;
}
/* Piece length */
if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
{
@ -297,7 +260,15 @@ int tr_metainfoParse( tr_info_t * inf, const char * path,
( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
{
tr_err( "Size of hashes and files don't match" );
free( inf->pieces );
tr_metainfoFree( inf );
tr_bencFree( &meta );
return 1;
}
/* get announce or announce-list */
if( getannounce( inf, &meta ) )
{
tr_metainfoFree( inf );
tr_bencFree( &meta );
return 1;
}
@ -306,6 +277,149 @@ int tr_metainfoParse( tr_info_t * inf, const char * path,
return 0;
}
void tr_metainfoFree( tr_info_t * inf )
{
int ii, jj;
free( inf->pieces );
free( inf->files );
for( ii = 0; ii < inf->trackerTiers; ii++ )
{
for( jj = 0; jj < inf->trackerList[ii].count; jj++ )
{
free( inf->trackerList[ii].list[jj].address );
free( inf->trackerList[ii].list[jj].announce );
}
free( inf->trackerList[ii].list );
}
free( inf->trackerList );
}
static int getannounce( tr_info_t * inf, benc_val_t * meta )
{
benc_val_t * val, * subval, * urlval;
char * address, * announce;
int ii, jj, port, random;
tr_tracker_info_t * sublist;
int subcount;
void * swapping;
/* Announce-list */
val = tr_bencDictFind( meta, "announce-list" );
if( NULL != val && TYPE_LIST == val->type && 0 < val->val.l.count )
{
inf->trackerTiers = 0;
inf->trackerList = calloc( sizeof( inf->trackerList[0] ),
val->val.l.count );
/* iterate through the announce-list's tiers */
for( ii = 0; ii < val->val.l.count; ii++ )
{
subval = &val->val.l.vals[ii];
if( TYPE_LIST != subval->type || 0 >= subval->val.l.count )
{
continue;
}
subcount = 0;
sublist = calloc( sizeof( sublist[0] ), subval->val.l.count );
/* iterate through the tier's items */
for( jj = 0; jj < subval->val.l.count; jj++ )
{
urlval = &subval->val.l.vals[jj];
if( TYPE_STR != urlval->type ||
tr_httpParseUrl( urlval->val.s.s, urlval->val.s.i,
&address, &port, &announce ) )
{
continue;
}
/* place the item info in a random location in the sublist */
random = tr_rand( subcount + 1 );
if( random != subcount )
{
sublist[subcount] = sublist[random];
}
sublist[random].address = address;
sublist[random].port = port;
sublist[random].announce = announce;
subcount++;
}
/* just use sublist as is if it's full */
if( subcount == subval->val.l.count )
{
inf->trackerList[inf->trackerTiers].list = sublist;
inf->trackerList[inf->trackerTiers].count = subcount;
inf->trackerTiers++;
}
/* if we skipped some of the tier's items then trim the sublist */
else if( 0 < subcount )
{
inf->trackerList[inf->trackerTiers].list = calloc( sizeof( sublist[0] ), subcount );
memcpy( inf->trackerList[inf->trackerTiers].list, sublist,
sizeof( sublist[0] ) * subcount );
inf->trackerList[inf->trackerTiers].count = subcount;
inf->trackerTiers++;
free( sublist );
}
/* drop the whole sublist if we didn't use any items at all */
else
{
free( sublist );
}
}
/* did we use any of the tiers? */
if( 0 == inf->trackerTiers )
{
tr_inf( "No valid entries in \"announce-list\"" );
free( inf->trackerList );
inf->trackerList = NULL;
}
/* trim unused sublist pointers */
else if( inf->trackerTiers < val->val.l.count )
{
swapping = inf->trackerList;
inf->trackerList = calloc( sizeof( inf->trackerList[0] ),
inf->trackerTiers );
memcpy( inf->trackerList, swapping,
sizeof( inf->trackerList[0] ) * inf->trackerTiers );
free( swapping );
}
}
/* Regular announce value */
if( 0 == inf->trackerTiers )
{
val = tr_bencDictFind( meta, "announce" );
if( NULL == val || TYPE_STR != val->type )
{
tr_err( "No \"announce\" entry" );
return 1;
}
if( tr_httpParseUrl( val->val.s.s, val->val.s.i,
&address, &port, &announce ) )
{
tr_err( "Invalid announce URL (%s)", val->val.s.s );
return 1;
}
sublist = calloc( sizeof( sublist[0] ), 1 );
sublist[0].address = address;
sublist[0].port = port;
sublist[0].announce = announce;
inf->trackerList = calloc( sizeof( inf->trackerList[0] ), 1 );
inf->trackerList[0].list = sublist;
inf->trackerList[0].count = 1;
inf->trackerTiers = 1;
}
return 0;
}
void tr_metainfoRemoveSaved( const char * hashString )
{
char file[MAX_PATH_LENGTH];

View File

@ -27,6 +27,7 @@
int tr_metainfoParse( tr_info_t *, const char * path,
const char * savedHash, int saveCopy );
void tr_metainfoFree( tr_info_t * inf );
void tr_metainfoRemoveSaved( const char * hashString );
#endif

View File

@ -24,12 +24,40 @@
#include "transmission.h"
typedef struct tr_announce_list_ptr_s tr_announce_list_ptr_t;
struct tr_announce_list_ptr_s
{
tr_tracker_info_t * item;
tr_announce_list_ptr_t * nextItem;
};
struct tr_tracker_s
{
tr_torrent_t * tor;
char * id;
char * trackerid;
const char * trackerAddress;
int trackerPort;
const char * trackerAnnounce;
char trackerScrape[MAX_PATH_LENGTH];
int trackerCanScrape;
tr_announce_list_ptr_t ** trackerAnnounceListPtr;
#define TC_CHANGE_NO 0
#define TC_CHANGE_NEXT 1
#define TC_CHANGE_NONEXT 2
#define TC_CHANGE_REDIRECT 4
int shouldChangeAnnounce;
int announceTier;
int announceTierLast;
char * redirectAddress;
int redirectAddressLen;
char * redirectScrapeAddress;
int redirectScrapeAddressLen;
char started;
char completed;
@ -42,6 +70,9 @@ struct tr_tracker_s
int leechers;
int hasManyPeers;
int complete;
int randOffset;
int completelyUnconnectable;
uint64_t dateTry;
uint64_t dateOk;
@ -61,6 +92,9 @@ struct tr_tracker_s
int newPort;
};
static int announceToScrape ( char * announce, char * scrape );
static void setAnnounce ( tr_tracker_t * tc, tr_announce_list_ptr_t * announceItem );
static void failureAnnouncing( tr_tracker_t * tc );
static tr_http_t * getQuery ( tr_tracker_t * tc );
static tr_http_t * getScrapeQuery ( tr_tracker_t * tc );
static void readAnswer ( tr_tracker_t * tc, const char *, int );
@ -69,38 +103,131 @@ static void killHttp ( tr_http_t ** http, tr_fd_t * fdlimit );
tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
{
tr_info_t * inf = &tor->info;
tr_tracker_t * tc;
tr_announce_list_ptr_t * prev, * cur;
int ii, jj;
tc = calloc( 1, sizeof( tr_tracker_t ) );
tc->tor = tor;
tc->id = tor->id;
tc->started = 1;
tc->shouldChangeAnnounce = TC_CHANGE_NO;
tc->redirectAddress = NULL;
tc->interval = 300;
tc->scrapeInterval = 600;
tc->seeders = -1;
tc->leechers = -1;
tc->complete = -1;
tc->lastAttempt = TC_ATTEMPT_NOREACH;
tc->bindPort = *(tor->bindPort);
tc->newPort = -1;
tc->trackerAnnounceListPtr = calloc( sizeof( int ), inf->trackerTiers );
for( ii = 0; ii < inf->trackerTiers; ii++ )
{
prev = NULL;
for( jj = 0; jj < inf->trackerList[ii].count; jj++ )
{
cur = calloc( sizeof( tr_announce_list_ptr_t ), 1 );
cur->item = &inf->trackerList[ii].list[jj];
if( NULL == prev )
{
tc->trackerAnnounceListPtr[ii] = cur;
}
else
{
prev->nextItem = cur;
}
prev = cur;
}
}
setAnnounce( tc, tc->trackerAnnounceListPtr[0] );
return tc;
}
static int announceToScrape( char * announce, char * scrape )
{
char * slash, * nextSlash;
int pre, post;
slash = strchr( announce, '/' );
while( ( nextSlash = strchr( slash + 1, '/' ) ) )
{
slash = nextSlash;
}
slash++;
if( !strncmp( slash, "announce", 8 ) )
{
pre = (long) slash - (long) announce;
post = strlen( announce ) - pre - 8;
memcpy( scrape, announce, pre );
sprintf( &scrape[pre], "scrape" );
memcpy( &scrape[pre+6], &announce[pre+8], post );
scrape[pre+6+post] = 0;
return 1;
}
else
{
return 0;
}
}
static void setAnnounce( tr_tracker_t * tc, tr_announce_list_ptr_t * announcePtr )
{
tr_tracker_info_t * announceItem = announcePtr->item;
tc->trackerAddress = announceItem->address;
tc->trackerPort = announceItem->port;
tc->trackerAnnounce = announceItem->announce;
tc->trackerCanScrape = announceToScrape( announceItem->announce, tc->trackerScrape );
/* Needs a new scrape */
tc->seeders = -1;
tc->leechers = -1;
tc->complete = -1;
tc->dateScrape = 0;
}
static void failureAnnouncing( tr_tracker_t * tc )
{
tr_info_t * inf = &tc->tor->info;
tc->shouldChangeAnnounce = tc->announceTier + 1 < inf->trackerTiers
|| tc->announceTierLast + 1 < inf->trackerList[tc->announceTier].count
? TC_CHANGE_NEXT : TC_CHANGE_NONEXT;
if( tc->shouldChangeAnnounce == TC_CHANGE_NONEXT )
{
tc->completelyUnconnectable = 1;
}
}
static int shouldConnect( tr_tracker_t * tc )
{
tr_torrent_t * tor = tc->tor;
uint64_t now;
/* Last tracker failed, try next */
if( tc->shouldChangeAnnounce == TC_CHANGE_NEXT
|| tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
{
return 1;
}
now = tr_date();
/* Unreachable tracker, try 10 seconds before trying again */
/* Unreachable tracker, wait 10 seconds + random value before trying again */
if( tc->lastAttempt == TC_ATTEMPT_NOREACH &&
now < tc->dateTry + 10000 )
now < tc->dateTry + tc->randOffset + 10000 )
{
return 0;
}
@ -109,7 +236,7 @@ static int shouldConnect( tr_tracker_t * tc )
don't hammer it - we'll probably get the same answer next time
anyway */
if( tc->lastAttempt == TC_ATTEMPT_ERROR &&
now < tc->dateTry + 1000 * tc->interval )
now < tc->dateTry + 1000 * tc->interval + tc->randOffset )
{
return 0;
}
@ -121,7 +248,7 @@ static int shouldConnect( tr_tracker_t * tc )
}
/* Should we try and get more peers? */
if( now > tc->dateOk + 1000 * tc->interval )
if( now > tc->dateOk + 1000 * tc->interval + tc->randOffset )
{
return 1;
}
@ -163,8 +290,8 @@ static int shouldScrape( tr_tracker_t * tc )
{
uint64_t now, interval;
/* scrape not supported */
if( !tc->tor->scrape[0] )
/* in process of changing tracker or scrape not supported */
if( tc->shouldChangeAnnounce != TC_CHANGE_NO || !tc->trackerCanScrape )
{
return 0;
}
@ -186,29 +313,106 @@ void tr_trackerChangePort( tr_tracker_t * tc, int port )
tc->newPort = port;
}
int tr_trackerPulse( tr_tracker_t * tc )
void tr_trackerPulse( tr_tracker_t * tc )
{
tr_torrent_t * tor = tc->tor;
tr_info_t * inf = &tor->info;
const char * data;
int len;
char * address, * announce;
int len, i, port;
tr_announce_list_ptr_t * announcePtr, * prevAnnouncePtr;
if( ( NULL == tc->http ) && shouldConnect( tc ) )
{
tc->completelyUnconnectable = 0;
tc->randOffset = tr_rand( 60000 );
if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
{
return 0;
return;
}
tc->dateTry = tr_date();
tc->http = getQuery( tc );
/* Use redirected address */
if( tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
{
if( !tr_httpParseUrl( tc->redirectAddress, tc->redirectAddressLen,
&address, &port, &announce ) )
{
tr_err( "Tracker: redirected URL: %s:%d", address, port );
tc->http = tr_httpClient( TR_HTTP_GET, address, port, announce );
free( address );
free( announce );
}
free( tc->redirectAddress );
tc->redirectAddress = NULL;
}
else
{
/* Need to change to next address in list */
if( tc->shouldChangeAnnounce == TC_CHANGE_NEXT )
{
tr_inf( "Tracker: failed to connect to %s, trying next", tc->trackerAddress );
if( tc->announceTierLast + 1 < inf->trackerList[tc->announceTier].count )
{
tc->announceTierLast++;
announcePtr = tc->trackerAnnounceListPtr[tc->announceTier];
for( i = 0; i < tc->announceTierLast; i++ )
{
announcePtr = announcePtr->nextItem;
}
}
else
{
tc->announceTierLast = 0;
tc->announceTier++;
announcePtr = tc->trackerAnnounceListPtr[tc->announceTier];
}
tr_inf( "Tracker: tracker address set to %s", tc->trackerAnnounceListPtr[tc->announceTier]->item->address );
setAnnounce( tc, announcePtr );
}
/* Need to change to first in list */
else if( tc->announceTier != 0 || tc->announceTierLast != 0 )
{
/* Check if the last announce was successful and wasn't the first in the sublist */
if( tc->shouldChangeAnnounce == TC_CHANGE_NO && tc->announceTierLast != 0 )
{
announcePtr = tc->trackerAnnounceListPtr[tc->announceTier];
for( i = 0; i < tc->announceTierLast; i++ )
{
prevAnnouncePtr = announcePtr;
announcePtr = announcePtr->nextItem;
}
/* Move address to front of tier in announce list */
prevAnnouncePtr->nextItem = announcePtr->nextItem;
announcePtr->nextItem = tc->trackerAnnounceListPtr[tc->announceTier];
tc->trackerAnnounceListPtr[tc->announceTier] = announcePtr;
}
setAnnounce( tc, tc->trackerAnnounceListPtr[0] );
tc->announceTier = 0;
tc->announceTierLast = 0;
}
tc->http = getQuery( tc );
tr_inf( "Tracker: connecting to %s:%d (%s)",
inf->trackerAddress, inf->trackerPort,
tc->started ? "sending 'started'" :
( tc->completed ? "sending 'completed'" :
( tc->stopped ? "sending 'stopped'" :
( 0 < tc->newPort ? "sending 'stopped' to change port" :
"getting peers" ) ) ) );
tr_inf( "Tracker: connecting to %s:%d (%s)",
tc->trackerAddress, tc->trackerPort,
tc->started ? "sending 'started'" :
( tc->completed ? "sending 'completed'" :
( tc->stopped ? "sending 'stopped'" :
( 0 < tc->newPort ? "sending 'stopped' to change port" :
"getting peers" ) ) ) );
}
tc->shouldChangeAnnounce = TC_CHANGE_NO;
}
if( NULL != tc->http )
@ -221,11 +425,28 @@ int tr_trackerPulse( tr_tracker_t * tc )
case TR_ERROR:
killHttp( &tc->http, tor->fdlimit );
tc->dateTry = tr_date();
failureAnnouncing( tc );
if ( tc->shouldChangeAnnounce == TC_CHANGE_NEXT )
{
tr_trackerPulse( tc );
return;
}
break;
case TR_OK:
readAnswer( tc, data, len );
killHttp( &tc->http, tor->fdlimit );
/* Something happened to need to try next address */
if ( tc->shouldChangeAnnounce == TC_CHANGE_NEXT
|| tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
{
tr_trackerPulse( tc );
return;
}
break;
}
}
@ -234,12 +455,31 @@ int tr_trackerPulse( tr_tracker_t * tc )
{
if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
{
return 0;
return;
}
tc->dateScrape = tr_date();
tc->httpScrape = getScrapeQuery( tc );
tr_inf( "Scrape: sent http request to %s:%d",
inf->trackerAddress, inf->trackerPort );
if ( tc->redirectScrapeAddress != NULL )
{
/* Use redirected address */
if( !tr_httpParseUrl( tc->redirectScrapeAddress, tc->redirectScrapeAddressLen,
&address, &port, &announce ) )
{
tr_err( "Scrape: redirected URL: %s:%d", address, port );
tc->httpScrape = tr_httpClient( TR_HTTP_GET, address, port, announce );
free( address );
free( announce );
}
free( tc->redirectScrapeAddress );
tc->redirectScrapeAddress = NULL;
}
else
{
tc->httpScrape = getScrapeQuery( tc );
tr_inf( "Scrape: sent HTTP request to %s:%d%s", tc->trackerAddress, tc->trackerPort, tc->trackerScrape );
}
}
if( NULL != tc->httpScrape )
@ -261,7 +501,7 @@ int tr_trackerPulse( tr_tracker_t * tc )
}
}
return 0;
return;
}
void tr_trackerCompleted( tr_tracker_t * tc )
@ -300,7 +540,6 @@ void tr_trackerClose( tr_tracker_t * tc )
static tr_http_t * getQuery( tr_tracker_t * tc )
{
tr_torrent_t * tor = tc->tor;
tr_info_t * inf = &tor->info;
char * event, * trackerid, * idparam;
uint64_t left;
@ -348,10 +587,10 @@ static tr_http_t * getQuery( tr_tracker_t * tc )
idparam = "&trackerid=";
}
start = ( strchr( inf->trackerAnnounce, '?' ) ? '&' : '?' );
start = ( strchr( tc->trackerAnnounce, '?' ) ? '&' : '?' );
left = tr_cpLeftBytes( tor->completion );
return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
return tr_httpClient( TR_HTTP_GET, tc->trackerAddress, tc->trackerPort,
"%s%c"
"info_hash=%s&"
"peer_id=%s&"
@ -364,7 +603,7 @@ static tr_http_t * getQuery( tr_tracker_t * tc )
"key=%s"
"%s%s"
"%s",
inf->trackerAnnounce, start, tor->escapedHashString,
tc->trackerAnnounce, start, tor->escapedHashString,
tc->id, tc->bindPort, up, down, left, numwant,
tor->key, idparam, trackerid, event );
}
@ -372,16 +611,14 @@ static tr_http_t * getQuery( tr_tracker_t * tc )
static tr_http_t * getScrapeQuery( tr_tracker_t * tc )
{
tr_torrent_t * tor = tc->tor;
tr_info_t * inf = &tor->info;
char start;
start = ( strchr( tor->scrape, '?' ) ? '&' : '?' );
start = ( strchr( tc->trackerScrape, '?' ) ? '&' : '?' );
return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
return tr_httpClient( TR_HTTP_GET, tc->trackerAddress, tc->trackerPort,
"%s%c"
"info_hash=%s",
tor->scrape, start, tor->escapedHashString );
tc->trackerScrape, start, tor->escapedHashString );
}
static void readAnswer( tr_tracker_t * tc, const char * data, int len )
@ -396,11 +633,31 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len )
tc->dateTry = tr_date();
code = tr_httpResponseCode( data, len );
if( 0 > code )
{
/* We don't have a valid HTTP status line */
tr_inf( "Tracker: invalid HTTP status line" );
tc->lastAttempt = TC_ATTEMPT_NOREACH;
failureAnnouncing( tc );
return;
}
if( code == 301 || code == 302 )
{
tr_err( "Tracker: HTTP status code: %i", code );
tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
tr_httpParse( data, len, hdr );
char * address = calloc( sizeof( char ), hdr->len+1 );
snprintf( address, hdr->len+1, "%s", hdr->data );
tc->shouldChangeAnnounce = TC_CHANGE_REDIRECT;
tc->redirectAddress = address;
tc->redirectAddressLen = hdr->len;
return;
}
@ -409,6 +666,7 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len )
/* we didn't get a 2xx status code */
tr_err( "Tracker: invalid HTTP status code: %i", code );
tc->lastAttempt = TC_ATTEMPT_ERROR;
failureAnnouncing( tc );
return;
}
@ -418,6 +676,7 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len )
{
tr_err( "Tracker: could not find end of HTTP headers" );
tc->lastAttempt = TC_ATTEMPT_NOREACH;
failureAnnouncing( tc );
return;
}
bodylen = len - ( body - (const uint8_t*)data );
@ -442,6 +701,7 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len )
}
tr_err( "Tracker: no valid dictionary found in answer" );
tc->lastAttempt = TC_ATTEMPT_ERROR;
failureAnnouncing( tc );
return;
}
@ -449,13 +709,24 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len )
if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
{
tr_err( "Tracker: %s", bePeers->val.s.s );
tr_err( "Tracker: Error - %s", bePeers->val.s.s );
tor->error |= TR_ETRACKER;
snprintf( tor->trackerError, sizeof( tor->trackerError ),
"%s", bePeers->val.s.s );
tc->lastAttempt = TC_ATTEMPT_ERROR;
failureAnnouncing( tc );
goto cleanup;
}
else if( ( bePeers = tr_bencDictFind( &beAll, "warning message" ) ) )
{
tr_err( "Tracker: Warning - %s", bePeers->val.s.s );
snprintf( tor->trackerError, sizeof( tor->trackerError ),
"%s", bePeers->val.s.s );
}
else
{
tor->trackerError[0] = '\0';
}
tor->error &= ~TR_ETRACKER;
tc->lastAttempt = TC_ATTEMPT_OK;
@ -542,6 +813,7 @@ static void readAnswer( tr_tracker_t * tc, const char * data, int len )
goto nodict;
}
tr_err( "Tracker: no \"peers\" field" );
failureAnnouncing( tc );
goto cleanup;
}
@ -639,11 +911,33 @@ static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len )
tc->lastScrapeFailed = 1;
return;
}
if( code == 301 || code == 302 )
{
tr_err( "Scrape: HTTP status code: %i", code );
tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
tr_httpParse( data, len, hdr );
char * address = calloc( sizeof( char ), hdr->len+1 );
snprintf( address, hdr->len+1, "%s", hdr->data );
/* Needs a new scrape */
tc->dateScrape = 0;
tc->redirectScrapeAddress = address;
tc->redirectScrapeAddressLen = hdr->len;
return;
}
if( !TR_HTTP_STATUS_OK( code ) )
{
/* we didn't get a 2xx status code */
tr_err( "Scrape: invalid HTTP status code: %i", code );
if( TR_HTTP_STATUS_FAIL_CLIENT( code ) )
tc->trackerCanScrape = 0;
tc->lastScrapeFailed = 1;
return;
}
@ -751,6 +1045,42 @@ int tr_trackerDownloaded( tr_tracker_t * tc )
return tc->complete;
}
const char * tr_trackerAddress( tr_tracker_t * tc )
{
if( !tc )
{
return NULL;
}
return tc->trackerAddress;
}
int tr_trackerPort( tr_tracker_t * tc )
{
if( !tc )
{
return 0;
}
return tc->trackerPort;
}
const char * tr_trackerAnnounce( tr_tracker_t * tc )
{
if( !tc )
{
return NULL;
}
return tc->trackerAnnounce;
}
int tr_trackerCannotConnecting( tr_tracker_t * tc )
{
if( !tc )
{
return 0;
}
return tc->completelyUnconnectable;
}
/* Blocking version */
int tr_trackerScrape( tr_torrent_t * tor, int * s, int * l, int * d )
{
@ -759,13 +1089,14 @@ int tr_trackerScrape( tr_torrent_t * tor, int * s, int * l, int * d )
const char * data;
int len;
int ret;
tc = tr_trackerInit( tor );
if( !tor->scrape[0] )
if( !tc->trackerCanScrape )
{
return 1;
}
tc = tr_trackerInit( tor );
http = getScrapeQuery( tc );
for( data = NULL; !data; tr_wait( 10 ) )

View File

@ -29,7 +29,7 @@ typedef struct tr_tracker_s tr_tracker_t;
tr_tracker_t * tr_trackerInit ( tr_torrent_t * );
void tr_trackerChangePort( tr_tracker_t *, int );
int tr_trackerPulse ( tr_tracker_t * );
void tr_trackerPulse ( tr_tracker_t * );
void tr_trackerCompleted ( tr_tracker_t * );
void tr_trackerStopped ( tr_tracker_t * );
void tr_trackerClose ( tr_tracker_t * );
@ -56,6 +56,12 @@ int tr_trackerLeechers ( tr_tracker_t * );
**********************************************************************/
int tr_trackerDownloaded( tr_tracker_t * tc );
const char * tr_trackerAddress ( tr_tracker_t * tc );
int tr_trackerPort ( tr_tracker_t * tc );
const char * tr_trackerAnnounce( tr_tracker_t * tc );
int tr_trackerCannotConnecting( tr_tracker_t * tc );
/***********************************************************************
* tr_trackerScrape
***********************************************************************

View File

@ -30,8 +30,8 @@
static tr_torrent_t * torrentRealInit( tr_handle_t *, tr_torrent_t * tor,
int flags, int * error );
static void torrentReallyStop( tr_torrent_t * );
static void downloadLoop( void * );
static void acceptLoop( void * );
static void downloadLoop( void * );
static void acceptLoop( void * );
static void acceptStop( tr_handle_t * h );
/***********************************************************************
@ -277,7 +277,6 @@ static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
tr_torrent_t * tor_tmp;
tr_info_t * inf;
int i;
char * s1, * s2;
inf = &tor->info;
inf->flags = flags;
@ -289,8 +288,7 @@ static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
SHA_DIGEST_LENGTH ) )
{
*error = TR_EDUPLICATE;
free( inf->pieces );
free( inf->files );
tr_metainfoFree( &tor->info );
free( tor );
return NULL;
}
@ -302,23 +300,6 @@ static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
tor->bindPort = &h->bindPort;
tor->finished = 0;
/* Guess scrape URL */
s1 = strchr( inf->trackerAnnounce, '/' );
while( ( s2 = strchr( s1 + 1, '/' ) ) )
{
s1 = s2;
}
s1++;
if( !strncmp( s1, "announce", 8 ) )
{
int pre = (long) s1 - (long) inf->trackerAnnounce;
int post = strlen( inf->trackerAnnounce ) - pre - 8;
memcpy( tor->scrape, inf->trackerAnnounce, pre );
sprintf( &tor->scrape[pre], "scrape" );
memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
}
/* Escaped info hash for HTTP queries */
for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
{
@ -473,6 +454,7 @@ tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
tr_stat_t * s;
tr_peer_t * peer;
tr_info_t * inf = &tor->info;
tr_tracker_t * tc = tor->tracker;
int i;
tor->statCur = ( tor->statCur + 1 ) % 2;
@ -492,6 +474,20 @@ tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
s->error = tor->error;
memcpy( s->trackerError, tor->trackerError,
sizeof( s->trackerError ) );
s->cannotConnect = tr_trackerCannotConnecting( tc );
if( tor->tracker )
{
s->trackerAddress = tr_trackerAddress( tor->tracker );
s->trackerPort = tr_trackerPort( tor->tracker );
s->trackerAnnounce = tr_trackerAnnounce( tor->tracker );
}
else
{
s->trackerAddress = inf->trackerList[0].list[0].address;
s->trackerPort = inf->trackerList[0].list[0].port;
s->trackerAnnounce = inf->trackerList[0].list[0].announce;
}
s->peersTotal = 0;
s->peersIncoming = 0;
@ -537,8 +533,9 @@ tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
}
s->rateUpload = tr_rcRate( tor->upload );
s->seeders = tr_trackerSeeders(tor->tracker);
s->leechers = tr_trackerLeechers(tor->tracker);
s->seeders = tr_trackerSeeders( tc );
s->leechers = tr_trackerLeechers( tc );
s->completedFromTracker = tr_trackerDownloaded( tor->tracker );
s->completedFromTracker = tr_trackerDownloaded(tor->tracker);
s->swarmspeed = tr_rcRate( tor->swarmspeed );
@ -689,8 +686,8 @@ void tr_torrentClose( tr_handle_t * h, tr_torrent_t * tor )
{
free( tor->destination );
}
free( inf->pieces );
free( inf->files );
tr_metainfoFree( inf );
if( tor->prev )
{

View File

@ -61,6 +61,8 @@ extern "C" {
typedef struct tr_handle_s tr_handle_t;
tr_handle_t * tr_init();
typedef struct tr_tracker_info_s tr_tracker_info_t;
/***********************************************************************
* tr_setMessageLevel
***********************************************************************
@ -303,37 +305,41 @@ tr_file_t;
struct tr_info_s
{
/* Path to torrent */
char torrent[MAX_PATH_LENGTH];
char torrent[MAX_PATH_LENGTH];
/* General info */
uint8_t hash[SHA_DIGEST_LENGTH];
char hashString[2*SHA_DIGEST_LENGTH+1];
char name[MAX_PATH_LENGTH];
uint8_t hash[SHA_DIGEST_LENGTH];
char hashString[2*SHA_DIGEST_LENGTH+1];
char name[MAX_PATH_LENGTH];
/* Flags */
#define TR_FSAVEPRIVATE 0x01 /* save a private copy of the torrent */
int flags;
int flags;
/* Tracker info */
char trackerAddress[256];
int trackerPort;
char trackerAnnounce[MAX_PATH_LENGTH];
struct
{
tr_tracker_info_t * list;
int count;
} * trackerList;
int trackerTiers;
/* Torrent info */
char comment[MAX_PATH_LENGTH];
char creator[MAX_PATH_LENGTH];
int dateCreated;
char comment[MAX_PATH_LENGTH];
char creator[MAX_PATH_LENGTH];
int dateCreated;
int privateTorrent;
/* Pieces info */
int pieceSize;
int pieceCount;
uint64_t totalSize;
uint8_t * pieces;
int pieceSize;
int pieceCount;
uint64_t totalSize;
uint8_t * pieces;
/* Files info */
int multifile;
int fileCount;
tr_file_t * files;
int multifile;
int fileCount;
tr_file_t * files;
};
/***********************************************************************
@ -357,6 +363,11 @@ struct tr_stat_s
#define TR_EINOUT 2
int error;
char trackerError[128];
int cannotConnect;
const char * trackerAddress;
int trackerPort;
const char * trackerAnnounce;
float progress;
float rateDownload;
@ -395,6 +406,13 @@ struct tr_msg_list_s
struct tr_msg_list_s * next;
};
struct tr_tracker_info_s
{
char * address;
int port;
char * announce;
};
#ifdef __TRANSMISSION__
# include "internal.h"
#endif

View File

@ -28,6 +28,7 @@
fDownloadedTotalField = NSTextField;
fDownloadedValidField = NSTextField;
fDownloadingFromField = NSTextField;
fErrorMessageView = NSTextView;
fFileTable = NSTableView;
fFileTableStatusField = NSTextField;
fHashField = NSTextField;
@ -42,6 +43,7 @@
fRatioMatrix = NSMatrix;
fRevealDataButton = NSButton;
fRevealTorrentButton = NSButton;
fSecureField = NSTextField;
fSeedersField = NSTextField;
fSizeField = NSTextField;
fStateField = NSTextField;

View File

@ -37,7 +37,7 @@
IBOutlet NSImageView * fImageView;
IBOutlet NSTextField * fNameField, * fSizeField, * fTrackerField,
* fPiecesField, * fHashField,
* fPiecesField, * fHashField, * fSecureField,
* fTorrentLocationField, * fDataLocationField,
* fDateStartedField,
* fCreatorField, * fDateCreatedField,
@ -50,14 +50,14 @@
IBOutlet NSTableView * fPeerTable;
IBOutlet NSTextField * fSeedersField, * fLeechersField, * fConnectedPeersField,
* fDownloadingFromField, * fUploadingToField, * fCompletedFromTrackerField;
IBOutlet NSTextView * fErrorMessageView;
IBOutlet PiecesView * fPiecesView;
IBOutlet NSTableView * fFileTable;
IBOutlet NSTextField * fFileTableStatusField;
IBOutlet NSMatrix * fRatioMatrix;
IBOutlet NSTextField * fRatioLimitField;
IBOutlet PiecesView * fPiecesView;
}
- (void) updateInfoForTorrents: (NSArray *) torrents;

View File

@ -39,8 +39,8 @@
#define TAB_OPTIONS_IDENT @"Options"
//15 spacing at the bottom of each tab
#define TAB_INFO_HEIGHT 268.0
#define TAB_ACTIVITY_HEIGHT 109.0
#define TAB_INFO_HEIGHT 284.0
#define TAB_ACTIVITY_HEIGHT 170.0
#define TAB_PEERS_HEIGHT 268.0
#define TAB_FILES_HEIGHT 268.0
#define TAB_OPTIONS_HEIGHT 83.0
@ -49,6 +49,7 @@
@interface InfoWindowController (Private)
- (void) updateInfoGeneral;
- (void) updateInfoActivity;
- (void) updateInfoPeers;
@ -154,8 +155,8 @@
[fPiecesField setStringValue: @""];
[fHashField setStringValue: @""];
[fHashField setToolTip: nil];
[fSecureField setStringValue: @""];
[fCommentView setString: @""];
[fCommentView setSelectable: NO];
[fCreatorField setStringValue: @""];
[fDateCreatedField setStringValue: @""];
@ -165,6 +166,7 @@
[fDataLocationField setStringValue: @""];
[fDataLocationField setToolTip: nil];
[fDateStartedField setStringValue: @""];
[fCommentView setSelectable: NO];
[fRevealDataButton setHidden: YES];
[fRevealTorrentButton setHidden: YES];
@ -186,6 +188,8 @@
[fDownloadingFromField setStringValue: @""];
[fUploadingToField setStringValue: @""];
[fSwarmSpeedField setStringValue: @""];
[fErrorMessageView setString: @""];
[fErrorMessageView setSelectable: NO];
[fPeers removeAllObjects];
[fPeerTable reloadData];
@ -203,17 +207,16 @@
[fNameField setToolTip: name];
[fSizeField setStringValue: [NSString stringForFileSize: [torrent size]]];
NSString * tracker = [[torrent tracker] stringByAppendingString: [torrent announce]],
* hashString = [torrent hashString],
NSString * hashString = [torrent hashString],
* commentString = [torrent comment];
[fTrackerField setStringValue: tracker];
[fTrackerField setToolTip: tracker];
[fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
[NSString stringForFileSize: [torrent pieceSize]]]];
[fHashField setStringValue: hashString];
[fHashField setToolTip: hashString];
[fSecureField setStringValue: [torrent privateTorrent]
? NSLocalizedString(@"Private Torrent", "Inspector -> is private torrent")
: NSLocalizedString(@"Public Torrent", "Inspector -> is not private torrent")];
[fCommentView setString: commentString];
[fCommentView setSelectable: YES];
[fCreatorField setStringValue: [torrent creator]];
[fDateCreatedField setObjectValue: [torrent dateCreated]];
@ -279,9 +282,23 @@
[self updateInfoActivity];
else if ([[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_PEERS_IDENT])
[self updateInfoPeers];
else if ([[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_INFO_IDENT])
[self updateInfoGeneral];
else;
}
- (void) updateInfoGeneral
{
int numberSelected = [fTorrents count];
if (numberSelected != 1)
return;
Torrent * torrent = [fTorrents objectAtIndex: 0];
NSString * tracker = [[torrent tracker] stringByAppendingString: [torrent announce]];
[fTrackerField setStringValue: tracker];
[fTrackerField setToolTip: tracker];
}
- (void) updateInfoActivity
{
int numberSelected = [fTorrents count];
@ -312,11 +329,16 @@
stringByAppendingFormat: @" (%.2f%%)", 100.0 * [torrent progress]]];
[fStateField setStringValue: [torrent stateString]];
[fRatioField setStringValue: [NSString stringForRatioWithDownload: downloadedTotal upload: uploadedTotal]];
[fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
NSString * errorMessage = [torrent errorMessage];
if (![errorMessage isEqualToString: [fErrorMessageView string]])
{
[fErrorMessageView setString: errorMessage];
[fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
}
[fPiecesView updateView: NO];
}
}

View File

@ -115,6 +115,7 @@
- (int) pieceSize;
- (int) pieceCount;
- (NSString *) hashString;
- (BOOL) privateTorrent;
- (NSString *) torrentLocation;
- (NSString *) publicTorrentLocation;
@ -127,11 +128,12 @@
- (float) progress;
- (int) eta;
- (BOOL) isActive;
- (BOOL) isSeeding;
- (BOOL) isPaused;
- (BOOL) isError;
- (BOOL) justFinished;
- (BOOL) isActive;
- (BOOL) isSeeding;
- (BOOL) isPaused;
- (BOOL) isError;
- (NSString *) errorMessage;
- (BOOL) justFinished;
- (NSArray *) peers;

View File

@ -349,18 +349,14 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
if (fStat->error & TR_ETRACKER)
{
[fStatusString setString: [NSLocalizedString(@"Error: ", "Torrent -> status string") stringByAppendingString:
[NSString stringWithUTF8String: fStat->trackerError]]];
if (!fError && [self isActive])
{
fError = YES;
if (![self isSeeding])
[[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
}
[self errorMessage]]];
}
else
BOOL wasError = fError;
if ((fError = fStat->cannotConnect))
{
if (fError)
fError = NO;
if (!wasError && [self isActive] && ![self isSeeding])
[[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
}
if ([self isActive] && fStat->status != TR_STATUS_CHECK )
@ -704,12 +700,12 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
- (NSString *) tracker
{
return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
return [NSString stringWithFormat: @"http://%s:%d", fStat->trackerAddress, fStat->trackerPort];
}
- (NSString *) announce
{
return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
return [NSString stringWithUTF8String: fStat->trackerAnnounce];
}
- (NSString *) comment
@ -743,6 +739,11 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
return [NSString stringWithUTF8String: fInfo->hashString];
}
- (BOOL) privateTorrent
{
return fInfo->privateTorrent;
}
- (NSString *) torrentLocation
{
return [NSString stringWithUTF8String: fInfo->torrent];
@ -822,6 +823,11 @@ static uint32_t kRed = BE(0xFF6450FF), //255, 100, 80
return fStat->error & TR_ETRACKER;
}
- (NSString *) errorMessage
{
[NSString stringWithUTF8String: fStat->trackerError];
}
- (BOOL) justFinished
{
return tr_getFinished(fHandle);