source: trunk/libtransmission/announcer.c @ 9354

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

(trunk libT) #2518: r9334 overzealously alarmist wrt unresponsive trackers

File size: 49.6 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 response is serious, *and* if the response may require
1210             * human intervention, then notify the user... otherwise just log it */
1211            if( responseCode >= 400 )
1212                if( tr_torrentIsPrivate( tier->tor ) || ( tier->tor->info.trackerCount < 2 ) )
1213                    publishWarning( tier, buf );
1214            tr_torinf( tier->tor, "%s", buf );
1215            dbgmsg( tier, "%s", buf );
1216
1217            tr_free( buf );
1218        }
1219        else
1220        {
1221            tr_strlcpy( tier->lastAnnounceStr,
1222                        _( "tracker did not respond" ),
1223                        sizeof( tier->lastAnnounceStr ) );
1224            dbgmsg( tier, "%s", tier->lastAnnounceStr );
1225        }
1226    }
1227
1228    if( tier )
1229    {
1230        tier->isAnnouncing = FALSE;
1231
1232        if( responseCode == 0 )
1233        {
1234            dbgmsg( tier, "No response from tracker... retrying in two minutes." );
1235            tier->manualAnnounceAllowedAt = ~(time_t)0;
1236            tierSetNextAnnounce( tier, tier->announceEvent, now + 120 );
1237        }
1238        else if( 200 <= responseCode && responseCode <= 299 )
1239        {
1240            const int interval = tier->announceIntervalSec;
1241            dbgmsg( tier, "request succeeded. reannouncing in %d seconds", interval );
1242
1243            if( gotScrape )
1244            {
1245                tier->lastScrapeTime = now;
1246                tier->scrapeAt = now + tier->scrapeIntervalSec;
1247            }
1248
1249            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1250
1251            if( strcmp( tier->announceEvent, "stopped" ) == 0 )
1252                tierClearNextAnnounce( tier );
1253            else
1254                tierSetNextAnnounce( tier, "", now + interval );
1255        }
1256        else if( 300 <= responseCode && responseCode <= 399 )
1257        {
1258            /* how did this get here?  libcurl handles this */
1259            const int interval = 5;
1260            dbgmsg( tier, "got a redirect. retrying in %d seconds", interval );
1261            tierSetNextAnnounce( tier, tier->announceEvent, now + interval );
1262            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1263        }
1264        else if( 400 <= responseCode && responseCode <= 499 )
1265        {
1266            /* The request could not be understood by the server due to
1267             * malformed syntax. The client SHOULD NOT repeat the
1268             * request without modifications. */
1269            publishErrorMessageAndStop( tier, _( "Tracker returned a 4xx message" ) );
1270            tier->manualAnnounceAllowedAt = ~(time_t)0;
1271        }
1272        else if( 500 <= responseCode && responseCode <= 599 )
1273        {
1274            /* Response status codes beginning with the digit "5" indicate
1275             * cases in which the server is aware that it has erred or is
1276             * incapable of performing the request.  So we pause a bit and
1277             * try again. */
1278            tier->manualAnnounceAllowedAt = ~(time_t)0;
1279            tierSetNextAnnounce( tier, tier->announceEvent, now + tier->retryAnnounceIntervalSec );
1280            tier->retryAnnounceIntervalSec *= 2;
1281        }
1282        else
1283        {
1284            /* WTF did we get?? */
1285            const int interval = 120;
1286            dbgmsg( tier, "Invalid response from tracker... retrying in two minutes." );
1287            tier->manualAnnounceAllowedAt = ~(time_t)0;
1288            tierSetNextAnnounce( tier, tier->announceEvent, now + interval );
1289        }
1290
1291        tier->lastAnnounceSucceeded = success;
1292
1293        if( success )
1294            tier->isRunning = data->isRunningOnSuccess;
1295
1296        if( !success )
1297            tierIncrementTracker( tier );
1298    }
1299
1300    if( announcer != NULL )
1301    {
1302        ++announcer->announceSlotsAvailable;
1303    }
1304
1305    tr_free( data );
1306}
1307
1308static const char *
1309getAnnounceEvent( const tr_tier * tier )
1310{
1311    const char * event;
1312
1313    assert( tier != NULL );
1314    assert( tier->announceEvent != NULL );
1315    assert( tr_isTorrent( tier->tor ) );
1316
1317    /* BEP 21: "In order to tell the tracker that a peer is a partial seed,
1318     * it MUST send an event=paused parameter in every announce while
1319     * it is a partial seed." */
1320    if( tr_cpGetStatus( &tier->tor->completion ) == TR_PARTIAL_SEED )
1321        return "paused";
1322
1323    event = tier->announceEvent;
1324
1325    if( !strcmp( event, "manual" ) )
1326        return "started";
1327
1328    return event;
1329}
1330
1331static void
1332tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1333{
1334    char * url;
1335    struct announce_data * data;
1336    const tr_torrent * tor = tier->tor;
1337    const time_t now = time( NULL );
1338
1339    assert( !tier->isAnnouncing );
1340
1341    data = tr_new0( struct announce_data, 1 );
1342    data->torrentId = tr_torrentId( tor );
1343    data->tierId = tier->key;
1344    data->isRunningOnSuccess = tor->isRunning;
1345    data->timeSent = now;
1346
1347    url = createAnnounceURL( announcer, tor, tier, getAnnounceEvent( tier ) );
1348
1349    tier->isAnnouncing = TRUE;
1350    tier->lastAnnounceStartTime = now;
1351    --announcer->announceSlotsAvailable;
1352    tr_webRun( announcer->session, url, NULL, onAnnounceDone, data );
1353
1354    tr_free( url );
1355}
1356
1357static tr_bool
1358parseScrapeResponse( tr_tier     * tier,
1359                     const char  * response,
1360                     size_t        responseLen,
1361                     char        * result,
1362                     size_t        resultlen )
1363{
1364    tr_bool success = FALSE;
1365    tr_benc benc, *files;
1366    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1367    if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) )
1368    {
1369        const char * key;
1370        tr_benc * val;
1371        int i = 0;
1372        while( tr_bencDictChild( files, i++, &key, &val ))
1373        {
1374            int64_t intVal;
1375            tr_benc * flags;
1376
1377            if( memcmp( tier->tor->info.hash, key, SHA_DIGEST_LENGTH ) )
1378                continue;
1379
1380            success = TRUE;
1381            publishErrorClear( tier );
1382
1383            if( ( tr_bencDictFindInt( val, "complete", &intVal ) ) )
1384                tier->currentTracker->seederCount = intVal;
1385
1386            if( ( tr_bencDictFindInt( val, "incomplete", &intVal ) ) )
1387                tier->currentTracker->leecherCount = intVal;
1388
1389            if( ( tr_bencDictFindInt( val, "downloaded", &intVal ) ) )
1390                tier->currentTracker->downloadCount = intVal;
1391
1392            if( ( tr_bencDictFindInt( val, "downloaders", &intVal ) ) )
1393                tier->currentTracker->downloaderCount = intVal;
1394
1395            if( tr_bencDictFindDict( val, "flags", &flags ) )
1396                if( ( tr_bencDictFindInt( flags, "min_request_interval", &intVal ) ) )
1397                    tier->scrapeIntervalSec = intVal;
1398
1399            /* as per ticket #1045, safeguard against trackers returning
1400             * a very low min_request_interval... */
1401            if( tier->scrapeIntervalSec < DEFAULT_SCRAPE_INTERVAL_SEC )
1402                tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
1403
1404            tr_tordbg( tier->tor,
1405                       "Scrape successful. Rescraping in %d seconds.",
1406                       tier->scrapeIntervalSec );
1407
1408            tier->retryScrapeIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
1409        }
1410    }
1411
1412    if( bencLoaded )
1413        tr_bencFree( &benc );
1414
1415    if( success )
1416        tr_strlcpy( result, _( "Success" ), resultlen );
1417    else
1418        tr_strlcpy( result, _( "Error parsing response" ), resultlen );
1419
1420    return success;
1421}
1422
1423static void
1424onScrapeDone( tr_session   * session,
1425              long           responseCode,
1426              const void   * response,
1427              size_t         responseLen,
1428              void         * vdata )
1429{
1430    tr_announcer * announcer = session->announcer;
1431    struct announce_data * data = vdata;
1432    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1433    const time_t now = time( NULL );
1434    tr_bool success = FALSE;
1435
1436    if( announcer )
1437        ++announcer->scrapeSlotsAvailable;
1438
1439    if( announcer && tier )
1440    {
1441        tier->isScraping = FALSE;
1442        tier->lastScrapeTime = now;
1443   
1444        if( tier->currentTracker->host )
1445        {
1446            tr_host * host = tier->currentTracker->host;
1447            host->lastRequestTime = data->timeSent;
1448            host->lastResponseInterval = now - data->timeSent;
1449        }
1450
1451        if( 200 <= responseCode && responseCode <= 299 )
1452        {
1453            const int interval = tier->scrapeIntervalSec;
1454            tier->scrapeAt = now + interval;
1455
1456            if( responseCode == HTTP_OK )
1457                success = parseScrapeResponse( tier, response, responseLen,
1458                                               tier->lastScrapeStr, sizeof( tier->lastScrapeStr ) );
1459            else
1460                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1461                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1462                             responseCode, tr_webGetResponseStr( responseCode ) );
1463            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1464        }
1465        else if( 300 <= responseCode && responseCode <= 399 )
1466        {
1467            /* this shouldn't happen; libcurl should handle this */
1468            const int interval = 5;
1469            tier->scrapeAt = now + interval;
1470            tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1471                         "Got a redirect. Retrying in %d seconds", interval );
1472            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1473        }
1474        else
1475        {
1476            const int interval = tier->retryScrapeIntervalSec;
1477            tier->retryScrapeIntervalSec *= 2;
1478            tier->scrapeAt = now + interval;
1479
1480            /* %1$ld - http status code, such as 404
1481             * %2$s - human-readable explanation of the http status code */
1482            if( !responseCode )
1483                tr_strlcpy( tier->lastScrapeStr, _( "tracker did not respond" ),
1484                            sizeof( tier->lastScrapeStr ) );
1485            else
1486                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1487                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1488                             responseCode, tr_webGetResponseStr( responseCode ) );
1489        }
1490
1491        tier->lastScrapeSucceeded = success;
1492    }
1493
1494    tr_free( data );
1495}
1496
1497static void
1498tierScrape( tr_announcer * announcer, tr_tier * tier )
1499{
1500    const char * scrape;
1501    struct evbuffer * buf;
1502    struct announce_data * data;
1503    const time_t now = time( NULL );
1504
1505    assert( tier );
1506    assert( !tier->isScraping );
1507    assert( tier->currentTracker != NULL );
1508    assert( tr_isTorrent( tier->tor ) );
1509
1510    data = tr_new0( struct announce_data, 1 );
1511    data->torrentId = tr_torrentId( tier->tor );
1512    data->tierId = tier->key;
1513
1514    scrape = tier->currentTracker->scrape;
1515
1516    buf = evbuffer_new( );
1517    evbuffer_add_printf( buf, "%s%cinfo_hash=%s",
1518                         scrape,
1519                         strchr( scrape, '?' ) ? '&' : '?',
1520                         tier->tor->info.hashEscaped );
1521
1522    tier->isScraping = TRUE;
1523    tier->lastScrapeStartTime = now;
1524    --announcer->scrapeSlotsAvailable;
1525    dbgmsg( tier, "scraping \"%s\"", (const char*)EVBUFFER_DATA(buf) );
1526    tr_webRun( announcer->session, (const char*)EVBUFFER_DATA(buf), NULL, onScrapeDone, data );
1527
1528    evbuffer_free( buf );
1529}
1530
1531static void
1532flushCloseMessages( tr_announcer * announcer )
1533{
1534    int i;
1535    const int n = tr_ptrArraySize( &announcer->stops );
1536
1537    for( i=0; i<n; ++i )
1538    {
1539        struct stop_message * stop = tr_ptrArrayNth( &announcer->stops, i );
1540        tr_webRun( announcer->session, stop->url, NULL, NULL, NULL );
1541        stopFree( stop );
1542    }
1543
1544    tr_ptrArrayClear( &announcer->stops );
1545}
1546
1547static tr_bool
1548tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1549{
1550    return !tier->isAnnouncing
1551        && !tier->isScraping
1552        && ( tier->announceEvent != NULL )
1553        && ( tier->announceAt <= now );
1554}
1555
1556static tr_bool
1557tierNeedsToScrape( const tr_tier * tier, const time_t now )
1558{
1559    return !tier->isScraping
1560        && ( tier->scrapeAt <= now )
1561        && ( tier->currentTracker != NULL )
1562        && ( tier->currentTracker->scrape != NULL );
1563}
1564
1565static void
1566announceMore( tr_announcer * announcer )
1567{
1568    const tr_bool canAnnounce = announcer->announceSlotsAvailable > 0;
1569    const tr_bool canScrape = announcer->scrapeSlotsAvailable > 0;
1570    tr_torrent * tor = NULL;
1571    const time_t now = time( NULL );
1572
1573    if( announcer->announceSlotsAvailable > 0 )
1574    {
1575        int i;
1576        int n;
1577        tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1578        tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1579
1580        /* build a list of tiers that need to be announced */
1581        while(( tor = tr_torrentNext( announcer->session, tor ))) {
1582            if( tor->tiers ) {
1583                n = tr_ptrArraySize( &tor->tiers->tiers );
1584                for( i=0; i<n; ++i ) {
1585                    tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1586                    if( canAnnounce && tierNeedsToAnnounce( tier, now ) )
1587                        tr_ptrArrayAppend( &announceMe, tier );
1588                    else if( canScrape && tierNeedsToScrape( tier, now ) )
1589                        tr_ptrArrayAppend( &scrapeMe, tier );
1590                }
1591            }
1592        }
1593
1594        /* if there are more tiers than slots available, prioritize */
1595        n = tr_ptrArraySize( &announceMe );
1596        if( n > announcer->announceSlotsAvailable )
1597            qsort( tr_ptrArrayBase( &announceMe ), n, sizeof( tr_tier * ), compareTiers );
1598
1599        /* announce some */
1600        n = MIN( tr_ptrArraySize( &announceMe ), announcer->announceSlotsAvailable );
1601        for( i=0; i<n; ++i ) {
1602            tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1603            dbgmsg( tier, "announcing tier %d of %d", i, n );
1604            tierAnnounce( announcer, tier );
1605        }
1606
1607
1608        /* scrape some */
1609        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->scrapeSlotsAvailable );
1610        for( i=0; i<n; ++i ) {
1611            tr_tier * tier = tr_ptrArrayNth( &scrapeMe, i );
1612            dbgmsg( tier, "scraping tier %d of %d", (i+1), n );
1613            tierScrape( announcer, tier );
1614        }
1615
1616        /* cleanup */
1617        tr_ptrArrayDestruct( &scrapeMe, NULL );
1618        tr_ptrArrayDestruct( &announceMe, NULL );
1619    }
1620
1621    tor = NULL;
1622    while(( tor = tr_torrentNext( announcer->session, tor ))) {
1623        if( tor->dhtAnnounceAt <= now ) {
1624            int rc = 1;
1625            if( tor->isRunning && tr_torrentAllowsDHT(tor) )
1626                rc = tr_dhtAnnounce(tor, 1);
1627            if(rc == 0)
1628                /* The DHT is not ready yet.  Try again soon. */
1629                tor->dhtAnnounceAt = now + 5 + tr_cryptoWeakRandInt( 5 );
1630            else
1631                /* We should announce at least once every 30 minutes. */
1632                tor->dhtAnnounceAt = now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1633        }
1634    }
1635}
1636
1637static void
1638onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1639{
1640    tr_announcer * announcer = vannouncer;
1641
1642    /* maybe send out some "stopped" messages for closed torrents */
1643    flushCloseMessages( announcer );
1644
1645    /* maybe send out some announcements to trackers */
1646    announceMore( announcer );
1647
1648    /* set up the next timer */
1649    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1650}
1651
1652/***
1653****
1654***/
1655
1656tr_tracker_stat *
1657tr_announcerStats( const tr_torrent * torrent,
1658                   int              * setmeTrackerCount )
1659{
1660    int i;
1661    int n;
1662    int out = 0;
1663    int tierCount;
1664    tr_tracker_stat * ret;
1665    const time_t now = time( NULL );
1666
1667    assert( tr_isTorrent( torrent ) );
1668
1669    /* count the trackers... */
1670    for( i=n=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i ) {
1671        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1672        n += tr_ptrArraySize( &tier->trackers );
1673    }
1674
1675    /* alloc the stats */
1676    *setmeTrackerCount = n;
1677    ret = tr_new0( tr_tracker_stat, n );
1678
1679    /* populate the stats */
1680    for( i=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i )
1681    {
1682        int j;
1683        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1684        n = tr_ptrArraySize( &tier->trackers );
1685        for( j=0; j<n; ++j )
1686        {
1687            const tr_tracker_item * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
1688            tr_tracker_stat * st = ret + out++;
1689
1690            tr_strlcpy( st->host, tracker->host->name, sizeof( st->host ) );
1691            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1692            st->tier = i + 1;
1693            st->isBackup = tracker != tier->currentTracker;
1694            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1695
1696            if( st->isBackup )
1697            {
1698                st->scrapeState = TR_TRACKER_INACTIVE;
1699                st->announceState = TR_TRACKER_INACTIVE;
1700                st->nextScrapeTime = 0;
1701                st->nextAnnounceTime = 0;
1702            }
1703            else
1704            {
1705                if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
1706                    st->lastScrapeTime = tier->lastScrapeTime;
1707                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1708                    tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, sizeof( st->lastScrapeResult ) );
1709                }
1710
1711                if( tier->isScraping )
1712                    st->scrapeState = TR_TRACKER_ACTIVE;
1713                else if( tier->scrapeAt > now )
1714                {
1715                    st->scrapeState = TR_TRACKER_WAITING;
1716                    st->nextScrapeTime = tier->scrapeAt; 
1717                }
1718                else
1719                    st->scrapeState = TR_TRACKER_QUEUED;
1720
1721                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1722
1723                if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
1724                    st->lastAnnounceTime = tier->lastAnnounceTime;
1725                    tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, sizeof( st->lastAnnounceResult ) );
1726                    if(( st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded )) {
1727                        st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1728                    }
1729                }
1730
1731                if( tier->isAnnouncing )
1732                    st->announceState = TR_TRACKER_ACTIVE;
1733                else if( !torrent->isRunning )
1734                    st->announceState = TR_TRACKER_INACTIVE;
1735                else if( tier->announceAt > now )
1736                {
1737                    st->announceState = TR_TRACKER_WAITING;
1738                    st->nextAnnounceTime = tier->announceAt; 
1739                }
1740                else
1741                    st->announceState = TR_TRACKER_QUEUED;
1742
1743                st->seederCount = tracker->seederCount;
1744                st->leecherCount = tracker->leecherCount;
1745                st->downloadCount = tracker->downloadCount;
1746            }
1747        }
1748    }
1749
1750    return ret;
1751}
1752
1753void
1754tr_announcerStatsFree( tr_tracker_stat * trackers,
1755                       int trackerCount UNUSED )
1756{
1757    tr_free( trackers );
1758}
Note: See TracBrowser for help on using the repository browser.