source: trunk/libtransmission/announcer.c @ 10194

Last change on this file since 10194 was 10194, checked in by charles, 12 years ago

(trunk libT) #2850 "tr_torrentSetAnnounceList() should not trigger reannounces to trackers" -- tweak to r10193

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