source: trunk/libtransmission/announcer.c @ 9306

Last change on this file since 9306 was 9306, checked in by charles, 13 years ago

(trunk libT) #2498: DHT broken

File size: 49.5 KB
Line 
1/*
2 * This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.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:$
11 */
12
13#include <assert.h>
14#include <limits.h>
15
16#include <event.h>
17#include <evhttp.h> /* for HTTP_OK */
18
19#include "transmission.h"
20#include "announcer.h"
21#include "crypto.h"
22#include "net.h"
23#include "ptrarray.h"
24#include "publish.h"
25#include "session.h"
26#include "tr-dht.h"
27#include "torrent.h"
28#include "utils.h"
29#include "web.h"
30
31#define dbgmsg( tier, ... ) \
32if( tr_deepLoggingIsActive( ) ) do { \
33  char name[128]; \
34  tr_snprintf( name, sizeof( name ), "[%s--%s]", tr_torrentName( tier->tor ), \
35      ( tier->currentTracker ? tier->currentTracker->host->name : "" ) ); \
36  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
37} while( 0 )
38
39enum
40{
41    /* unless the tracker says otherwise, rescrape this frequently */
42    DEFAULT_SCRAPE_INTERVAL_SEC = ( 60 * 15 ),
43
44    /* unless the tracker says otherwise, this is the announce interval */
45    DEFAULT_ANNOUNCE_INTERVAL_SEC = ( 60 * 10 ),
46
47    /* unless the tracker says otherwise, this is the announce min_interval */
48    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = ( 60 * 2 ),
49
50    /* how long to wait before a reannounce the first time we get an error */
51    FIRST_ANNOUNCE_RETRY_INTERVAL_SEC = 30,
52
53    /* how long to wait before a rescrape the first time we get an error */
54    FIRST_SCRAPE_RETRY_INTERVAL_SEC = 120,
55
56    /* the length of the 'key' argument passed in tracker requests */
57    KEYLEN = 8,
58
59    /* how many scrapes we allow at one time */
60    MAX_CONCURRENT_SCRAPES = 16,
61
62    /* how many announces we allow at one time */
63    MAX_CONCURRENT_ANNOUNCES = 48,
64
65    /* if a tracker takes more than this long to respond,
66     * we treat it as nonresponsive */
67    MAX_TRACKER_RESPONSE_TIME_SECS = ( 60 * 2 ),
68
69    /* the value of the 'numwant' argument passed in tracker requests. */
70    NUMWANT = 200,
71
72    /* how long to put slow (nonresponsive) trackers in the penalty box */
73    SLOW_HOST_PENALTY_SECS = ( 60 * 10 ),
74
75    UPKEEP_INTERVAL_SECS = 1
76
77};
78
79/***
80****
81***/
82
83static int
84compareTransfer( int a_uploaded, int a_downloaded,
85                 int b_uploaded, int b_downloaded )
86{
87    /* higher upload count goes first */
88    if( a_uploaded != b_uploaded )
89        return a_uploaded > b_uploaded ? -1 : 1;
90
91    /* then higher download count goes first */
92    if( a_downloaded != b_downloaded )
93        return a_downloaded > b_downloaded ? -1 : 1;
94
95    return 0;
96}
97
98/***
99****
100***/
101
102/**
103 * used by tr_announcer to recognize nonresponsive
104 * trackers and de-prioritize them
105 */
106typedef struct
107{
108    char * name;
109
110    /* how many seconds it took to get the last tracker response */
111    int lastResponseInterval;
112
113    /* the last time we sent an announce or scrape message */
114    time_t lastRequestTime;
115}
116tr_host;
117
118static int
119compareHosts( const void * va, const void * vb )
120{
121    const tr_host * a = va;
122    const tr_host * b = vb;
123    return strcmp( a->name, b->name );
124}
125
126static int
127compareHostToName( const void * va, const void * vb )
128{
129    const tr_host * a = va;
130    return strcmp( a->name, vb );
131}
132
133static char *
134getHostName( const char * url )
135{
136    int port = 0;
137    char * host = NULL;
138    char * ret;
139    tr_httpParseURL( url, strlen( url ), &host, &port, NULL );
140    ret = tr_strdup_printf( "%s:%d", ( host ? host : "invalid" ), port );
141    tr_free( host );
142    return ret;
143}
144
145static tr_host*
146hostNew( const char * name )
147{
148    tr_host * host = tr_new0( tr_host, 1 );
149    host->name = tr_strdup( name );
150    return host;
151}
152
153static void
154hostFree( void * vhost )
155{
156    tr_host * host = vhost;
157
158    tr_free( host->name );
159    tr_free( host );
160}
161
162/***
163****
164***/
165
166/**
167 * Since we can't poll a tr_torrent's fields after it's destroyed,
168 * we pre-build the "stop" announcement message when a torrent
169 * is removed from Transmission
170 */
171struct stop_message
172{
173    tr_host * host;
174    char * url;
175    int up;
176    int down;
177};
178
179static void
180stopFree( struct stop_message * stop )
181{
182    tr_free( stop->url );
183    tr_free( stop );
184}
185
186static int
187compareStops( const void * va, const void * vb )
188{
189    const struct stop_message * a = va;
190    const struct stop_message * b = vb;
191    return compareTransfer( a->up, a->down, b->up, b->down);
192}
193
194/***
195****
196***/
197
198/**
199 * "global" (per-tr_session) fields
200 */
201typedef struct tr_announcer
202{
203    tr_ptrArray hosts; /* tr_host */
204    tr_ptrArray stops; /* struct stop_message */
205    tr_session * session;
206    struct event * upkeepTimer;
207    int announceSlotsAvailable;
208    int scrapeSlotsAvailable;
209}
210tr_announcer;
211
212static tr_host *
213getHost( tr_announcer * announcer, const char * url )
214{
215    char * name = getHostName( url );
216    tr_host * host;
217
218    host = tr_ptrArrayFindSorted( &announcer->hosts, name, compareHostToName );
219    if( host == NULL )
220    {
221        host = hostNew( name );
222        tr_ptrArrayInsertSorted( &announcer->hosts, host, compareHosts );
223    }
224
225    tr_free( name );
226    return host;
227}
228
229static void
230onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer );
231
232void
233tr_announcerInit( tr_session * session )
234{
235    tr_announcer * a;
236
237    assert( tr_isSession( session ) );
238
239    a = tr_new0( tr_announcer, 1 );
240    a->hosts = TR_PTR_ARRAY_INIT;
241    a->stops = TR_PTR_ARRAY_INIT;
242    a->session = session;
243    a->announceSlotsAvailable = MAX_CONCURRENT_ANNOUNCES;
244    a->scrapeSlotsAvailable = MAX_CONCURRENT_SCRAPES;
245    a->upkeepTimer = tr_new0( struct event, 1 );
246    evtimer_set( a->upkeepTimer, onUpkeepTimer, a );
247    tr_timerAdd( a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
248
249    session->announcer = a;
250}
251
252static void flushCloseMessages( tr_announcer * announcer );
253
254void
255tr_announcerClose( tr_session * session )
256{
257    tr_announcer * announcer = session->announcer;
258
259    flushCloseMessages( announcer );
260
261    evtimer_del( announcer->upkeepTimer );
262    tr_free( announcer->upkeepTimer );
263    announcer->upkeepTimer = NULL;
264
265    tr_ptrArrayDestruct( &announcer->stops, NULL );
266    tr_ptrArrayDestruct( &announcer->hosts, hostFree );
267
268    session->announcer = NULL;
269    tr_free( announcer );
270}
271
272/***
273****
274***/
275
276/* a row in tr_tier's list of trackers */
277typedef struct
278{
279    tr_host * host;
280
281    char * announce;
282    char * scrape;
283
284    char * tracker_id;
285
286    int seederCount;
287    int leecherCount;
288    int downloadCount;
289    int downloaderCount;
290
291    /* sent as the "key" argument in tracker requests
292     * to verify us if our IP address changes.
293     * This is immutable for the life of the tracker object.
294     * The +1 is for '\0' */
295    char key_param[KEYLEN + 1];
296}
297tr_tracker_item;
298
299static void
300generateKeyParam( char * msg, size_t msglen )
301{
302    size_t i;
303    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
304    const int poolSize = strlen( pool );
305
306    for( i=0; i<msglen; ++i )
307        *msg++ = pool[tr_cryptoRandInt( poolSize )];
308    *msg = '\0';
309}
310
311static tr_tracker_item*
312trackerNew( tr_announcer  * announcer,
313            const char    * announce,
314            const char    * scrape )
315{
316    tr_tracker_item * tracker = tr_new0( tr_tracker_item, 1  );
317    tracker->host = getHost( announcer, announce );
318    tracker->announce = tr_strdup( announce );
319    tracker->scrape = tr_strdup( scrape );
320    generateKeyParam( tracker->key_param, KEYLEN );
321    tracker->seederCount = -1;
322    tracker->leecherCount = -1;
323    tracker->downloadCount = -1;
324    return tracker;
325}
326
327static void
328trackerFree( void * vtracker )
329{
330    tr_tracker_item * tracker = vtracker;
331
332    tr_free( tracker->tracker_id );
333    tr_free( tracker->announce );
334    tr_free( tracker->scrape );
335    tr_free( tracker );
336}
337
338/***
339****
340***/
341
342struct tr_torrent_tiers;
343
344/**
345 * A group of trackers in a single tier,
346 * as per the multitracker spec
347 */
348typedef struct
349{
350    tr_ptrArray trackers; /* tr_tracker_item */
351    tr_tracker_item * currentTracker;
352
353    tr_torrent * tor;
354
355    time_t lastScrapeTime;
356    time_t lastAnnounceTime;
357    time_t lastScrapeStartTime;
358    time_t lastAnnounceStartTime;
359    tr_bool lastScrapeSucceeded;
360    tr_bool lastAnnounceSucceeded;
361
362    time_t scrapeAt;
363    time_t manualAnnounceAllowedAt;
364
365    time_t announceAt;
366    const char * announceEvent;
367
368    /* unique lookup key */
369    int key;
370
371    int currentTrackerIndex;
372    int scrapeIntervalSec;
373    int announceIntervalSec;
374    int announceMinIntervalSec;
375    int retryAnnounceIntervalSec;
376    int retryScrapeIntervalSec;
377
378    int lastAnnouncePeerCount;
379
380    tr_bool isRunning;
381    tr_bool isAnnouncing;
382    tr_bool isScraping;
383
384    char lastAnnounceStr[128];
385    char lastScrapeStr[128];
386}
387tr_tier;
388
389static tr_tier *
390tierNew( tr_torrent * tor )
391{
392    tr_tier * t;
393    static int nextKey = 1;
394    const time_t now = time( NULL );
395
396    t = tr_new0( tr_tier, 1 );
397    t->key = nextKey++;
398    t->trackers = TR_PTR_ARRAY_INIT;
399    t->currentTracker = NULL;
400    t->currentTrackerIndex = -1;
401    t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
402    t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
403    t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
404    t->scrapeAt = now;
405    t->tor = tor;
406
407    return t;
408}
409
410static void
411tierFree( void * vtier )
412{
413    tr_tier * tier = vtier;
414    tr_ptrArrayDestruct( &tier->trackers, trackerFree );
415    tr_free( tier );
416}
417
418static void
419tierIncrementTracker( tr_tier * tier )
420{
421    const int i = ( tier->currentTrackerIndex + 1 )
422                        % tr_ptrArraySize( &tier->trackers );
423    tier->currentTracker = tr_ptrArrayNth( &tier->trackers, i );
424    tier->currentTrackerIndex = i;
425
426    /* reset some of the tier's fields */
427    tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
428    tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
429    tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
430    tier->retryAnnounceIntervalSec = FIRST_ANNOUNCE_RETRY_INTERVAL_SEC;
431    tier->retryScrapeIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
432    tier->isAnnouncing = FALSE;
433    tier->isScraping = FALSE;
434    tier->lastAnnounceStartTime = 0;
435    tier->lastScrapeStartTime = 0;
436}
437
438static void
439tierAddTracker( tr_announcer * announcer,
440                tr_tier      * tier,
441                const char   * announce,
442                const char   * scrape )
443{
444    tr_tracker_item * tracker = trackerNew( announcer, announce, scrape );
445
446    tr_ptrArrayAppend( &tier->trackers, tracker );
447    dbgmsg( tier, "adding tracker %s", announce );
448
449    if( !tier->currentTracker )
450        tierIncrementTracker( tier );
451}
452
453
454/***
455****
456***/
457
458/*
459 * per-torrent info.
460 * this opaque data structure can be found in tr_torrent.tiers
461 */
462typedef struct tr_torrent_tiers
463{
464    tr_ptrArray tiers;
465    tr_publisher publisher;
466}
467tr_torrent_tiers;
468
469static tr_torrent_tiers*
470tiersNew( void )
471{
472    tr_torrent_tiers * tiers = tr_new0( tr_torrent_tiers, 1 );
473    tiers->tiers = TR_PTR_ARRAY_INIT;
474    tiers->publisher = TR_PUBLISHER_INIT;
475    return tiers;
476}
477
478static void
479tiersClear( tr_torrent_tiers * tiers )
480{
481    tr_ptrArrayDestruct( &tiers->tiers, tierFree );
482    tiers->tiers = TR_PTR_ARRAY_INIT;
483}
484
485static void
486tiersFree( tr_torrent_tiers * tiers )
487{
488    tr_publisherDestruct( &tiers->publisher );
489    tr_ptrArrayDestruct( &tiers->tiers, tierFree );
490    tr_free( tiers );
491}
492
493static tr_tier*
494getTier( tr_announcer * announcer, int torrentId, int tierId )
495{
496    tr_tier * tier = NULL;
497
498    if( announcer )
499    {
500        tr_torrent * tor = tr_torrentFindFromId( announcer->session, torrentId );
501
502        if( tor && tor->tiers )
503        {
504            int i;
505            tr_ptrArray * tiers = &tor->tiers->tiers;
506            const int n = tr_ptrArraySize( tiers );
507            for( i=0; !tier && i<n; ++i )
508            {
509                tr_tier * tmp = tr_ptrArrayNth( tiers, i );
510                if( tmp->key == tierId )
511                    tier = tmp;
512            }
513        }
514    }
515
516    return tier;
517}
518
519/***
520****  PUBLISH
521***/
522
523static const tr_tracker_event emptyEvent = { 0, NULL, NULL, 0, 0 };
524
525static void
526publishMessage( tr_tier * tier, const char * msg, int type )
527{
528    if( tier && tier->tor && tier->tor->tiers )
529    {
530        tr_torrent_tiers * tiers = tier->tor->tiers;
531        tr_tracker_event event = emptyEvent;
532        event.messageType = type;
533        event.text = msg;
534        tr_publisherPublish( &tiers->publisher, tier, &event );
535    }
536}
537
538static void
539publishErrorClear( tr_tier * tier )
540{
541    publishMessage( tier, NULL, TR_TRACKER_ERROR_CLEAR );
542}
543
544static void
545publishErrorMessageAndStop( tr_tier * tier, const char * msg )
546{
547    tier->isRunning = FALSE;
548
549    publishMessage( tier, msg, TR_TRACKER_ERROR );
550}
551
552static void
553publishWarning( tr_tier * tier, const char * msg )
554{
555    publishMessage( tier, msg, TR_TRACKER_WARNING );
556}
557
558static int
559publishNewPeers( tr_tier * tier, tr_bool allAreSeeds,
560                 const void * compact, int compactLen )
561{
562    tr_tracker_event e = emptyEvent;
563
564    e.messageType = TR_TRACKER_PEERS;
565    e.allAreSeeds = allAreSeeds;
566    e.compact = compact;
567    e.compactLen = compactLen;
568
569    if( compactLen )
570        tr_publisherPublish( &tier->tor->tiers->publisher, tier, &e );
571
572    return compactLen / 6;
573}
574
575static int
576publishNewPeersCompact( tr_tier * tier, tr_bool allAreSeeds,
577                        const void * compact, int compactLen )
578{
579    int i;
580    const uint8_t *compactWalk;
581    uint8_t *array, *walk;
582    const int peerCount = compactLen / 6;
583    const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 );
584    tr_address addr;
585    tr_port port;
586
587    addr.type = TR_AF_INET;
588    memset( &addr.addr, 0, sizeof( addr.addr ) );
589    array = tr_new( uint8_t, arrayLen );
590    for ( i=0, walk=array, compactWalk=compact ; i<peerCount ; ++i )
591    {
592        memcpy( &addr.addr.addr4, compactWalk, 4 );
593        memcpy( &port, compactWalk + 4, 2 );
594
595        memcpy( walk, &addr, sizeof( addr ) );
596        memcpy( walk + sizeof( addr ), &port, 2 );
597
598        walk += sizeof( tr_address ) + 2;
599        compactWalk += 6;
600    }
601
602    publishNewPeers( tier, allAreSeeds, array, arrayLen );
603
604    tr_free( array );
605
606    return peerCount;
607}
608
609static int
610publishNewPeersCompact6( tr_tier * tier, tr_bool allAreSeeds,
611                         const void * compact, int compactLen )
612{
613    int i;
614    const uint8_t *compactWalk;
615    uint8_t *array, *walk;
616    const int peerCount = compactLen / 18;
617    const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 );
618    tr_address addr;
619    tr_port port;
620
621    addr.type = TR_AF_INET6;
622    memset( &addr.addr, 0, sizeof( addr.addr ) );
623    array = tr_new( uint8_t, arrayLen );
624    for ( i = 0, walk = array, compactWalk = compact ; i < peerCount ; ++i )
625    {
626        memcpy( &addr.addr.addr6, compactWalk, 16 );
627        memcpy( &port, compactWalk + 16, 2 );
628        compactWalk += 18;
629
630        memcpy( walk, &addr, sizeof( addr ) );
631        memcpy( walk + sizeof( addr ), &port, 2 );
632        walk += sizeof( tr_address ) + 2;
633    }
634    publishNewPeers( tier, allAreSeeds, array, arrayLen );
635    tr_free( array );
636
637    return peerCount;
638}
639
640static char*
641createAnnounceURL( const tr_announcer     * announcer,
642                   const tr_torrent       * torrent,
643                   const tr_tier          * tier,
644                   const char             * eventName )
645{
646    const int isStopping = !strcmp( eventName, "stopped" );
647    const int numwant = isStopping ? 0 : NUMWANT;
648    const tr_tracker_item  * tracker = tier->currentTracker;
649    const char * ann = tracker->announce;
650    struct evbuffer * buf = evbuffer_new( );
651    char * ret;
652
653    evbuffer_add_printf( buf, "%s"
654                              "%c"
655                              "info_hash=%s"
656                              "&peer_id=%s"
657                              "&port=%d"
658                              "&uploaded=%" PRIu64
659                              "&downloaded=%" PRIu64
660                              "&left=%" PRIu64
661                              "&numwant=%d"
662                              "&key=%s"
663                              "&compact=1"
664                              "&supportcrypto=1",
665                              ann,
666                              strchr( ann, '?' ) ? '&' : '?',
667                              torrent->info.hashEscaped,
668                              torrent->peer_id,
669                              tr_sessionGetPeerPort( announcer->session ),
670                              torrent->uploadedCur,
671                              torrent->downloadedCur,
672                              tr_cpLeftUntilComplete( &torrent->completion ),
673                              numwant,
674                              tracker->key_param );
675
676    if( torrent->corruptCur )
677        evbuffer_add_printf( buf, "&corrupt=%" PRIu64, torrent->corruptCur );
678
679    if( !isStopping )
680        evbuffer_add_printf( buf, "&upload_only=%d", tr_torrentIsSeed( torrent ) ? 1 : 0 );
681
682    if( announcer->session->encryptionMode == TR_ENCRYPTION_REQUIRED )
683        evbuffer_add_printf( buf, "&requirecrypto=1" );
684
685    if( eventName && *eventName )
686        evbuffer_add_printf( buf, "&event=%s", eventName );
687
688    if( tracker->tracker_id && *tracker->tracker_id )
689        evbuffer_add_printf( buf, "&trackerid=%s", tracker->tracker_id );
690
691    ret = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
692    evbuffer_free( buf );
693
694    dbgmsg( tier, "announce URL is \"%s\"", ret );
695    return ret;
696}
697
698
699/***
700****
701***/
702
703static tr_bool
704announceURLIsSupported( const char * announce )
705{
706    return ( announce != NULL )
707        && ( ( strstr( announce, "http://" ) == announce ) ||
708             ( strstr( announce, "https://" ) == announce ) );
709}
710
711static void
712addTorrentToTier( tr_announcer * announcer, tr_torrent_tiers * tiers, tr_torrent * tor )
713{
714    int i, n;
715    const tr_tracker_info ** infos;
716    const int trackerCount = tor->info.trackerCount;
717    const tr_tracker_info  * trackers = tor->info.trackers;
718
719    /* get the trackers that we support... */
720    infos = tr_new0( const tr_tracker_info*, trackerCount );
721    for( i=n=0; i<trackerCount; ++i )
722        if( announceURLIsSupported( trackers[i].announce ) )
723            infos[n++] = &trackers[i];
724
725    /* build our private table of tiers... */
726    if( n > 0 )
727    {
728        int tierIndex = -1;
729        tr_tier * tier = NULL;
730
731        for( i=0; i<trackerCount; ++i )
732        {
733            const tr_tracker_info * info = infos[i];
734
735            if( info->tier != tierIndex )
736                tier = NULL;
737
738            tierIndex = info->tier;
739
740            if( tier == NULL ) {
741                tier = tierNew( tor );
742                dbgmsg( tier, "adding tier" );
743                tr_ptrArrayAppend( &tiers->tiers, tier );
744            }
745
746            tierAddTracker( announcer, tier, info->announce, info->scrape );
747        }
748    }
749
750    tr_free( infos );
751}
752
753tr_torrent_tiers *
754tr_announcerAddTorrent( tr_announcer * announcer, tr_torrent * tor )
755{
756    tr_torrent_tiers * tiers;
757
758    assert( announcer != NULL );
759    assert( tr_isTorrent( tor ) );
760
761    tiers = tiersNew( );
762
763    addTorrentToTier( announcer, tiers, tor );
764
765    return tiers;
766}
767
768void
769tr_announcerResetTorrent( tr_announcer * announcer, tr_torrent * tor )
770{
771    if( tor->tiers != NULL )
772    {
773        tiersClear( tor->tiers );
774
775        addTorrentToTier( announcer, tor->tiers, tor );
776
777        if( tor->isRunning )
778            tr_announcerTorrentStarted( tor );
779    }
780}
781
782tr_publisher_tag
783tr_announcerSubscribe( struct tr_torrent_tiers   * tiers,
784                       tr_delivery_func            func,
785                       void                      * userData )
786{
787    return tr_publisherSubscribe( &tiers->publisher, func, userData );
788}
789
790void
791tr_announcerUnsubscribe( struct tr_torrent_tiers  * tiers,
792                         tr_publisher_tag           tag )
793{
794    if( tiers )
795        tr_publisherUnsubscribe( &tiers->publisher, tag );
796}
797
798static tr_bool
799tierCanManualAnnounce( const tr_tier * tier )
800{
801    return tier->isRunning
802        && tier->manualAnnounceAllowedAt <= time( NULL );
803}
804
805tr_bool
806tr_announcerCanManualAnnounce( const tr_torrent * tor )
807{
808    int i;
809    int n;
810    const tr_tier ** tiers;
811
812    assert( tr_isTorrent( tor ) );
813    assert( tor->tiers != NULL );
814
815    n = tr_ptrArraySize( &tor->tiers->tiers );
816    tiers = (const tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
817    for( i=0; i<n; ++i )
818        if( tierCanManualAnnounce( tiers[i] ) )
819            return TRUE;
820
821    return FALSE;
822}
823
824time_t
825tr_announcerNextManualAnnounce( const tr_torrent * tor )
826{
827    int i;
828    int n;
829    const tr_torrent_tiers * tiers;
830    time_t ret = ~(time_t)0;
831
832    assert( tr_isTorrent( tor  ) );
833
834    tiers = tor->tiers;
835    n = tr_ptrArraySize( &tiers->tiers );
836    for( i=0; i<n; ++i ) {
837        tr_tier * tier = tr_ptrArrayNth( (tr_ptrArray*)&tiers->tiers, i );
838        if( tier->isRunning )
839            ret = MIN( ret, tier->manualAnnounceAllowedAt );
840    }
841
842    return ret;
843}
844
845static void
846tierClearNextAnnounce( tr_tier * tier )
847{
848    tier->announceAt = 0;
849    tier->announceEvent = NULL;
850    dbgmsg( tier, "cleared out announceEvent -- no announces pending." );
851}
852
853static void
854tierSetNextAnnounce( tr_tier * tier, const char * announceEvent, time_t announceAt )
855{
856    assert( tier != NULL );
857    assert( announceEvent != NULL );
858
859    if( !strcmp( announceEvent, "manual" ) && !tierCanManualAnnounce( tier ) )
860        return;
861
862    tier->announceAt = announceAt;
863    tier->announceEvent = announceEvent;
864    dbgmsg( tier, "next announce event is \"%s\" in %d seconds\n", announceEvent, (int)difftime(announceAt,time(NULL)) );
865}
866
867static void
868torrentSetNextAnnounce( tr_torrent * tor, const char * announceEvent, time_t announceAt )
869{
870    int i;
871    int n;
872    tr_torrent_tiers * tiers;
873
874    assert( tr_isTorrent( tor ) );
875
876    tiers = tor->tiers;
877    n = tr_ptrArraySize( &tiers->tiers );
878    for( i=0; i<n; ++i )
879        tierSetNextAnnounce( tr_ptrArrayNth( &tiers->tiers, i ), announceEvent, announceAt );
880}
881
882void
883tr_announcerManualAnnounce( tr_torrent * tor )
884{
885    torrentSetNextAnnounce( tor, "manual", time( NULL ) );
886}
887void
888tr_announcerTorrentStarted( tr_torrent * tor )
889{
890    torrentSetNextAnnounce( tor, "started", time( NULL ) );
891}
892void
893tr_announcerTorrentStopped( tr_torrent * tor )
894{
895    torrentSetNextAnnounce( tor, "stopped", time( NULL ) );
896}
897void
898tr_announcerTorrentCompleted( tr_torrent * tor )
899{
900    torrentSetNextAnnounce( tor, "completed", time( NULL ) );
901}
902void
903tr_announcerChangeMyPort( tr_torrent * tor )
904{
905    tr_announcerTorrentStarted( tor );
906}
907
908
909void
910tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor )
911{
912    assert( tr_isTorrent( tor ) );
913
914    if( tor->tiers )
915    {
916        int i;
917        const int n = tr_ptrArraySize( &tor->tiers->tiers );
918        for( i=0; i<n; ++i )
919        {
920            tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
921
922            if( tier->isRunning )
923            {
924                struct stop_message * s = tr_new0( struct stop_message, 1 );
925                s->up = tor->uploadedCur;
926                s->down = tor->downloadedCur;
927                s->url = createAnnounceURL( announcer, tor, tier, "stopped" );
928                s->host = tier->currentTracker->host;
929                tr_ptrArrayInsertSorted( &announcer->stops, s, compareStops );
930            }
931        }
932
933        tiersFree( tor->tiers );
934        tor->tiers = NULL;
935    }
936}
937
938/* return true if (1) we've tried it recently AND (2) it didn't respond... */
939static tr_bool
940hostIsNotResponding( const tr_host * host, const time_t now )
941{
942    tr_bool b = ( host->lastRequestTime )
943             && ( host->lastRequestTime >= ( now - SLOW_HOST_PENALTY_SECS ) )
944             && ( host->lastResponseInterval > MAX_TRACKER_RESPONSE_TIME_SECS );
945    return b;
946}
947
948static tr_bool
949tierIsNotResponding( const tr_tier * tier, const time_t now )
950{
951    return !tier->currentTracker
952        || hostIsNotResponding( tier->currentTracker->host, now );
953}
954
955static int
956compareTiers( const void * va, const void * vb )
957{
958    int ret = 0;
959    tr_bool af, bf;
960    const tr_tier * a = *(const tr_tier**)va;
961    const tr_tier * b = *(const tr_tier**)vb;
962
963    /* working domains come before non-working */
964    if( !ret ) {
965        const time_t now = time( NULL );
966        af = tierIsNotResponding( a, now );
967        bf = tierIsNotResponding( b, now );
968        if( af != bf )
969            ret = !af ? -1 : 1;
970    }
971
972    /* stops come before starts */
973    if( !ret ) {
974        af = a->tor->isRunning;
975        bf = b->tor->isRunning;
976        if( af != bf )
977            ret = af ? 1 : -1;
978    }
979
980    /* upload comes before download */
981    if( !ret )
982        ret = compareTransfer( a->tor->uploadedCur, a->tor->downloadedCur,
983                               b->tor->uploadedCur, b->tor->downloadedCur );
984
985    /* incomplete comes before complete */
986    if( !ret ) {
987        af = a->tor->completeness == TR_LEECH;
988        bf = b->tor->completeness == TR_LEECH;
989        if( af != bf )
990            return af ? -1 : 1;
991    }
992
993    /* private before public */
994    if( !ret ) {
995        af = tr_torrentIsPrivate( a->tor );
996        bf = tr_torrentIsPrivate( b->tor );
997        if( af != bf )
998            ret = af ? -1 : 1;
999    }
1000
1001    return ret;
1002}
1003
1004static uint8_t *
1005parseOldPeers( tr_benc * bePeers, size_t *  byteCount )
1006{
1007    int       i;
1008    uint8_t * array, *walk;
1009    const int peerCount = bePeers->val.l.count;
1010
1011    assert( tr_bencIsList( bePeers ) );
1012
1013    array = tr_new( uint8_t, peerCount * ( sizeof( tr_address ) + 2 ) );
1014
1015    for( i = 0, walk = array; i < peerCount; ++i )
1016    {
1017        const char * s;
1018        int64_t      itmp;
1019        tr_address   addr;
1020        tr_port      port;
1021        tr_benc    * peer = &bePeers->val.l.vals[i];
1022
1023        if( tr_bencDictFindStr( peer, "ip", &s ) )
1024            if( tr_pton( s, &addr ) == NULL )
1025                continue;
1026
1027        if( !tr_bencDictFindInt( peer, "port", &itmp )
1028                || itmp < 0
1029                || itmp > USHRT_MAX )
1030            continue;
1031
1032        memcpy( walk, &addr, sizeof( tr_address ) );
1033        port = htons( itmp );
1034        memcpy( walk + sizeof( tr_address ), &port, 2 );
1035        walk += sizeof( tr_address ) + 2;
1036    }
1037
1038    *byteCount = peerCount * sizeof( tr_address ) + 2;
1039    return array;
1040}
1041
1042static tr_bool
1043parseAnnounceResponse( tr_tier     * tier,
1044                       const char  * response,
1045                       size_t        responseLen,
1046                       tr_bool     * gotScrape )
1047{
1048    tr_benc benc;
1049    tr_bool success = FALSE;
1050    int scrapeFields = 0;
1051    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1052
1053    dbgmsg( tier, "response len: %d, isBenc: %d", (int)responseLen, (int)bencLoaded );
1054    publishErrorClear( tier );
1055    if( bencLoaded && tr_bencIsDict( &benc ) )
1056    {
1057        int incomplete = -1;
1058        size_t rawlen;
1059        int64_t i;
1060        tr_benc * tmp;
1061        const char * str;
1062        const uint8_t * raw;
1063
1064        success = TRUE;
1065
1066        tier->retryAnnounceIntervalSec = FIRST_ANNOUNCE_RETRY_INTERVAL_SEC;
1067
1068        if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
1069        {
1070            tr_strlcpy( tier->lastAnnounceStr, str,
1071                        sizeof( tier->lastAnnounceStr ) );
1072            dbgmsg( tier, "tracker gave \"%s\"", str );
1073            publishMessage( tier, str, TR_TRACKER_ERROR );
1074            success = FALSE;
1075        }
1076
1077        if( tr_bencDictFindStr( &benc, "warning message", &str ) )
1078        {
1079            tr_strlcpy( tier->lastAnnounceStr, str,
1080                        sizeof( tier->lastAnnounceStr ) );
1081            dbgmsg( tier, "tracker gave \"%s\"", str );
1082            publishWarning( tier, str );
1083        }
1084
1085        if( tr_bencDictFindInt( &benc, "interval", &i ) )
1086        {
1087            dbgmsg( tier, "setting interval to %d", (int)i );
1088            tier->announceIntervalSec = i;
1089        }
1090
1091        if( tr_bencDictFindInt( &benc, "min interval", &i ) )
1092        {
1093            dbgmsg( tier, "setting min interval to %d", (int)i );
1094            tier->announceMinIntervalSec = i;
1095        }
1096
1097        if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
1098        {
1099            tier->currentTracker->tracker_id = tr_strdup( str );
1100        }
1101
1102        if( tr_bencDictFindInt( &benc, "complete", &i ) )
1103        {
1104            ++scrapeFields;
1105            tier->currentTracker->seederCount = i;
1106        }
1107
1108        if( tr_bencDictFindInt( &benc, "incomplete", &i ) )
1109        {
1110            ++scrapeFields;
1111            tier->currentTracker->leecherCount = incomplete = i;
1112        }
1113
1114        if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
1115        {
1116            ++scrapeFields;
1117            tier->currentTracker->downloadCount = i;
1118        }
1119
1120        if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) )
1121        {
1122            /* "compact" extension */
1123            const int allAreSeeds = incomplete == 0;
1124            tier->lastAnnouncePeerCount = publishNewPeersCompact( tier, allAreSeeds, raw, rawlen );
1125        }
1126        else if( tr_bencDictFindList( &benc, "peers", &tmp ) )
1127        {
1128            /* original version of peers */
1129            const tr_bool allAreSeeds = incomplete == 0;
1130            size_t byteCount = 0;
1131            uint8_t * array = parseOldPeers( tmp, &byteCount );
1132            tier->lastAnnouncePeerCount = publishNewPeers( tier, allAreSeeds, array, byteCount );
1133            tr_free( array );
1134        }
1135
1136        if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) )
1137        {
1138            /* "compact" extension */
1139            const tr_bool allAreSeeds = incomplete == 0;
1140            tier->lastAnnouncePeerCount += publishNewPeersCompact6( tier, allAreSeeds, raw, rawlen );
1141        }
1142
1143        if( tier->lastAnnounceStr[0] == '\0' )
1144            tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
1145                        sizeof( tier->lastAnnounceStr ) );
1146    }
1147
1148    if( bencLoaded )
1149        tr_bencFree( &benc );
1150
1151    *gotScrape = scrapeFields >= 2;
1152
1153    return success;
1154}
1155
1156struct announce_data
1157{
1158    int torrentId;
1159    int tierId;
1160    time_t timeSent;
1161
1162    /** If the request succeeds, the value for tier's "isRunning" flag */
1163    tr_bool isRunningOnSuccess;
1164};
1165
1166static void
1167onAnnounceDone( tr_session   * session,
1168                long           responseCode,
1169                const void   * response,
1170                size_t         responseLen,
1171                void         * vdata )
1172{
1173    tr_announcer * announcer = session->announcer;
1174    struct announce_data * data = vdata;
1175    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1176    tr_bool gotScrape = FALSE;
1177    tr_bool success = FALSE;
1178    const time_t now = time ( NULL );
1179
1180    if( announcer && tier )
1181    {
1182        tier->lastAnnouncePeerCount = 0;
1183
1184        if( tier->currentTracker->host )
1185        {
1186            tr_host * host = tier->currentTracker->host;
1187            host->lastRequestTime = data->timeSent;
1188            host->lastResponseInterval = now - data->timeSent;
1189        }
1190
1191        if( responseCode == HTTP_OK )
1192        {
1193            tier->lastAnnounceTime = now;
1194            success = parseAnnounceResponse( tier, response, responseLen, &gotScrape );
1195            dbgmsg( tier, "success is %d", success );
1196        }
1197        else if( responseCode )
1198        {
1199            /* %1$ld - http status code, such as 404
1200             * %2$s - human-readable explanation of the http status code */
1201            char * buf = tr_strdup_printf(
1202                _( "Announce failed: tracker gave HTTP Response Code %1$ld (%2$s)" ),
1203                responseCode,
1204                tr_webGetResponseStr( responseCode ) );
1205
1206            tr_strlcpy( tier->lastAnnounceStr, buf,
1207                        sizeof( tier->lastAnnounceStr ) );
1208
1209            /* if the repsonse may require intervention, notify the user;
1210             * otherwise, just log it */
1211            if( responseCode >= 400 )
1212                publishWarning( tier, buf );
1213            tr_torinf( tier->tor, "%s", buf );
1214            dbgmsg( tier, "%s", buf );
1215
1216            tr_free( buf );
1217        }
1218        else
1219        {
1220            tr_strlcpy( tier->lastAnnounceStr,
1221                        _( "tracker did not respond" ),
1222                        sizeof( tier->lastAnnounceStr ) );
1223            dbgmsg( tier, "%s", tier->lastAnnounceStr );
1224        }
1225    }
1226
1227    if( tier )
1228    {
1229        tier->isAnnouncing = FALSE;
1230
1231        if( responseCode == 0 )
1232        {
1233            dbgmsg( tier, "No response from tracker... retrying in two minutes." );
1234            tier->manualAnnounceAllowedAt = ~(time_t)0;
1235            tierSetNextAnnounce( tier, tier->announceEvent, now + 120 );
1236        }
1237        else if( 200 <= responseCode && responseCode <= 299 )
1238        {
1239            const int interval = tier->announceIntervalSec;
1240            dbgmsg( tier, "request succeeded. reannouncing in %d seconds", interval );
1241
1242            if( gotScrape )
1243            {
1244                tier->lastScrapeTime = now;
1245                tier->scrapeAt = now + tier->scrapeIntervalSec;
1246            }
1247
1248            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1249
1250            if( strcmp( tier->announceEvent, "stopped" ) == 0 )
1251                tierClearNextAnnounce( tier );
1252            else
1253                tierSetNextAnnounce( tier, "", now + interval );
1254        }
1255        else if( 300 <= responseCode && responseCode <= 399 )
1256        {
1257            /* how did this get here?  libcurl handles this */
1258            const int interval = 5;
1259            dbgmsg( tier, "got a redirect. retrying in %d seconds", interval );
1260            tierSetNextAnnounce( tier, tier->announceEvent, now + interval );
1261            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1262        }
1263        else if( 400 <= responseCode && responseCode <= 499 )
1264        {
1265            /* The request could not be understood by the server due to
1266             * malformed syntax. The client SHOULD NOT repeat the
1267             * request without modifications. */
1268            publishErrorMessageAndStop( tier, _( "Tracker returned a 4xx message" ) );
1269            tier->manualAnnounceAllowedAt = ~(time_t)0;
1270        }
1271        else if( 500 <= responseCode && responseCode <= 599 )
1272        {
1273            /* Response status codes beginning with the digit "5" indicate
1274             * cases in which the server is aware that it has erred or is
1275             * incapable of performing the request.  So we pause a bit and
1276             * try again. */
1277            tier->manualAnnounceAllowedAt = ~(time_t)0;
1278            tierSetNextAnnounce( tier, tier->announceEvent, now + tier->retryAnnounceIntervalSec );
1279            tier->retryAnnounceIntervalSec *= 2;
1280        }
1281        else
1282        {
1283            /* WTF did we get?? */
1284            const int interval = 120;
1285            dbgmsg( tier, "Invalid response from tracker... retrying in two minutes." );
1286            tier->manualAnnounceAllowedAt = ~(time_t)0;
1287            tierSetNextAnnounce( tier, tier->announceEvent, now + interval );
1288        }
1289
1290        tier->lastAnnounceSucceeded = success;
1291
1292        if( success )
1293            tier->isRunning = data->isRunningOnSuccess;
1294
1295        if( !success )
1296            tierIncrementTracker( tier );
1297    }
1298
1299    if( announcer != NULL )
1300    {
1301        ++announcer->announceSlotsAvailable;
1302    }
1303
1304    tr_free( data );
1305}
1306
1307static const char *
1308getAnnounceEvent( const tr_tier * tier )
1309{
1310    const char * event;
1311
1312    assert( tier != NULL );
1313    assert( tier->announceEvent != NULL );
1314    assert( tr_isTorrent( tier->tor ) );
1315
1316    /* BEP 21: "In order to tell the tracker that a peer is a partial seed,
1317     * it MUST send an event=paused parameter in every announce while
1318     * it is a partial seed." */
1319    if( tr_cpGetStatus( &tier->tor->completion ) == TR_PARTIAL_SEED )
1320        return "paused";
1321
1322    event = tier->announceEvent;
1323
1324    if( !strcmp( event, "manual" ) )
1325        return "started";
1326
1327    return event;
1328}
1329
1330static void
1331tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1332{
1333    char * url;
1334    struct announce_data * data;
1335    const tr_torrent * tor = tier->tor;
1336    const time_t now = time( NULL );
1337
1338    assert( !tier->isAnnouncing );
1339
1340    data = tr_new0( struct announce_data, 1 );
1341    data->torrentId = tr_torrentId( tor );
1342    data->tierId = tier->key;
1343    data->isRunningOnSuccess = tor->isRunning;
1344    data->timeSent = now;
1345
1346    url = createAnnounceURL( announcer, tor, tier, getAnnounceEvent( tier ) );
1347
1348    tier->isAnnouncing = TRUE;
1349    tier->lastAnnounceStartTime = now;
1350    --announcer->announceSlotsAvailable;
1351    tr_webRun( announcer->session, url, NULL, onAnnounceDone, data );
1352
1353    tr_free( url );
1354}
1355
1356static tr_bool
1357parseScrapeResponse( tr_tier     * tier,
1358                     const char  * response,
1359                     size_t        responseLen,
1360                     char        * result,
1361                     size_t        resultlen )
1362{
1363    tr_bool success = FALSE;
1364    tr_benc benc, *files;
1365    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1366    if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) )
1367    {
1368        const char * key;
1369        tr_benc * val;
1370        int i = 0;
1371        while( tr_bencDictChild( files, i++, &key, &val ))
1372        {
1373            int64_t intVal;
1374            tr_benc * flags;
1375
1376            if( memcmp( tier->tor->info.hash, key, SHA_DIGEST_LENGTH ) )
1377                continue;
1378
1379            success = TRUE;
1380            publishErrorClear( tier );
1381
1382            if( ( tr_bencDictFindInt( val, "complete", &intVal ) ) )
1383                tier->currentTracker->seederCount = intVal;
1384
1385            if( ( tr_bencDictFindInt( val, "incomplete", &intVal ) ) )
1386                tier->currentTracker->leecherCount = intVal;
1387
1388            if( ( tr_bencDictFindInt( val, "downloaded", &intVal ) ) )
1389                tier->currentTracker->downloadCount = intVal;
1390
1391            if( ( tr_bencDictFindInt( val, "downloaders", &intVal ) ) )
1392                tier->currentTracker->downloaderCount = intVal;
1393
1394            if( tr_bencDictFindDict( val, "flags", &flags ) )
1395                if( ( tr_bencDictFindInt( flags, "min_request_interval", &intVal ) ) )
1396                    tier->scrapeIntervalSec = intVal;
1397
1398            /* as per ticket #1045, safeguard against trackers returning
1399             * a very low min_request_interval... */
1400            if( tier->scrapeIntervalSec < DEFAULT_SCRAPE_INTERVAL_SEC )
1401                tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
1402
1403            tr_tordbg( tier->tor,
1404                       "Scrape successful. Rescraping in %d seconds.",
1405                       tier->scrapeIntervalSec );
1406
1407            tier->retryScrapeIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
1408        }
1409    }
1410
1411    if( bencLoaded )
1412        tr_bencFree( &benc );
1413
1414    if( success )
1415        tr_strlcpy( result, _( "Success" ), resultlen );
1416    else
1417        tr_strlcpy( result, _( "Error parsing response" ), resultlen );
1418
1419    return success;
1420}
1421
1422static void
1423onScrapeDone( tr_session   * session,
1424              long           responseCode,
1425              const void   * response,
1426              size_t         responseLen,
1427              void         * vdata )
1428{
1429    tr_announcer * announcer = session->announcer;
1430    struct announce_data * data = vdata;
1431    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1432    const time_t now = time( NULL );
1433    tr_bool success = FALSE;
1434
1435    if( announcer )
1436        ++announcer->scrapeSlotsAvailable;
1437
1438    if( announcer && tier )
1439    {
1440        tier->isScraping = FALSE;
1441        tier->lastScrapeTime = now;
1442   
1443        if( tier->currentTracker->host )
1444        {
1445            tr_host * host = tier->currentTracker->host;
1446            host->lastRequestTime = data->timeSent;
1447            host->lastResponseInterval = now - data->timeSent;
1448        }
1449
1450        if( 200 <= responseCode && responseCode <= 299 )
1451        {
1452            const int interval = tier->scrapeIntervalSec;
1453            tier->scrapeAt = now + interval;
1454
1455            if( responseCode == HTTP_OK )
1456                success = parseScrapeResponse( tier, response, responseLen,
1457                                               tier->lastScrapeStr, sizeof( tier->lastScrapeStr ) );
1458            else
1459                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1460                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1461                             responseCode, tr_webGetResponseStr( responseCode ) );
1462            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1463        }
1464        else if( 300 <= responseCode && responseCode <= 399 )
1465        {
1466            /* this shouldn't happen; libcurl should handle this */
1467            const int interval = 5;
1468            tier->scrapeAt = now + interval;
1469            tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1470                         "Got a redirect. Retrying in %d seconds", interval );
1471            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1472        }
1473        else
1474        {
1475            const int interval = tier->retryScrapeIntervalSec;
1476            tier->retryScrapeIntervalSec *= 2;
1477            tier->scrapeAt = now + interval;
1478
1479            /* %1$ld - http status code, such as 404
1480             * %2$s - human-readable explanation of the http status code */
1481            if( !responseCode )
1482                tr_strlcpy( tier->lastScrapeStr, _( "tracker did not respond" ),
1483                            sizeof( tier->lastScrapeStr ) );
1484            else
1485                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1486                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1487                             responseCode, tr_webGetResponseStr( responseCode ) );
1488        }
1489
1490        tier->lastScrapeSucceeded = success;
1491    }
1492
1493    tr_free( data );
1494}
1495
1496static void
1497tierScrape( tr_announcer * announcer, tr_tier * tier )
1498{
1499    const char * scrape;
1500    struct evbuffer * buf;
1501    struct announce_data * data;
1502    const time_t now = time( NULL );
1503
1504    assert( tier );
1505    assert( !tier->isScraping );
1506    assert( tier->currentTracker != NULL );
1507    assert( tr_isTorrent( tier->tor ) );
1508
1509    data = tr_new0( struct announce_data, 1 );
1510    data->torrentId = tr_torrentId( tier->tor );
1511    data->tierId = tier->key;
1512
1513    scrape = tier->currentTracker->scrape;
1514
1515    buf = evbuffer_new( );
1516    evbuffer_add_printf( buf, "%s%cinfo_hash=%s",
1517                         scrape,
1518                         strchr( scrape, '?' ) ? '&' : '?',
1519                         tier->tor->info.hashEscaped );
1520
1521    tier->isScraping = TRUE;
1522    tier->lastScrapeStartTime = now;
1523    --announcer->scrapeSlotsAvailable;
1524    dbgmsg( tier, "scraping \"%s\"", (const char*)EVBUFFER_DATA(buf) );
1525    tr_webRun( announcer->session, (const char*)EVBUFFER_DATA(buf), NULL, onScrapeDone, data );
1526
1527    evbuffer_free( buf );
1528}
1529
1530static void
1531flushCloseMessages( tr_announcer * announcer )
1532{
1533    int i;
1534    const int n = tr_ptrArraySize( &announcer->stops );
1535
1536    for( i=0; i<n; ++i )
1537    {
1538        struct stop_message * stop = tr_ptrArrayNth( &announcer->stops, i );
1539        tr_webRun( announcer->session, stop->url, NULL, NULL, NULL );
1540        stopFree( stop );
1541    }
1542
1543    tr_ptrArrayClear( &announcer->stops );
1544}
1545
1546static tr_bool
1547tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1548{
1549    return !tier->isAnnouncing
1550        && !tier->isScraping
1551        && ( tier->announceEvent != NULL )
1552        && ( tier->announceAt <= now );
1553}
1554
1555static tr_bool
1556tierNeedsToScrape( const tr_tier * tier, const time_t now )
1557{
1558    return !tier->isScraping
1559        && ( tier->scrapeAt <= now )
1560        && ( tier->currentTracker != NULL )
1561        && ( tier->currentTracker->scrape != NULL );
1562}
1563
1564static void
1565announceMore( tr_announcer * announcer )
1566{
1567    const tr_bool canAnnounce = announcer->announceSlotsAvailable > 0;
1568    const tr_bool canScrape = announcer->scrapeSlotsAvailable > 0;
1569    tr_torrent * tor = NULL;
1570    const time_t now = time( NULL );
1571
1572    if( announcer->announceSlotsAvailable > 0 )
1573    {
1574        int i;
1575        int n;
1576        tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1577        tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1578
1579        /* build a list of tiers that need to be announced */
1580        while(( tor = tr_torrentNext( announcer->session, tor ))) {
1581            if( tor->tiers ) {
1582                n = tr_ptrArraySize( &tor->tiers->tiers );
1583                for( i=0; i<n; ++i ) {
1584                    tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1585                    if( canAnnounce && tierNeedsToAnnounce( tier, now ) )
1586                        tr_ptrArrayAppend( &announceMe, tier );
1587                    else if( canScrape && tierNeedsToScrape( tier, now ) )
1588                        tr_ptrArrayAppend( &scrapeMe, tier );
1589                }
1590            }
1591        }
1592
1593        /* if there are more tiers than slots available, prioritize */
1594        n = tr_ptrArraySize( &announceMe );
1595        if( n > announcer->announceSlotsAvailable )
1596            qsort( tr_ptrArrayBase( &announceMe ), n, sizeof( tr_tier * ), compareTiers );
1597
1598        /* announce some */
1599        n = MIN( tr_ptrArraySize( &announceMe ), announcer->announceSlotsAvailable );
1600        for( i=0; i<n; ++i ) {
1601            tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1602            dbgmsg( tier, "announcing tier %d of %d", i, n );
1603            tierAnnounce( announcer, tier );
1604        }
1605
1606
1607        /* scrape some */
1608        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->scrapeSlotsAvailable );
1609        for( i=0; i<n; ++i ) {
1610            tr_tier * tier = tr_ptrArrayNth( &scrapeMe, i );
1611            dbgmsg( tier, "scraping tier %d of %d", (i+1), n );
1612            tierScrape( announcer, tier );
1613        }
1614
1615        /* cleanup */
1616        tr_ptrArrayDestruct( &scrapeMe, NULL );
1617        tr_ptrArrayDestruct( &announceMe, NULL );
1618    }
1619
1620    tor = NULL;
1621    while(( tor = tr_torrentNext( announcer->session, tor ))) {
1622        if( tor->dhtAnnounceAt <= now ) {
1623            int rc = 1;
1624            if( tor->isRunning && tr_torrentAllowsDHT(tor) )
1625                rc = tr_dhtAnnounce(tor, 1);
1626            if(rc == 0)
1627                /* The DHT is not ready yet.  Try again soon. */
1628                tor->dhtAnnounceAt = now + 5 + tr_cryptoWeakRandInt( 5 );
1629            else
1630                /* We should announce at least once every 30 minutes. */
1631                tor->dhtAnnounceAt = now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1632        }
1633    }
1634}
1635
1636static void
1637onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1638{
1639    tr_announcer * announcer = vannouncer;
1640
1641    /* maybe send out some "stopped" messages for closed torrents */
1642    flushCloseMessages( announcer );
1643
1644    /* maybe send out some announcements to trackers */
1645    announceMore( announcer );
1646
1647    /* set up the next timer */
1648    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1649}
1650
1651/***
1652****
1653***/
1654
1655tr_tracker_stat *
1656tr_announcerStats( const tr_torrent * torrent,
1657                   int              * setmeTrackerCount )
1658{
1659    int i;
1660    int n;
1661    int out = 0;
1662    int tierCount;
1663    tr_tracker_stat * ret;
1664    const time_t now = time( NULL );
1665
1666    assert( tr_isTorrent( torrent ) );
1667
1668    /* count the trackers... */
1669    for( i=n=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i ) {
1670        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1671        n += tr_ptrArraySize( &tier->trackers );
1672    }
1673
1674    /* alloc the stats */
1675    *setmeTrackerCount = n;
1676    ret = tr_new0( tr_tracker_stat, n );
1677
1678    /* populate the stats */
1679    for( i=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i )
1680    {
1681        int j;
1682        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1683        n = tr_ptrArraySize( &tier->trackers );
1684        for( j=0; j<n; ++j )
1685        {
1686            const tr_tracker_item * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
1687            tr_tracker_stat * st = ret + out++;
1688
1689            tr_strlcpy( st->host, tracker->host->name, sizeof( st->host ) );
1690            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1691            st->tier = i + 1;
1692            st->isBackup = tracker != tier->currentTracker;
1693            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1694
1695            if( st->isBackup )
1696            {
1697                st->scrapeState = TR_TRACKER_INACTIVE;
1698                st->announceState = TR_TRACKER_INACTIVE;
1699                st->nextScrapeTime = 0;
1700                st->nextAnnounceTime = 0;
1701            }
1702            else
1703            {
1704                if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
1705                    st->lastScrapeTime = tier->lastScrapeTime;
1706                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1707                    tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, sizeof( st->lastScrapeResult ) );
1708                }
1709
1710                if( tier->isScraping )
1711                    st->scrapeState = TR_TRACKER_ACTIVE;
1712                else if( tier->scrapeAt > now )
1713                {
1714                    st->scrapeState = TR_TRACKER_WAITING;
1715                    st->nextScrapeTime = tier->scrapeAt; 
1716                }
1717                else
1718                    st->scrapeState = TR_TRACKER_QUEUED;
1719
1720                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1721
1722                if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
1723                    st->lastAnnounceTime = tier->lastAnnounceTime;
1724                    tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, sizeof( st->lastAnnounceResult ) );
1725                    if(( st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded )) {
1726                        st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1727                    }
1728                }
1729
1730                if( tier->isAnnouncing )
1731                    st->announceState = TR_TRACKER_ACTIVE;
1732                else if( !torrent->isRunning )
1733                    st->announceState = TR_TRACKER_INACTIVE;
1734                else if( tier->announceAt > now )
1735                {
1736                    st->announceState = TR_TRACKER_WAITING;
1737                    st->nextAnnounceTime = tier->announceAt; 
1738                }
1739                else
1740                    st->announceState = TR_TRACKER_QUEUED;
1741
1742                st->seederCount = tracker->seederCount;
1743                st->leecherCount = tracker->leecherCount;
1744                st->downloadCount = tracker->downloadCount;
1745            }
1746        }
1747    }
1748
1749    return ret;
1750}
1751
1752void
1753tr_announcerStatsFree( tr_tracker_stat * trackers,
1754                       int trackerCount UNUSED )
1755{
1756    tr_free( trackers );
1757}
Note: See TracBrowser for help on using the repository browser.