source: trunk/libtransmission/announcer.c @ 10238

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

(trunk libT) #2922 "announce's 'download' field should follow the de facto standard" -- added to trunk for 1.91

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