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:
parent
f79b141ab1
commit
3374ce9a32
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) )
|
||||
|
|
|
@ -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
|
||||
***********************************************************************
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Binary file not shown.
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue