source: trunk/libtransmission/tracker.c @ 3451

Last change on this file since 3451 was 3451, checked in by charles, 15 years ago

huge tracker cleanup for the "no response from tracker" issue

  • Property svn:keywords set to Date Rev Author Id
File size: 24.8 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: tracker.c 3451 2007-10-17 18:59:58Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isalnum */
15#include <stdio.h> /* snprintf */
16#include <stdlib.h>
17#include <string.h> /* strcmp, strchr */
18
19#include <sys/queue.h> /* libevent needs this */
20#include <sys/types.h> /* libevent needs this */
21#include <event.h>
22#include <evhttp.h>
23
24#include "transmission.h"
25#include "bencode.h"
26#include "completion.h"
27#include "net.h"
28#include "publish.h"
29#include "shared.h"
30#include "tracker.h"
31#include "trevent.h"
32#include "utils.h"
33
34enum
35{
36    /* unless the tracker says otherwise, rescrape this frequently */
37    DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 15),
38
39    /* unless the tracker says otherwise, this is the announce interval */
40    DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 4),
41
42    /* unless the tracker says otherwise, this is the announce min_interval */
43    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
44
45    /* this is how long we'll leave a request hanging before timeout */
46    TIMEOUT_INTERVAL_SEC = 45,
47
48    /* this is how long we'll leave a 'stop' request hanging before timeout.
49       we wait less time for this so it doesn't slow down shutdowns */
50    STOP_TIMEOUT_INTERVAL_SEC = 5,
51
52    /* the value of the 'numwant' argument passed in tracker requests. */
53    NUMWANT = 200,
54
55    /* the length of the 'key' argument passed in tracker requests */
56    KEYLEN = 10
57};
58
59/**
60***
61**/
62
63struct tr_tracker
64{
65    tr_handle * handle;
66
67    /* these are set from the latest scrape or tracker response */
68    int announceIntervalSec;
69    int announceMinIntervalSec;
70    int scrapeIntervalSec;
71
72    tr_tracker_info * redirect;
73    tr_tracker_info * addresses;
74    int addressIndex;
75    int addressCount;
76    int * tierFronts;
77
78    /* sent as the "key" argument in tracker requests
79       to verify us if our IP address changes.
80       This is immutable for the life of the tracker object. */
81    char key_param[KEYLEN+1];
82
83    tr_publisher_t * publisher;
84
85    /* torrent hash string */
86    uint8_t hash[SHA_DIGEST_LENGTH];
87    char escaped[SHA_DIGEST_LENGTH * 3 + 1];
88    char * name;
89
90    /* corresponds to the peer_id sent as a tracker request parameter.
91       OiNK's op TooMuchTime says: "When the same torrent is opened and
92       closed and opened again without quitting Transmission ...
93       change the peerid. It would help sometimes if a stopped event
94       was missed to ensure that we didn't think someone was cheating. */
95    char peer_id[TR_ID_LEN + 1];
96
97    /* these are set from the latest tracker response... -1 is 'unknown' */
98    int timesDownloaded;
99    int seederCount;
100    int leecherCount;
101    char * trackerID;
102
103    /* the last tracker request we sent. (started, stopped, etc.)
104       automatic announces are an empty string;
105       NULL means no message has ever been sent */
106    char * lastRequest;
107
108    time_t manualAnnounceAllowedAt;
109
110    tr_timer * scrapeTimer;
111    tr_timer * reannounceTimer;
112
113    unsigned int isRunning : 1;
114};
115
116/***
117****  Connections that know how to clean up after themselves
118***/
119
120static int
121freeConnection( void * evcon )
122{
123    evhttp_connection_free( evcon );
124    return FALSE;
125}
126
127static void
128connectionClosedCB( struct evhttp_connection * evcon, void * handle )
129{
130    /* libevent references evcon right after calling this function,
131       so we can't free it yet... defer it to after this call chain
132       has played out */
133    tr_timerNew( handle, freeConnection, evcon, 100 );
134}
135
136static struct evhttp_connection*
137getConnection( tr_tracker * t, const char * address, int port )
138{
139    struct evhttp_connection * c = evhttp_connection_new( address, port );
140    evhttp_connection_set_closecb( c, connectionClosedCB, t->handle );
141    return c;
142}
143
144/***
145****  PUBLISH
146***/
147
148static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0 };
149
150static void
151publishMessage( tr_tracker * t, const char * msg, int type )
152{
153    tr_tracker_event event = emptyEvent;
154    event.hash = t->hash;
155    event.messageType = type;
156    event.text = msg;
157    tr_publisherPublish( t->publisher, t, &event );
158}
159
160static void
161publishErrorClear( tr_tracker * t )
162{
163    publishMessage( t, NULL, TR_TRACKER_ERROR_CLEAR );
164}
165
166static void
167publishErrorMessage( tr_tracker * t, const char * msg )
168{
169    publishMessage( t, msg, TR_TRACKER_ERROR );
170}
171
172static void
173publishWarning( tr_tracker * t, const char * msg )
174{
175    publishMessage( t, msg, TR_TRACKER_WARNING );
176}
177
178static void
179publishNewPeers( tr_tracker * t, int count, uint8_t * peers )
180{
181    tr_tracker_event event = emptyEvent;
182    event.hash = t->hash;
183    event.messageType = TR_TRACKER_PEERS;
184    event.peerCount = count;
185    event.peerCompact = peers;
186    tr_inf( "Torrent \"%s\" got %d new peers", t->name, count );
187    tr_publisherPublish( t->publisher, t, &event );
188}
189
190/***
191****  LIFE CYCLE
192***/
193
194static void
195generateKeyParam( char * msg, int len )
196{
197    int i;
198    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
199    const int poolSize = strlen( pool );
200    for( i=0; i<len; ++i )
201        *msg++ = pool[tr_rand(poolSize)];
202    *msg = '\0';
203}
204
205static void
206escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
207{
208    const uint8_t *end = in + in_len;
209    while( in != end )
210        if( isalnum(*in) )
211            *out++ = (char) *in++;
212        else 
213            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
214    *out = '\0';
215}
216
217static void scrapeNow( tr_tracker * );
218
219tr_tracker *
220tr_trackerNew( const tr_torrent * torrent )
221{
222    const tr_info * info = &torrent->info;
223    int i, j, sum, *iwalk;
224    tr_tracker_info * nwalk;
225    tr_tracker * t;
226
227    tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
228
229    t = tr_new0( tr_tracker, 1 );
230    t->handle = torrent->handle;
231    t->scrapeIntervalSec       = DEFAULT_SCRAPE_INTERVAL_SEC;
232    t->announceIntervalSec     = DEFAULT_ANNOUNCE_INTERVAL_SEC;
233    t->announceMinIntervalSec  = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
234    generateKeyParam( t->key_param, KEYLEN );
235
236    t->publisher = tr_publisherNew( );
237    t->timesDownloaded = -1;
238    t->seederCount = -1;
239    t->leecherCount = -1;
240    t->manualAnnounceAllowedAt = ~(time_t)0;
241    t->name = tr_strdup( info->name );
242    memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
243    escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
244
245    for( sum=i=0; i<info->trackerTiers; ++i )
246         sum += info->trackerList[i].count;
247    t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
248    t->addressIndex = 0;
249    t->addressCount = sum;
250    t->tierFronts = iwalk = tr_new0( int, sum );
251
252    for( i=0; i<info->trackerTiers; ++i )
253    {
254        const int tierFront = nwalk - t->addresses;
255
256        for( j=0; j<info->trackerList[i].count; ++j )
257        {
258            const tr_tracker_info * src = &info->trackerList[i].list[j];
259            nwalk->address = tr_strdup( src->address );
260            nwalk->port = src->port;
261            nwalk->announce = tr_strdup( src->announce );
262            nwalk->scrape = tr_strdup( src->scrape );
263            ++nwalk;
264
265            *iwalk++ = tierFront;
266        }
267    }
268
269    assert( nwalk - t->addresses == sum );
270    assert( iwalk - t->tierFronts == sum );
271
272    scrapeNow( t );
273fprintf( stderr, "sizeof(tr_tracker) is %d\n", (int)sizeof(tr_tracker) );
274    return t;
275}
276
277static void
278onTrackerFreeNow( void * vt )
279{
280    int i;
281    tr_tracker * t = vt;
282
283    tr_timerFree( &t->scrapeTimer );
284    tr_timerFree( &t->reannounceTimer );
285    tr_publisherFree( &t->publisher );
286    tr_free( t->name );
287    tr_free( t->trackerID );
288    tr_free( t->lastRequest );
289
290    for( i=0; i<t->addressCount; ++i )
291        tr_trackerInfoClear( &t->addresses[i] );
292
293    if( t->redirect ) {
294        tr_trackerInfoClear( t->redirect );
295        tr_free( t->redirect );
296    }
297
298    tr_timerFree( &t->scrapeTimer );
299
300    tr_free( t->addresses );
301    tr_free( t->tierFronts );
302    tr_free( t );
303}
304
305void
306tr_trackerFree( tr_tracker * t )
307{
308    tr_runInEventThread( t->handle, onTrackerFreeNow, t );
309}
310
311/***
312****  UTIL
313***/
314
315static int
316parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
317{
318    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
319    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
320    int ret = 1;
321    int i;
322
323    for( i=0; ret && i<bodylen; ++i )
324        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
325            ret = 0;
326
327    return ret;
328}
329
330static const char*
331updateAddresses( tr_tracker * t, const struct evhttp_request * req )
332{
333    const char * ret = NULL;
334    int moveToNextAddress = FALSE;
335
336    if( !req )
337    {
338        ret = "No response from tracker -- will keep trying.";
339        tr_inf( ret );
340
341        moveToNextAddress = TRUE;
342    }
343    else if( req->response_code == HTTP_OK )
344    {
345        if( t->redirect != NULL )
346        {
347            /* multitracker spec: "if a connection with a tracker is
348               successful, it will be moved to the front of the tier." */
349            const int i = t->addressIndex;
350            const int j = t->tierFronts[i];
351            const tr_tracker_info swap = t->addresses[i];
352            t->addresses[i] = t->addresses[j];
353            t->addresses[j] = swap;
354        }
355    }
356    else if(    ( req->response_code == HTTP_MOVEPERM )
357             || ( req->response_code == HTTP_MOVETEMP ) )
358    {
359        const char * loc = evhttp_find_header( req->input_headers, "Location" );
360        tr_tracker_info tmp;
361        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
362        {
363            moveToNextAddress = TRUE;
364        }
365        else if( req->response_code == HTTP_MOVEPERM )
366        {
367            tr_tracker_info * cur = &t->addresses[t->addressIndex];
368            tr_trackerInfoClear( cur );
369            *cur = tmp;
370        }
371        else if( req->response_code == HTTP_MOVETEMP )
372        {
373            if( t->redirect == NULL )
374                t->redirect = tr_new0( tr_tracker_info, 1 );
375            else
376                tr_trackerInfoClear( t->redirect );
377            *t->redirect = tmp;
378        }
379    }
380    else 
381    {
382        ret = "No response from tracker -- will keep trying.";
383        moveToNextAddress = TRUE;
384    }
385
386    if( moveToNextAddress )
387        if ( ++t->addressIndex >= t->addressCount )
388            t->addressIndex = 0;
389
390    return ret;
391}
392
393static tr_tracker_info *
394getCurrentAddress( const tr_tracker * t )
395{
396    assert( t->addresses != NULL );
397    assert( t->addressIndex >= 0 );
398    assert( t->addressIndex < t->addressCount );
399
400    return &t->addresses[t->addressIndex];
401}
402static int
403trackerSupportsScrape( const tr_tracker * t )
404{
405    const tr_tracker_info * info = getCurrentAddress( t );
406
407    return ( info != NULL )
408        && ( info->scrape != NULL )
409        && ( info->scrape[0] != '\0' );
410}
411
412
413static void
414addCommonHeaders( const tr_tracker * t,
415                  struct evhttp_request * req )
416{
417    char buf[1024];
418    const tr_tracker_info * address = getCurrentAddress( t );
419    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
420    evhttp_add_header( req->output_headers, "Host", buf );
421    evhttp_add_header( req->output_headers, "Connection", "close" );
422    evhttp_add_header( req->output_headers, "Content-Length", "0" );
423    evhttp_add_header( req->output_headers, "User-Agent",
424                                         TR_NAME "/" LONG_VERSION_STRING );
425}
426
427/**
428***
429**/
430
431struct torrent_hash
432{
433    tr_handle * handle;
434    uint8_t hash[SHA_DIGEST_LENGTH];
435};
436
437static struct torrent_hash*
438torrentHashNew( tr_tracker * t )
439{
440    struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
441    data->handle = t->handle;
442    memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
443    return data;
444}
445
446tr_tracker *
447findTrackerFromHash( struct torrent_hash * data )
448{
449    tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
450    return torrent ? torrent->tracker : NULL;
451}
452
453/***
454****
455****  SCRAPE
456****
457***/
458
459static int
460onScrapeNow( void * vt );
461
462static void
463onScrapeResponse( struct evhttp_request * req, void * vhash )
464{
465    const char * warning;
466    time_t nextScrapeSec = 60;
467    tr_tracker * t;
468
469    t = findTrackerFromHash( vhash );
470    tr_free( vhash );
471    if( t == NULL ) /* tracker's been closed... */
472        return;
473
474    tr_inf( "Got scrape response for  '%s': %s",
475            t->name,
476            ( ( req && req->response_code_line ) ? req->response_code_line
477                                                 : "(null)") );
478
479    if( req && ( req->response_code == HTTP_OK ) )
480    {
481        benc_val_t benc, *files;
482        const int bencLoaded = !parseBencResponse( req, &benc );
483
484        if( bencLoaded
485            && (( files = tr_bencDictFind( &benc, "files" ) ))
486            && ( files->type == TYPE_DICT ) )
487        {
488            int i;
489            for( i=0; i<files->val.l.count; i+=2 )
490            {
491                const uint8_t* hash =
492                        (const uint8_t*) files->val.l.vals[i].val.s.s;
493                benc_val_t *tmp, *flags;
494                benc_val_t *tordict = &files->val.l.vals[i+1];
495                if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
496                    continue;
497
498                publishErrorClear( t );
499
500                if(( tmp = tr_bencDictFind( tordict, "complete" )))
501                    t->seederCount = tmp->val.i;
502
503                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
504                    t->leecherCount = tmp->val.i;
505
506                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
507                    t->timesDownloaded = tmp->val.i;
508
509                if(( flags = tr_bencDictFind( tordict, "flags" )))
510                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
511                        t->scrapeIntervalSec = tmp->val.i;
512
513                tr_dbg( "Torrent '%s' scrape successful."
514                        "  Rescraping in %d seconds",
515                        t->name, t->scrapeIntervalSec );
516
517                nextScrapeSec = t->scrapeIntervalSec;
518            }
519        }
520
521        if( bencLoaded )
522            tr_bencFree( &benc );
523    }
524
525    if (( warning = updateAddresses( t, req ) )) {
526        tr_err( warning );
527        publishWarning( t, warning );
528    }
529
530    tr_timerFree( &t->scrapeTimer );
531
532    t->scrapeTimer = tr_timerNew( t->handle,
533                                  onScrapeNow, t,
534                                  nextScrapeSec*1000 );
535}
536
537static int
538onScrapeNow( void * vt )
539{
540    tr_tracker * t = vt;
541    const tr_tracker_info * address = getCurrentAddress( t );
542
543    if( trackerSupportsScrape( t ) )
544    {
545        char * uri;
546        struct evhttp_connection * evcon;
547        struct evhttp_request *req;
548        struct evbuffer * buf = evbuffer_new( );
549
550        evbuffer_add_printf( buf, "%s?info_hash=%s", address->scrape, t->escaped );
551        uri = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
552        evbuffer_free( buf );
553
554        tr_inf( "Sending scrape to tracker %s:%d: %s",
555                address->address, address->port, uri );
556
557        evcon = getConnection( t, address->address, address->port );
558        evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
559        req = evhttp_request_new( onScrapeResponse, torrentHashNew( t ) );
560        addCommonHeaders( t, req );
561        tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
562    }
563
564    t->scrapeTimer = NULL;
565    return FALSE;
566}
567
568static void
569scrapeNow( tr_tracker * t )
570{
571    onScrapeNow( t );
572}
573
574/***
575****
576****  TRACKER REQUESTS
577****
578***/
579
580static char*
581buildTrackerRequestURI( const tr_tracker  * t,
582                        const tr_torrent  * torrent,
583                        const char        * eventName )
584{
585    const int isStopping = !strcmp( eventName, "stopped" );
586    const int numwant = isStopping ? 0 : NUMWANT;
587    struct evbuffer * buf = evbuffer_new( );
588    char * ret;
589
590    evbuffer_add_printf( buf, "%s"
591                              "?info_hash=%s"
592                              "&peer_id=%s"
593                              "&port=%d"
594                              "&uploaded=%"PRIu64
595                              "&downloaded=%"PRIu64
596                              "&corrupt=%"PRIu64
597                              "&left=%"PRIu64
598                              "&compact=1"
599                              "&numwant=%d"
600                              "&key=%s"
601                              "%s%s"
602                              "%s%s",
603        getCurrentAddress(t)->announce,
604        t->escaped,
605        t->peer_id,
606        tr_sharedGetPublicPort( t->handle->shared ),
607        torrent->uploadedCur,
608        torrent->downloadedCur,
609        torrent->corruptCur,
610        tr_cpLeftUntilComplete( torrent->completion ),
611        numwant,
612        t->key_param,
613        ( ( eventName && *eventName ) ? "&event=" : "" ),
614        ( ( eventName && *eventName ) ? eventName : "" ),
615        ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
616        ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
617
618    ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
619    evbuffer_free( buf );
620    return ret;
621}
622
623/* Convert to compact form */
624static uint8_t *
625parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
626{
627    int i;
628    uint8_t *compact, *walk;
629    const int peerCount = bePeers->val.l.count;
630
631    assert( bePeers->type == TYPE_LIST );
632
633    compact = tr_new( uint8_t, peerCount*6 );
634
635    for( i=0, walk=compact; i<peerCount; ++i )
636    {
637        struct in_addr addr;
638        tr_port_t port;
639        benc_val_t * val;
640        benc_val_t * peer = &bePeers->val.l.vals[i];
641
642        val = tr_bencDictFind( peer, "ip" );
643        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
644            continue;
645
646        memcpy( walk, &addr, 4 );
647        walk += 4;
648
649        val = tr_bencDictFind( peer, "port" );
650        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
651            continue;
652
653        port = htons( val->val.i );
654        memcpy( walk, &port, 2 );
655        walk += 2;
656    }
657
658    *setmePeerCount = peerCount;
659    return compact;
660}
661
662static int onReannounceNow( void * vtor );
663
664static void
665onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
666{
667}
668
669static void
670onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
671{
672    const char * warning;
673    tr_tracker * t;
674    int err = 0;
675
676    t = findTrackerFromHash( torrent_hash );
677    if( t == NULL ) /* tracker has been closed */
678        return;
679
680    tr_inf( "Torrent \"%s\" tracker response: %s",
681            t->name,
682            ( req ? req->response_code_line : "(null)") );
683
684    if( req && ( req->response_code == HTTP_OK ) )
685    {
686        benc_val_t benc;
687        const int bencLoaded = !parseBencResponse( req, &benc );
688
689        publishErrorClear( t );
690
691        if( bencLoaded && benc.type==TYPE_DICT )
692        {
693            benc_val_t * tmp;
694
695            if(( tmp = tr_bencDictFind( &benc, "failure reason" )))
696                publishErrorMessage( t, tmp->val.s.s );
697
698            if(( tmp = tr_bencDictFind( &benc, "warning message" )))
699                publishWarning( t, tmp->val.s.s );
700
701            if(( tmp = tr_bencDictFind( &benc, "interval" )))
702                t->announceIntervalSec = tmp->val.i;
703
704            if(( tmp = tr_bencDictFind( &benc, "min interval" )))
705                t->announceMinIntervalSec = tmp->val.i;
706
707            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
708                t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
709
710            if(( tmp = tr_bencDictFind( &benc, "complete" )))
711                t->seederCount = tmp->val.i;
712
713            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
714                t->leecherCount = tmp->val.i;
715
716            if(( tmp = tr_bencDictFind( &benc, "peers" )))
717            {
718                int peerCount = 0;
719                uint8_t * peerCompact = NULL;
720
721                if( tmp->type == TYPE_LIST ) /* original protocol */
722                {
723                    if( tmp->val.l.count > 0 )
724                        peerCompact = parseOldPeers( tmp, &peerCount );
725                }
726                else if( tmp->type == TYPE_STR ) /* "compact" extension */
727                {
728                    if( tmp->val.s.i >= 6 )
729                    {
730                        peerCount = tmp->val.s.i / 6;
731                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
732                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
733                    }
734                }
735
736                publishNewPeers( t, peerCount, peerCompact );
737                tr_free( peerCompact );
738            }
739        }
740
741        if( bencLoaded )
742            tr_bencFree( &benc );
743    }
744    else
745    {
746        tr_inf( "Bad response for torrent '%s' on request '%s' "
747                "... trying again in 30 seconds",
748                t->name, t->lastRequest );
749
750        err = 1;
751    }
752
753    if (( warning = updateAddresses( t, req ) )) {
754        publishWarning( t, warning );
755        tr_err( warning );
756    }
757
758    /* set reannounce times */
759    tr_timerFree( &t->reannounceTimer );
760    t->reannounceTimer = NULL;
761    t->manualAnnounceAllowedAt = ~(time_t)0;
762    if( t->isRunning )
763    {
764        tr_dbg( "torrent '%s' reannouncing in %d seconds",
765                t->name, t->announceIntervalSec );
766
767        t->reannounceTimer = tr_timerNew( t->handle,
768                                          onReannounceNow, t,
769                                          t->announceIntervalSec * 1000 );
770
771        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
772    }
773}
774
775static int
776sendTrackerRequest( void * vt, const char * eventName )
777{
778    tr_tracker * t = vt;
779    const int isStopping = eventName && !strcmp( eventName, "stopped" );
780    const tr_tracker_info * address = getCurrentAddress( t );
781    char * uri;
782    struct evhttp_connection * evcon;
783    const tr_torrent * tor;
784
785    tor = tr_torrentFindFromHash( t->handle, t->hash );
786    if( tor == NULL )
787        return FALSE;   
788
789    uri = buildTrackerRequestURI( t, tor, eventName );
790
791    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
792            t->name,
793            (eventName ? eventName : "periodic announce"),
794            address->address, address->port,
795            uri );
796
797    /* kill any pending requests */
798    tr_timerFree( &t->reannounceTimer );
799
800    evcon = getConnection( t, address->address, address->port );
801    if ( !evcon ) {
802        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
803        tr_free( uri );
804    } else {
805        struct evhttp_request * req;
806        tr_free( t->lastRequest );
807        t->lastRequest = tr_strdup( eventName );
808        if( isStopping ) {
809            evhttp_connection_set_timeout( evcon, STOP_TIMEOUT_INTERVAL_SEC );
810            req = evhttp_request_new( onStoppedResponse, t->handle );
811        } else {
812            evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
813            req = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
814        }
815        addCommonHeaders( t, req );
816        tr_evhttp_make_request( t->handle, evcon,
817                                req, EVHTTP_REQ_GET, uri );
818    }
819
820    return FALSE;
821}
822
823static int
824onReannounceNow( void * vt )
825{
826    tr_tracker * t = vt;
827    sendTrackerRequest( t, "" );
828    t->reannounceTimer = NULL;
829    return FALSE;
830}
831
832/***
833****  PUBLIC
834***/
835
836tr_publisher_tag
837tr_trackerSubscribe( tr_tracker          * t,
838                     tr_delivery_func      func,
839                     void                * user_data )
840{
841    return tr_publisherSubscribe( t->publisher, func, user_data );
842}
843
844void
845tr_trackerUnsubscribe( tr_tracker        * t,
846                       tr_publisher_tag    tag )
847{
848    tr_publisherUnsubscribe( t->publisher, tag );
849}
850
851const tr_tracker_info *
852tr_trackerGetAddress( const tr_tracker   * t )
853{
854    return getCurrentAddress( t );
855}
856
857int
858tr_trackerCanManualAnnounce ( const tr_tracker * t)
859{
860    return t->isRunning
861            && ( time(NULL) >= t->manualAnnounceAllowedAt );
862}
863
864void
865tr_trackerGetCounts( const tr_tracker  * t,
866                     int               * setme_completedCount,
867                     int               * setme_leecherCount,
868                     int               * setme_seederCount )
869{
870    if( setme_completedCount )
871       *setme_completedCount = t->timesDownloaded;
872
873    if( setme_leecherCount )
874       *setme_leecherCount = t->leecherCount;
875
876    if( setme_seederCount )
877       *setme_seederCount = t->seederCount;
878}
879
880void
881tr_trackerStart( tr_tracker * t )
882{
883    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
884
885    if( !t->reannounceTimer && !t->isRunning )
886    {
887        t->isRunning = 1;
888        sendTrackerRequest( t, "started" );
889    }
890}
891
892void
893tr_trackerReannounce( tr_tracker * t )
894{
895    sendTrackerRequest( t, "started" );
896}
897
898void
899tr_trackerCompleted( tr_tracker * t )
900{
901    sendTrackerRequest( t, "completed" );
902}
903
904void
905tr_trackerStop( tr_tracker * t )
906{
907    if( t->isRunning )
908    {
909        t->isRunning = 0;
910        sendTrackerRequest( t, "stopped" );
911    }
912}
913
914void
915tr_trackerChangeMyPort( tr_tracker * t )
916{
917    if( t->isRunning )
918        tr_trackerReannounce( t );
919}
Note: See TracBrowser for help on using the repository browser.