source: trunk/libtransmission/announcer.c @ 9226

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

(trunk, libT) #2464: Mac Client shows incorrect announce/scrape results for backup trackers

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