source: trunk/libtransmission/announcer.c @ 10149

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

(trunk libT) fix the &event= key when telling the tracker that a partial seed is being stopped

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