source: trunk/libtransmission/announcer.c @ 12131

Last change on this file since 12131 was 12131, checked in by jordan, 11 years ago

(trunk libT) repeat the r12129 fix for delegating https scrapes

  • Property svn:keywords set to Date Rev Author Id
File size: 47.5 KB
Line 
1/*
2 * This file Copyright (C) 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: announcer.c 12131 2011-03-11 13:04:09Z jordan $
11 */
12
13#include <assert.h>
14#include <limits.h>
15
16#include <event2/buffer.h>
17#include <event2/event.h> /* evtimer */
18
19#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
20
21#include "transmission.h"
22#include "announcer.h"
23#include "announcer-common.h"
24#include "crypto.h"
25#include "net.h"
26#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
27#include "ptrarray.h"
28#include "session.h"
29#include "tr-dht.h"
30#include "tr-lpd.h"
31#include "torrent.h"
32#include "utils.h"
33#include "web.h"
34
35struct tr_tier;
36
37static void tier_build_log_name( const struct tr_tier * tier,
38                                 char * buf, size_t buflen );
39
40#define dbgmsg( tier, ... ) \
41if( tr_deepLoggingIsActive( ) ) do { \
42  char name[128]; \
43  tier_build_log_name( tier, name, sizeof( name ) ); \
44  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
45} while( 0 )
46
47enum
48{
49    /* unless the tracker says otherwise, rescrape this frequently */
50    DEFAULT_SCRAPE_INTERVAL_SEC = ( 60 * 30 ),
51
52    /* unless the tracker says otherwise, this is the announce interval */
53    DEFAULT_ANNOUNCE_INTERVAL_SEC = ( 60 * 10 ),
54
55    /* unless the tracker says otherwise, this is the announce min_interval */
56    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = ( 60 * 2 ),
57
58    /* how many web tasks we allow at one time */
59    MAX_CONCURRENT_TASKS = 48,
60
61    /* the value of the 'numwant' argument passed in tracker requests. */
62    NUMWANT = 80,
63
64    UPKEEP_INTERVAL_SECS = 1,
65
66    /* this is an upper limit for the frequency of LDS announces */
67    LPD_HOUSEKEEPING_INTERVAL_SECS = 5
68};
69
70/***
71****
72***/
73
74const char*
75tr_announce_event_get_string( tr_announce_event e )
76{
77    switch( e )
78    {
79        case TR_ANNOUNCE_EVENT_COMPLETED:  return "completed";
80        case TR_ANNOUNCE_EVENT_STARTED:    return "started";
81        case TR_ANNOUNCE_EVENT_STOPPED:    return "stopped";
82        default:                           return "";
83    }
84}
85
86/***
87****
88***/
89
90static int
91compareTransfer( uint64_t a_uploaded, uint64_t a_downloaded,
92                 uint64_t b_uploaded, uint64_t b_downloaded )
93{
94    /* higher upload count goes first */
95    if( a_uploaded != b_uploaded )
96        return a_uploaded > b_uploaded ? -1 : 1;
97
98    /* then higher download count goes first */
99    if( a_downloaded != b_downloaded )
100        return a_downloaded > b_downloaded ? -1 : 1;
101
102    return 0;
103}
104
105static int
106compareStops( const void * va, const void * vb )
107{
108    const tr_announce_request * a = va;
109    const tr_announce_request * b = vb;
110    return compareTransfer( a->up, a->down, b->up, b->down);
111}
112
113/***
114****
115***/
116
117/**
118 * "global" (per-tr_session) fields
119 */
120typedef struct tr_announcer
121{
122   /* Since we can't poll a tr_torrent's fields after it's destroyed,
123    * we pre-build the "stop" tr_announce_request when a torrent is
124    * removed from Transmission */
125    tr_ptrArray stops;
126
127    tr_session * session;
128    struct event * upkeepTimer;
129    int slotsAvailable;
130    int key;
131    time_t lpdHouseKeepingAt;
132}
133tr_announcer;
134
135tr_bool
136tr_announcerHasBacklog( const struct tr_announcer * announcer )
137{
138    return announcer->slotsAvailable < 1;
139}
140
141static inline time_t
142calcRescheduleWithJitter( const int minPeriod )
143{
144    const double jitter = 0.1;
145
146    assert( minPeriod > 0 );
147
148    return tr_time()
149        + minPeriod
150        + tr_cryptoWeakRandInt( (int) ( minPeriod * jitter ) + 1 );
151}
152
153static void
154onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer );
155
156void
157tr_announcerInit( tr_session * session )
158{
159    tr_announcer * a;
160
161    const time_t lpdAt =
162        calcRescheduleWithJitter( LPD_HOUSEKEEPING_INTERVAL_SECS / 3 );
163
164    assert( tr_isSession( session ) );
165
166    a = tr_new0( tr_announcer, 1 );
167    a->stops = TR_PTR_ARRAY_INIT;
168    a->key = tr_cryptoRandInt( INT_MAX );
169    a->session = session;
170    a->slotsAvailable = MAX_CONCURRENT_TASKS;
171    a->lpdHouseKeepingAt = lpdAt;
172    a->upkeepTimer = evtimer_new( session->event_base, onUpkeepTimer, a );
173    tr_timerAdd( a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
174
175    session->announcer = a;
176}
177
178static void flushCloseMessages( tr_announcer * announcer );
179
180void
181tr_announcerClose( tr_session * session )
182{
183    tr_announcer * announcer = session->announcer;
184
185    flushCloseMessages( announcer );
186
187    event_free( announcer->upkeepTimer );
188    announcer->upkeepTimer = NULL;
189
190    tr_ptrArrayDestruct( &announcer->stops, NULL );
191
192    session->announcer = NULL;
193    tr_free( announcer );
194}
195
196/***
197****
198***/
199
200/* a row in tr_tier's list of trackers */
201typedef struct
202{
203    char * hostname;
204    char * announce;
205    char * scrape;
206
207    char * tracker_id_str;
208
209    int seederCount;
210    int leecherCount;
211    int downloadCount;
212    int downloaderCount;
213
214    int consecutiveAnnounceFailures;
215
216    uint32_t id;
217}
218tr_tracker;
219
220/* format: hostname + ':' + port */
221static char *
222getHostName( const char * url )
223{
224    int port = 0;
225    char * host = NULL;
226    char * ret;
227    tr_urlParse( url, -1, NULL, &host, &port, NULL );
228    ret = tr_strdup_printf( "%s:%d", ( host ? host : "invalid" ), port );
229    tr_free( host );
230    return ret;
231}
232
233static tr_tracker*
234trackerNew( const char * announce, const char * scrape, uint32_t id )
235{
236    tr_tracker * tracker = tr_new0( tr_tracker, 1  );
237    tracker->hostname = getHostName( announce );
238    tracker->announce = tr_strdup( announce );
239    tracker->scrape = tr_strdup( scrape );
240    tracker->id = id;
241    tracker->seederCount = -1;
242    tracker->leecherCount = -1;
243    tracker->downloadCount = -1;
244    return tracker;
245}
246
247static void
248trackerFree( void * vtracker )
249{
250    tr_tracker * tracker = vtracker;
251
252    tr_free( tracker->tracker_id_str );
253    tr_free( tracker->scrape );
254    tr_free( tracker->announce );
255    tr_free( tracker->hostname );
256    tr_free( tracker );
257}
258
259/***
260****
261***/
262
263struct tr_torrent_tiers;
264
265/** @brief A group of trackers in a single tier, as per the multitracker spec */
266typedef struct tr_tier
267{
268    /* number of up/down/corrupt bytes since the last time we sent an
269     * "event=stopped" message that was acknowledged by the tracker */
270    uint64_t byteCounts[3];
271
272    tr_ptrArray trackers; /* tr_tracker */
273    tr_tracker * currentTracker;
274    int currentTrackerIndex;
275
276    tr_torrent * tor;
277
278    time_t scrapeAt;
279    time_t lastScrapeStartTime;
280    time_t lastScrapeTime;
281    tr_bool lastScrapeSucceeded;
282    tr_bool lastScrapeTimedOut;
283
284    time_t announceAt;
285    time_t manualAnnounceAllowedAt;
286    time_t lastAnnounceStartTime;
287    time_t lastAnnounceTime;
288    tr_bool lastAnnounceSucceeded;
289    tr_bool lastAnnounceTimedOut;
290
291    tr_announce_event * announce_events;
292    int announce_event_count;
293    int announce_event_alloc;
294
295    /* unique lookup key */
296    int key;
297
298    int scrapeIntervalSec;
299    int announceIntervalSec;
300    int announceMinIntervalSec;
301
302    int lastAnnouncePeerCount;
303
304    tr_bool isRunning;
305    tr_bool isAnnouncing;
306    tr_bool isScraping;
307    tr_bool wasCopied;
308
309    char lastAnnounceStr[128];
310    char lastScrapeStr[128];
311}
312tr_tier;
313
314static tr_tier *
315tierNew( tr_torrent * tor )
316{
317    tr_tier * t;
318    static int nextKey = 1;
319    const time_t now = tr_time( );
320
321    t = tr_new0( tr_tier, 1 );
322    t->key = nextKey++;
323    t->announce_events = NULL;
324    t->announce_event_count = 0;
325    t->announce_event_alloc = 0;
326    t->trackers = TR_PTR_ARRAY_INIT;
327    t->currentTracker = NULL;
328    t->currentTrackerIndex = -1;
329    t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
330    t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
331    t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
332    t->scrapeAt = now + tr_cryptoWeakRandInt( 60*5 );
333    t->tor = tor;
334
335    return t;
336}
337
338static void
339tierFree( void * vtier )
340{
341    tr_tier * tier = vtier;
342    tr_ptrArrayDestruct( &tier->trackers, trackerFree );
343    tr_free( tier->announce_events );
344    tr_free( tier );
345}
346
347static void
348tier_build_log_name( const tr_tier * tier, char * buf, size_t buflen )
349{
350    tr_snprintf( buf, buflen, "[%s---%s]",
351       ( tier && tier->tor ) ? tr_torrentName( tier->tor ) : "?",
352       ( tier && tier->currentTracker ) ? tier->currentTracker->hostname : "?" );
353}
354
355static void
356tierIncrementTracker( tr_tier * tier )
357{
358    /* move our index to the next tracker in the tier */
359    const int i = ( tier->currentTrackerIndex + 1 )
360                        % tr_ptrArraySize( &tier->trackers );
361    tier->currentTracker = tr_ptrArrayNth( &tier->trackers, i );
362    tier->currentTrackerIndex = i;
363
364    /* reset some of the tier's fields */
365    tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
366    tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
367    tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
368    tier->isAnnouncing = FALSE;
369    tier->isScraping = FALSE;
370    tier->lastAnnounceStartTime = 0;
371    tier->lastScrapeStartTime = 0;
372}
373
374static void
375tierAddTracker( tr_tier    * tier,
376                const char * announce,
377                const char * scrape,
378                uint32_t     id )
379{
380    tr_tracker * tracker = trackerNew( announce, scrape, id );
381
382    tr_ptrArrayAppend( &tier->trackers, tracker );
383    dbgmsg( tier, "adding tracker %s", announce );
384
385    if( !tier->currentTracker )
386        tierIncrementTracker( tier );
387}
388
389
390/***
391****
392***/
393
394/**
395 * @brief Opaque, per-torrent data structure for tracker announce information
396 *
397 * this opaque data structure can be found in tr_torrent.tiers
398 */
399typedef struct tr_torrent_tiers
400{
401    tr_ptrArray tiers; /* tr_tier */
402    tr_tracker_callback * callback;
403    void * callbackData;
404}
405tr_torrent_tiers;
406
407static tr_torrent_tiers*
408tiersNew( void )
409{
410    tr_torrent_tiers * tiers = tr_new0( tr_torrent_tiers, 1 );
411    tiers->tiers = TR_PTR_ARRAY_INIT;
412    return tiers;
413}
414
415static void
416tiersFree( tr_torrent_tiers * tiers )
417{
418    tr_ptrArrayDestruct( &tiers->tiers, tierFree );
419    tr_free( tiers );
420}
421
422static tr_tier*
423getTier( tr_announcer * announcer, const uint8_t * info_hash, int tierId )
424{
425    tr_tier * tier = NULL;
426
427    if( announcer != NULL )
428    {
429        tr_session * session = announcer->session;
430        tr_torrent * tor = tr_torrentFindFromHash( session, info_hash );
431
432        if( tor && tor->tiers )
433        {
434            int i;
435            tr_ptrArray * tiers = &tor->tiers->tiers;
436            const int n = tr_ptrArraySize( tiers );
437            for( i=0; !tier && i<n; ++i )
438            {
439                tr_tier * tmp = tr_ptrArrayNth( tiers, i );
440                if( tmp->key == tierId )
441                    tier = tmp;
442            }
443        }
444    }
445
446    return tier;
447}
448
449/***
450****  PUBLISH
451***/
452
453static const tr_tracker_event TRACKER_EVENT_INIT = { 0, 0, 0, 0, 0, 0 };
454
455static void
456publishMessage( tr_tier * tier, const char * msg, int type )
457{
458    if( tier && tier->tor && tier->tor->tiers )
459    {
460        tr_torrent_tiers * tiers = tier->tor->tiers;
461        tr_tracker_event event = TRACKER_EVENT_INIT;
462        event.messageType = type;
463        event.text = msg;
464        if( tier->currentTracker )
465            event.tracker = tier->currentTracker->announce;
466
467        if( tiers->callback != NULL )
468            tiers->callback( tier->tor, &event, tiers->callbackData );
469    }
470}
471
472static void
473publishErrorClear( tr_tier * tier )
474{
475    publishMessage( tier, NULL, TR_TRACKER_ERROR_CLEAR );
476}
477
478static void
479publishWarning( tr_tier * tier, const char * msg )
480{
481    publishMessage( tier, msg, TR_TRACKER_WARNING );
482}
483
484static void
485publishError( tr_tier * tier, const char * msg )
486{
487    publishMessage( tier, msg, TR_TRACKER_ERROR );
488}
489
490static int8_t
491getSeedProbability( tr_tier * tier, int seeds, int leechers, int pex_count )
492{
493    /* special case optimization:
494       ocelot omits seeds from peer lists sent to seeds on private trackers.
495       so check for that case... */
496    if( ( leechers == pex_count ) && tr_torrentIsPrivate( tier->tor )
497                                  && tr_torrentIsSeed( tier->tor )
498                                  && ( seeds + leechers < NUMWANT ) )
499        return 0;
500
501    if( !seeds )
502        return 0;
503
504    if( seeds>=0 && leechers>=0 )
505        return (int8_t)((100.0*seeds)/(seeds+leechers));
506
507    return -1; /* unknown */
508}
509
510static void
511publishPeersPex( tr_tier * tier, int seeds, int leechers,
512                 const tr_pex * pex, int n )
513{
514    tr_tracker_event e = TRACKER_EVENT_INIT;
515    e.messageType = TR_TRACKER_PEERS;
516    e.seedProbability = getSeedProbability( tier, seeds, leechers, n );
517    e.pex = pex;
518    e.pexCount = n;
519
520    dbgmsg( tier, "got %d peers; seed probability %d", n, (int)e.seedProbability );
521
522    if( tier->tor->tiers->callback != NULL )
523        tier->tor->tiers->callback( tier->tor, &e, NULL );
524}
525
526/***
527****
528***/
529
530static void
531addTorrentToTier( tr_torrent_tiers * tiers, tr_torrent * tor )
532{
533    int i, n;
534    const tr_tracker_info ** infos;
535    const int trackerCount = tor->info.trackerCount;
536    const tr_tracker_info  * trackers = tor->info.trackers;
537
538    /* get the trackers that we support... */
539    infos = tr_new0( const tr_tracker_info*, trackerCount );
540    for( i=n=0; i<trackerCount; ++i )
541        if( tr_urlIsValidTracker( trackers[i].announce ) )
542            infos[n++] = &trackers[i];
543
544    /* build our private table of tiers... */
545    if( n > 0 )
546    {
547        int tierIndex = -1;
548        tr_tier * tier = NULL;
549
550        for( i=0; i<n; ++i )
551        {
552            const tr_tracker_info * info = infos[i];
553
554            if( info->tier != tierIndex )
555                tier = NULL;
556
557            tierIndex = info->tier;
558
559            if( tier == NULL ) {
560                tier = tierNew( tor );
561                dbgmsg( tier, "adding tier" );
562                tr_ptrArrayAppend( &tiers->tiers, tier );
563            }
564
565            tierAddTracker( tier, info->announce, info->scrape, info->id );
566        }
567    }
568
569    tr_free( infos );
570}
571
572tr_torrent_tiers *
573tr_announcerAddTorrent( tr_torrent           * tor,
574                        tr_tracker_callback  * callback,
575                        void                 * callbackData )
576{
577    tr_torrent_tiers * tiers;
578
579    assert( tr_isTorrent( tor ) );
580
581    tiers = tiersNew( );
582    tiers->callback = callback;
583    tiers->callbackData = callbackData;
584
585    addTorrentToTier( tiers, tor );
586
587    return tiers;
588}
589
590/***
591****
592***/
593
594static tr_bool
595tierCanManualAnnounce( const tr_tier * tier )
596{
597    return tier->manualAnnounceAllowedAt <= tr_time( );
598}
599
600tr_bool
601tr_announcerCanManualAnnounce( const tr_torrent * tor )
602{
603    int i;
604    int n;
605    const tr_tier ** tiers;
606
607    assert( tr_isTorrent( tor ) );
608    assert( tor->tiers != NULL );
609
610    if( !tor->isRunning )
611        return FALSE;
612
613    n = tr_ptrArraySize( &tor->tiers->tiers );
614    tiers = (const tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
615    for( i=0; i<n; ++i )
616        if( tierCanManualAnnounce( tiers[i] ) )
617            return TRUE;
618
619    return FALSE;
620}
621
622time_t
623tr_announcerNextManualAnnounce( const tr_torrent * tor )
624{
625    int i;
626    int n;
627    const tr_torrent_tiers * tiers;
628    time_t ret = ~(time_t)0;
629
630    assert( tr_isTorrent( tor  ) );
631
632    tiers = tor->tiers;
633    n = tr_ptrArraySize( &tiers->tiers );
634    for( i=0; i<n; ++i ) {
635        tr_tier * tier = tr_ptrArrayNth( (tr_ptrArray*)&tiers->tiers, i );
636        if( tier->isRunning )
637            ret = MIN( ret, tier->manualAnnounceAllowedAt );
638    }
639
640    return ret;
641}
642
643static void
644dbgmsg_tier_announce_queue( const tr_tier * tier )
645{
646    if( tr_deepLoggingIsActive( ) )
647    {
648        int i;
649        char * str;
650        char name[128];
651        struct evbuffer * buf = evbuffer_new( );
652
653        tier_build_log_name( tier, name, sizeof( name ) );
654        for( i=0; i<tier->announce_event_count; ++i )
655        {
656            const tr_announce_event e = tier->announce_events[i];
657            const char * str = tr_announce_event_get_string( e );
658            evbuffer_add_printf( buf, "[%d:%s]", i, str );
659        }
660        str = evbuffer_free_to_str( buf );
661        tr_deepLog( __FILE__, __LINE__, name, "announce queue is %s", str );
662        tr_free( str );
663    }
664}
665
666static void
667tier_announce_remove_trailing( tr_tier * tier, tr_announce_event e )
668{
669    while( ( tier->announce_event_count > 0 ) && ( tier->announce_events[tier->announce_event_count-1] == e ) )
670        --tier->announce_event_count;
671}
672
673static void
674tier_announce_event_push( tr_tier * tier, tr_announce_event e, time_t announceAt )
675{
676    int i;
677
678    assert( tier != NULL );
679
680    dbgmsg_tier_announce_queue( tier );
681    dbgmsg( tier, "appending \"%s\" to announce queue", tr_announce_event_get_string( e ) );
682
683    if( tier->announce_event_count > 0 )
684    {
685        /* special case #1: if we're adding a "stopped" event,
686         * dump everything leading up to it except "completed" */
687        if( e == TR_ANNOUNCE_EVENT_STOPPED ) {
688            tr_bool has_completed = FALSE;
689            const tr_announce_event c = TR_ANNOUNCE_EVENT_COMPLETED;
690            for( i=0; !has_completed && i<tier->announce_event_count; ++i )
691                has_completed = c == tier->announce_events[i];
692            tier->announce_event_count = 0;
693            if( has_completed )
694                tier->announce_events[tier->announce_event_count++] = c;
695        }
696
697        /* special case #2: dump all empty strings leading up to this event */
698        tier_announce_remove_trailing( tier, TR_ANNOUNCE_EVENT_NONE );
699
700        /* special case #3: no consecutive duplicates */
701        tier_announce_remove_trailing( tier, e );
702    }
703
704    /* make room in the array for another event */
705    if( tier->announce_event_alloc <= tier->announce_event_count ) {
706        tier->announce_event_alloc += 4;
707        tier->announce_events = tr_renew( tr_announce_event,
708                                          tier->announce_events,
709                                          tier->announce_event_alloc );
710    }
711
712    /* add it */
713    tier->announce_events[tier->announce_event_count++] = e;
714    tier->announceAt = announceAt;
715
716    dbgmsg_tier_announce_queue( tier );
717    dbgmsg( tier, "announcing in %d seconds", (int)difftime(announceAt,tr_time()) );
718}
719
720static tr_announce_event
721tier_announce_event_pull( tr_tier * tier )
722{
723    const tr_announce_event e = tier->announce_events[0];
724
725    tr_removeElementFromArray( tier->announce_events,
726                               0, sizeof( tr_announce_event ),
727                               tier->announce_event_count-- );
728
729    return e;
730}
731
732static void
733torrentAddAnnounce( tr_torrent * tor, tr_announce_event e, time_t announceAt )
734{
735    int i;
736    int n;
737    tr_torrent_tiers * tiers;
738
739    assert( tr_isTorrent( tor ) );
740
741    tiers = tor->tiers;
742    n = tr_ptrArraySize( &tiers->tiers );
743    for( i=0; i<n; ++i )
744    {
745        tr_tier * tier = tr_ptrArrayNth( &tiers->tiers, i );
746        tier_announce_event_push( tier, e, announceAt );
747    }
748}
749
750void
751tr_announcerTorrentStarted( tr_torrent * tor )
752{
753    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STARTED, tr_time( ) );
754}
755void
756tr_announcerManualAnnounce( tr_torrent * tor )
757{
758    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_NONE, tr_time( ) );
759}
760void
761tr_announcerTorrentStopped( tr_torrent * tor )
762{
763    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time( ) );
764}
765void
766tr_announcerTorrentCompleted( tr_torrent * tor )
767{
768    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time( ) );
769}
770void
771tr_announcerChangeMyPort( tr_torrent * tor )
772{
773    tr_announcerTorrentStarted( tor );
774}
775
776/***
777****
778***/
779
780void
781tr_announcerAddBytes( tr_torrent * tor, int type, uint32_t byteCount )
782{
783    int i, n;
784    tr_torrent_tiers * tiers;
785
786    assert( tr_isTorrent( tor ) );
787    assert( type==TR_ANN_UP || type==TR_ANN_DOWN || type==TR_ANN_CORRUPT );
788
789    tiers = tor->tiers;
790    n = tr_ptrArraySize( &tiers->tiers );
791    for( i=0; i<n; ++i )
792    {
793        tr_tier * tier = tr_ptrArrayNth( &tiers->tiers, i );
794        tier->byteCounts[ type ] += byteCount;
795    }
796}
797
798/***
799****
800***/
801
802static tr_announce_request *
803announce_request_new( const tr_announcer  * announcer,
804                      const tr_torrent    * tor,
805                      const tr_tier       * tier,
806                      tr_announce_event     event )
807{
808    tr_announce_request * req = tr_new0( tr_announce_request, 1 );
809    req->url = tr_strdup( tier->currentTracker->announce );
810    req->tracker_id_str = tr_strdup( tier->currentTracker->tracker_id_str );
811    memcpy( req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH );
812    memcpy( req->peer_id, tor->peer_id, PEER_ID_LEN );
813    req->up = tier->byteCounts[TR_ANN_UP];
814    req->down = tier->byteCounts[TR_ANN_DOWN];
815    req->corrupt = tier->byteCounts[TR_ANN_CORRUPT];
816    req->left = tr_cpLeftUntilComplete( &tor->completion ),
817    req->event = event;
818    req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT;
819    req->key = announcer->key;
820    req->partial_seed = tr_cpGetStatus( &tor->completion ) == TR_PARTIAL_SEED;
821    tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) );
822    return req;
823}
824
825void
826tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor )
827{
828    assert( tr_isTorrent( tor ) );
829
830    if( tor->tiers )
831    {
832        int i;
833        const int n = tr_ptrArraySize( &tor->tiers->tiers );
834        for( i=0; i<n; ++i )
835        {
836            tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
837            if( tier->isRunning )
838            {
839                const tr_announce_event e = TR_ANNOUNCE_EVENT_STOPPED;
840                tr_announce_request * req = announce_request_new( announcer, tor, tier, e );
841                tr_ptrArrayInsertSorted( &announcer->stops, req, compareStops );
842            }
843        }
844
845        tiersFree( tor->tiers );
846        tor->tiers = NULL;
847    }
848}
849
850static int
851getRetryInterval( const tr_tracker * t )
852{
853    int minutes;
854    const unsigned int jitter_seconds = tr_cryptoWeakRandInt( 60 );
855    switch( t->consecutiveAnnounceFailures ) {
856        case 0:  minutes =   1; break;
857        case 1:  minutes =   5; break;
858        case 2:  minutes =  15; break;
859        case 3:  minutes =  30; break;
860        case 4:  minutes =  60; break;
861        default: minutes = 120; break;
862    }
863    return ( minutes * 60 ) + jitter_seconds;
864}
865
866struct announce_data
867{
868    int tierId;
869    time_t timeSent;
870    tr_announce_event event;
871
872    /** If the request succeeds, the value for tier's "isRunning" flag */
873    tr_bool isRunningOnSuccess;
874};
875
876static void
877on_announce_error( tr_tier * tier, const char * err, tr_announce_event e )
878{
879    int interval;
880
881    /* set the error message */
882    dbgmsg( tier, "%s", err );
883    tr_torinf( tier->tor, "%s", err );
884    tr_strlcpy( tier->lastAnnounceStr, err, sizeof( tier->lastAnnounceStr ) );
885
886    /* switch to the next tracker */
887    tierIncrementTracker( tier );
888
889    /* schedule a reannounce */
890    interval = getRetryInterval( tier->currentTracker );
891    dbgmsg( tier, "Retrying announce in %d seconds.", interval );
892    tier_announce_event_push( tier, e, tr_time( ) + interval );
893}
894
895static void
896on_announce_done( tr_session                  * session,
897                  const tr_announce_response  * response,
898                  void                        * vdata )
899{
900    tr_announcer * announcer = session->announcer;
901    struct announce_data * data = vdata;
902    tr_tier * tier = getTier( announcer, response->info_hash, data->tierId );
903    const time_t now = tr_time( );
904    const tr_announce_event event = data->event;
905
906    if( announcer )
907        ++announcer->slotsAvailable;
908
909    if( tier != NULL )
910    {
911        tr_tracker * tracker;
912
913        dbgmsg( tier, "Got announce response: "
914                      "connected:%d "
915                      "timeout:%d "
916                      "seeders:%d "
917                      "leechers:%d "
918                      "downloads:%d "
919                      "interval:%d "
920                      "min_interval:%d "
921                      "tracker_id_str:%s "
922                      "pex:%zu "
923                      "pex6:%zu "
924                      "err:%s "
925                      "warn:%s",
926                      (int)response->did_connect,
927                      (int)response->did_timeout,
928                      response->seeders,
929                      response->leechers,
930                      response->downloads,
931                      response->interval,
932                      response->min_interval,
933                      response->tracker_id_str ? response->tracker_id_str : "none",
934                      response->pex_count,
935                      response->pex6_count,
936                      response->errmsg ? response->errmsg : "none",
937                      response->warning ? response->warning : "none" );
938
939        tier->lastAnnounceTime = now;
940        tier->lastAnnounceTimedOut = response->did_timeout;
941        tier->lastAnnounceSucceeded = FALSE;
942        tier->isAnnouncing = FALSE;
943        tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
944
945        if(( tracker = tier->currentTracker ))
946            ++tracker->consecutiveAnnounceFailures;
947
948        if( !response->did_connect )
949        {
950            on_announce_error( tier, _( "Could not connect to tracker" ), event );
951        }
952        else if( response->did_timeout )
953        {
954            on_announce_error( tier, _( "Tracker did not respond" ), event );
955        }
956        else if( response->errmsg )
957        {
958            publishError( tier, response->errmsg );
959            on_announce_error( tier, response->errmsg, event );
960        }
961        else
962        {
963            int i; 
964            const char * str;
965            const tr_bool isStopped = event == TR_ANNOUNCE_EVENT_STOPPED;
966
967            publishErrorClear( tier );
968
969            if(( tracker = tier->currentTracker ))
970                tracker->consecutiveAnnounceFailures = 0;
971
972            if(( str = response->warning ))
973            {
974                tr_strlcpy( tier->lastAnnounceStr, str, sizeof( tier->lastAnnounceStr ) );
975                dbgmsg( tier, "tracker gave \"%s\"", str );
976                publishWarning( tier, str );
977            }
978
979            if(( i = response->min_interval ))
980                tier->announceMinIntervalSec = i;
981
982            if(( i = response->interval ))
983                tier->announceIntervalSec = i;
984
985            if(( str = response->tracker_id_str ))
986            {
987                tr_free( tier->currentTracker->tracker_id_str );
988                tier->currentTracker->tracker_id_str = tr_strdup( str );
989            }
990
991            tier->currentTracker->seederCount = response->seeders;
992            tier->currentTracker->leecherCount = response->leechers;
993            tier->currentTracker->downloadCount = response->downloads;
994
995            if( response->pex_count > 0 )
996                publishPeersPex( tier, response->seeders, response->leechers,
997                                 response->pex, response->pex_count );
998
999            if( response->pex6_count > 0 )
1000                publishPeersPex( tier, response->seeders, response->leechers,
1001                                 response->pex6, response->pex6_count );
1002
1003            if( !*tier->lastAnnounceStr )
1004                tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
1005                            sizeof( tier->lastAnnounceStr ) );
1006
1007            tier->isRunning = data->isRunningOnSuccess;
1008            tier->scrapeAt = now + tier->scrapeIntervalSec;
1009            tier->lastScrapeTime = now;
1010            tier->lastScrapeSucceeded = TRUE;
1011            tier->lastAnnounceSucceeded = TRUE;
1012            tier->lastAnnouncePeerCount = response->pex_count
1013                                        + response->pex6_count;
1014
1015            if( isStopped )
1016            {
1017                /* now that we've successfully stopped the torrent,
1018                 * we can reset the up/down/corrupt count we've kept
1019                 * for this tracker */
1020                tier->byteCounts[ TR_ANN_UP ] = 0;
1021                tier->byteCounts[ TR_ANN_DOWN ] = 0;
1022                tier->byteCounts[ TR_ANN_CORRUPT ] = 0;
1023            }
1024
1025            if( !isStopped && !tier->announce_event_count )
1026            {
1027                /* the queue is empty, so enqueue a perodic update */
1028                i = tier->announceIntervalSec;
1029                dbgmsg( tier, "Sending periodic reannounce in %d seconds", i );
1030                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_NONE, now + i );
1031            }
1032        }
1033    }
1034
1035    tr_free( data );
1036}
1037
1038static void
1039announce_request_delegate( tr_announcer               * announcer,
1040                           tr_announce_request        * request,
1041                           tr_announce_response_func  * callback,
1042                           void                       * callback_data )
1043{
1044    tr_session * session = announcer->session;
1045
1046    if( !memcmp( request->url, "http", 4 ) )
1047        tr_tracker_http_announce( session, request, callback, callback_data );
1048    else
1049        abort();//fprintf( stderr, "can't handle [%s] yet\n", request->url );
1050
1051    tr_free( request->tracker_id_str );
1052    tr_free( request->url );
1053    tr_free( request );
1054}
1055
1056static void
1057tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1058{
1059    tr_announce_event announce_event;
1060    tr_announce_request * req;
1061    struct announce_data * data;
1062    const tr_torrent * tor = tier->tor;
1063    const time_t now = tr_time( );
1064
1065    assert( !tier->isAnnouncing );
1066    assert( tier->announce_event_count > 0 );
1067
1068    announce_event = tier_announce_event_pull( tier );
1069    req = announce_request_new( announcer, tor, tier, announce_event );
1070
1071    data = tr_new0( struct announce_data, 1 );
1072    data->tierId = tier->key;
1073    data->isRunningOnSuccess = tor->isRunning;
1074    data->timeSent = now;
1075    data->event = announce_event;
1076
1077    tier->isAnnouncing = TRUE;
1078    tier->lastAnnounceStartTime = now;
1079    --announcer->slotsAvailable;
1080
1081    announce_request_delegate( announcer, req, on_announce_done, data );
1082}
1083
1084/***
1085****
1086****  SCRAPE
1087****
1088***/
1089
1090static void
1091on_scrape_error( tr_tier * tier, const char * errmsg )
1092{
1093    int interval;
1094
1095    /* set the error message */
1096    dbgmsg( tier, "Scrape error: %s", errmsg );
1097    tr_torinf( tier->tor, "Scrape error: %s", errmsg );
1098    tr_strlcpy( tier->lastScrapeStr, errmsg, sizeof( tier->lastScrapeStr ) );
1099
1100    /* switch to the next tracker */
1101    tierIncrementTracker( tier );
1102
1103    /* schedule a rescrape */
1104    interval = getRetryInterval( tier->currentTracker );
1105    dbgmsg( tier, "Retrying scrape in %d seconds.", interval );
1106    tier->lastScrapeSucceeded = FALSE;
1107    tier->scrapeAt = tr_time() + interval;
1108}
1109
1110static tr_tier *
1111find_tier( tr_torrent * tor, const char * url )
1112{
1113    int i;
1114    int n = tr_ptrArraySize( &tor->tiers->tiers );
1115    tr_tier ** tiers = (tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
1116   
1117    for( i=0; i<n; ++i ) {
1118        tr_tracker * tracker = tiers[i]->currentTracker;
1119        if( tracker && !tr_strcmp0( tracker->scrape, url ) )
1120            return tiers[i];
1121    }
1122
1123    return NULL;
1124}
1125
1126static void
1127on_scrape_done( tr_session                * session,
1128                const tr_scrape_response  * response,
1129                void                      * user_data UNUSED )
1130{
1131    int i;
1132    const time_t now = tr_time( );
1133    tr_announcer * announcer = session->announcer;
1134
1135    for( i=0; i<response->row_count; ++i )
1136    {
1137        const struct tr_scrape_response_row * row = &response->rows[i];
1138        tr_torrent * tor = tr_torrentFindFromHash( session, row->info_hash );
1139
1140        if( tor != NULL )
1141        {
1142            tr_tier * tier = find_tier( tor, response->url );
1143
1144            if( tier != NULL )
1145            {
1146                dbgmsg( tier, "scraped url:%s -- "
1147                              "did_connect:%d "
1148                              "did_timeout:%d "
1149                              "seeders:%d "
1150                              "leechers:%d "
1151                              "downloads:%d "
1152                              "downloaders:%d "
1153                              "min_request_interval:%d "
1154                              "err:%s ",
1155                              response->url,
1156                              (int)response->did_connect,
1157                              (int)response->did_timeout,
1158                              row->seeders,
1159                              row->leechers,
1160                              row->downloads,
1161                              row->downloaders,
1162                              response->min_request_interval,
1163                              response->errmsg ? response->errmsg : "none" );
1164
1165                tier->isScraping = FALSE;
1166                tier->lastScrapeTime = now;
1167                tier->lastScrapeSucceeded = FALSE;
1168                tier->lastScrapeTimedOut = response->did_timeout;
1169
1170                if( !response->did_connect )
1171                {
1172                    on_scrape_error( tier, _( "Could not connect to tracker" ) );
1173                }
1174                else if( response->did_timeout )
1175                {
1176                    on_scrape_error( tier, _( "Tracker did not respond" ) );
1177                }
1178                else if( response->errmsg )
1179                {
1180                    on_scrape_error( tier, response->errmsg );
1181                }
1182                else
1183                {
1184                    tr_tracker * tracker;
1185
1186                    tier->lastScrapeSucceeded = TRUE;
1187                    tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC,
1188                                                   response->min_request_interval );
1189                    tier->scrapeAt = now + tier->scrapeIntervalSec;
1190                    tr_tordbg( tier->tor, "Scrape successful. Rescraping in %d seconds.",
1191                               tier->scrapeIntervalSec );
1192
1193                    if(( tracker = tier->currentTracker ))
1194                    {
1195                        tracker->seederCount = row->seeders;
1196                        tracker->leecherCount = row->leechers;
1197                        tracker->downloadCount = row->downloads;
1198                        tracker->downloaderCount = row->downloaders;
1199                    }
1200                }
1201            }
1202        }
1203    }
1204
1205    if( announcer )
1206        ++announcer->slotsAvailable;
1207}
1208
1209static void
1210scrape_request_delegate( tr_announcer             * announcer,
1211                         tr_scrape_request        * request,
1212                         tr_scrape_response_func  * callback,
1213                         void                     * callback_data )
1214{
1215    tr_session * session = announcer->session;
1216
1217    if( !memcmp( request->url, "http", 4 ) )
1218        tr_tracker_http_scrape( session, request, callback, callback_data );
1219    else
1220        abort();//fprintf( stderr, "can't handle [%s] yet\n", request->url );
1221}
1222
1223static void
1224tierScrape( tr_announcer * announcer, tr_tier * tier )
1225{
1226    tr_scrape_request request;
1227
1228    /* sanity clause */
1229    assert( tier );
1230    assert( !tier->isScraping );
1231    assert( tier->currentTracker != NULL );
1232    assert( tr_isTorrent( tier->tor ) );
1233
1234    /* initialize the request */
1235    request.url = tier->currentTracker->scrape;
1236    request.info_hash_count = 1;
1237    memcpy( request.info_hash[0], tier->tor->info.hash, SHA_DIGEST_LENGTH );
1238    tier_build_log_name( tier, request.log_name, sizeof( request.log_name ) );
1239
1240    /* start scraping */
1241    dbgmsg( tier, "scraping \"%s\"", request.url );
1242    tier->isScraping = TRUE;
1243    tier->lastScrapeStartTime = tr_time( );
1244    --announcer->slotsAvailable;
1245    scrape_request_delegate( announcer, &request, on_scrape_done, NULL );
1246}
1247
1248static void
1249flushCloseMessages( tr_announcer * announcer )
1250{
1251    int i;
1252    const int n = tr_ptrArraySize( &announcer->stops );
1253
1254    for( i=0; i<n; ++i )
1255        announce_request_delegate( announcer, tr_ptrArrayNth( &announcer->stops, i ), NULL, NULL );
1256
1257    tr_ptrArrayClear( &announcer->stops );
1258}
1259
1260static tr_bool
1261tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1262{
1263    return !tier->isAnnouncing
1264        && !tier->isScraping
1265        && ( tier->announceAt != 0 )
1266        && ( tier->announceAt <= now )
1267        && ( tier->announce_event_count > 0 );
1268}
1269
1270static tr_bool
1271tierNeedsToScrape( const tr_tier * tier, const time_t now )
1272{
1273    return ( !tier->isScraping )
1274        && ( tier->scrapeAt != 0 )
1275        && ( tier->scrapeAt <= now )
1276        && ( tier->currentTracker != NULL )
1277        && ( tier->currentTracker->scrape != NULL );
1278}
1279
1280static int
1281compareTiers( const void * va, const void * vb )
1282{
1283    int ret;
1284    const tr_tier * a = *(const tr_tier**)va;
1285    const tr_tier * b = *(const tr_tier**)vb;
1286
1287    /* primary key: larger stats come before smaller */
1288    ret = compareTransfer( a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN],
1289                           b->byteCounts[TR_ANN_UP], b->byteCounts[TR_ANN_DOWN] );
1290
1291    /* secondary key: announcements that have been waiting longer go first */
1292    if( !ret && ( a->announceAt != b->announceAt ) )
1293        ret = a->announceAt < b->announceAt ? -1 : 1;
1294
1295    return ret;
1296}
1297
1298static void
1299announceMore( tr_announcer * announcer )
1300{
1301    tr_torrent * tor = NULL;
1302    const time_t now = tr_time( );
1303
1304    dbgmsg( NULL, "announceMore: slotsAvailable is %d", announcer->slotsAvailable );
1305
1306    if( announcer->slotsAvailable > 0 )
1307    {
1308        int i;
1309        int n;
1310        tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1311        tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1312
1313        /* build a list of tiers that need to be announced */
1314        while(( tor = tr_torrentNext( announcer->session, tor ))) {
1315            if( tor->tiers ) {
1316                n = tr_ptrArraySize( &tor->tiers->tiers );
1317                for( i=0; i<n; ++i ) {
1318                    tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1319                    if( tierNeedsToAnnounce( tier, now ) )
1320                        tr_ptrArrayAppend( &announceMe, tier );
1321                    else if( tierNeedsToScrape( tier, now ) )
1322                        tr_ptrArrayAppend( &scrapeMe, tier );
1323                }
1324            }
1325        }
1326
1327        /* if there are more tiers than slots available, prioritize */
1328        n = tr_ptrArraySize( &announceMe );
1329        if( n > announcer->slotsAvailable )
1330            qsort( tr_ptrArrayBase( &announceMe ), n, sizeof( tr_tier * ), compareTiers );
1331
1332        /* announce some */
1333        n = MIN( tr_ptrArraySize( &announceMe ), announcer->slotsAvailable );
1334        for( i=0; i<n; ++i ) {
1335            tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1336            dbgmsg( tier, "announcing tier %d of %d", i, n );
1337            tierAnnounce( announcer, tier );
1338        }
1339
1340        /* scrape some */
1341        /* FIXME: multiscrape */
1342        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->slotsAvailable );
1343        for( i=0; i<n; ++i ) {
1344            tr_tier * tier = tr_ptrArrayNth( &scrapeMe, i );
1345            dbgmsg( tier, "scraping tier %d of %d", (i+1), n );
1346            tierScrape( announcer, tier );
1347        }
1348
1349#if 0
1350char timebuf[64];
1351tr_getLogTimeStr( timebuf, 64 );
1352fprintf( 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) );
1353#endif
1354
1355        /* cleanup */
1356        tr_ptrArrayDestruct( &scrapeMe, NULL );
1357        tr_ptrArrayDestruct( &announceMe, NULL );
1358    }
1359
1360    tor = NULL;
1361    while(( tor = tr_torrentNext( announcer->session, tor ))) {
1362        if( tor->dhtAnnounceAt <= now ) {
1363            if( tor->isRunning && tr_torrentAllowsDHT(tor) ) {
1364                int rc;
1365                rc = tr_dhtAnnounce(tor, AF_INET, 1);
1366                if(rc == 0)
1367                    /* The DHT is not ready yet. Try again soon. */
1368                    tor->dhtAnnounceAt = now + 5 + tr_cryptoWeakRandInt( 5 );
1369                else
1370                    /* We should announce at least once every 30 minutes. */
1371                    tor->dhtAnnounceAt =
1372                        now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1373            }
1374        }
1375
1376        if( tor->dhtAnnounce6At <= now ) {
1377            if( tor->isRunning && tr_torrentAllowsDHT(tor) ) {
1378                int rc;
1379                rc = tr_dhtAnnounce(tor, AF_INET6, 1);
1380                if(rc == 0)
1381                    tor->dhtAnnounce6At = now + 5 + tr_cryptoWeakRandInt( 5 );
1382                else
1383                    tor->dhtAnnounce6At =
1384                        now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1385            }
1386        }
1387    }
1388
1389    /* Local Peer Discovery */
1390    if( announcer->lpdHouseKeepingAt <= now )
1391    {
1392        tr_lpdAnnounceMore( now, LPD_HOUSEKEEPING_INTERVAL_SECS );
1393
1394        /* reschedule more LDS announces for ( the future + jitter ) */
1395        announcer->lpdHouseKeepingAt =
1396            calcRescheduleWithJitter( LPD_HOUSEKEEPING_INTERVAL_SECS );
1397    }
1398}
1399
1400static void
1401onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1402{
1403    tr_announcer * announcer = vannouncer;
1404    tr_sessionLock( announcer->session );
1405
1406    /* maybe send out some "stopped" messages for closed torrents */
1407    flushCloseMessages( announcer );
1408
1409    /* maybe send out some announcements to trackers */
1410    announceMore( announcer );
1411
1412    /* set up the next timer */
1413    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1414
1415    tr_sessionUnlock( announcer->session );
1416}
1417
1418/***
1419****
1420***/
1421
1422tr_tracker_stat *
1423tr_announcerStats( const tr_torrent * torrent,
1424                   int              * setmeTrackerCount )
1425{
1426    int i;
1427    int n;
1428    int out = 0;
1429    int tierCount;
1430    tr_tracker_stat * ret;
1431    const time_t now = tr_time( );
1432
1433    assert( tr_isTorrent( torrent ) );
1434    assert( tr_torrentIsLocked( torrent ) );
1435
1436    /* count the trackers... */
1437    tierCount = tr_ptrArraySize( &torrent->tiers->tiers );
1438    for( i=n=0; i<tierCount; ++i ) {
1439        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1440        n += tr_ptrArraySize( &tier->trackers );
1441    }
1442
1443    /* alloc the stats */
1444    *setmeTrackerCount = n;
1445    ret = tr_new0( tr_tracker_stat, n );
1446
1447    /* populate the stats */
1448    tierCount = tr_ptrArraySize( &torrent->tiers->tiers );
1449    for( i=0; i<tierCount; ++i )
1450    {
1451        int j;
1452        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1453        n = tr_ptrArraySize( &tier->trackers );
1454        for( j=0; j<n; ++j )
1455        {
1456            const tr_tracker * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
1457            tr_tracker_stat * st = ret + out++;
1458
1459            st->id = tracker->id;
1460            tr_strlcpy( st->host, tracker->hostname, sizeof( st->host ) );
1461            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1462            st->tier = i;
1463            st->isBackup = tracker != tier->currentTracker;
1464            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1465            if( tracker->scrape )
1466                tr_strlcpy( st->scrape, tracker->scrape, sizeof( st->scrape ) );
1467            else
1468                st->scrape[0] = '\0';
1469
1470            st->seederCount = tracker->seederCount;
1471            st->leecherCount = tracker->leecherCount;
1472            st->downloadCount = tracker->downloadCount;
1473
1474            if( st->isBackup )
1475            {
1476                st->scrapeState = TR_TRACKER_INACTIVE;
1477                st->announceState = TR_TRACKER_INACTIVE;
1478                st->nextScrapeTime = 0;
1479                st->nextAnnounceTime = 0;
1480            }
1481            else
1482            {
1483                if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
1484                    st->lastScrapeTime = tier->lastScrapeTime;
1485                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1486                    st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
1487                    tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, sizeof( st->lastScrapeResult ) );
1488                }
1489
1490                if( tier->isScraping )
1491                    st->scrapeState = TR_TRACKER_ACTIVE;
1492                else if( !tier->scrapeAt )
1493                    st->scrapeState = TR_TRACKER_INACTIVE;
1494                else if( tier->scrapeAt > now )
1495                {
1496                    st->scrapeState = TR_TRACKER_WAITING;
1497                    st->nextScrapeTime = tier->scrapeAt;
1498                }
1499                else
1500                    st->scrapeState = TR_TRACKER_QUEUED;
1501
1502                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1503
1504                if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
1505                    st->lastAnnounceTime = tier->lastAnnounceTime;
1506                    tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, sizeof( st->lastAnnounceResult ) );
1507                    st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
1508                    st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
1509                    st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1510                }
1511
1512                if( tier->isAnnouncing )
1513                    st->announceState = TR_TRACKER_ACTIVE;
1514                else if( !torrent->isRunning || !tier->announceAt )
1515                    st->announceState = TR_TRACKER_INACTIVE;
1516                else if( tier->announceAt > now )
1517                {
1518                    st->announceState = TR_TRACKER_WAITING;
1519                    st->nextAnnounceTime = tier->announceAt;
1520                }
1521                else
1522                    st->announceState = TR_TRACKER_QUEUED;
1523            }
1524        }
1525    }
1526
1527    return ret;
1528}
1529
1530void
1531tr_announcerStatsFree( tr_tracker_stat * trackers,
1532                       int trackerCount UNUSED )
1533{
1534    tr_free( trackers );
1535}
1536
1537/***
1538****
1539***/
1540
1541static void
1542trackerItemCopyAttributes( tr_tracker * t, const tr_tracker * o )
1543{
1544    assert( t != o );
1545    assert( t != NULL );
1546    assert( o != NULL );
1547
1548    t->seederCount = o->seederCount;
1549    t->leecherCount = o->leecherCount;
1550    t->downloadCount = o->downloadCount;
1551    t->downloaderCount = o->downloaderCount;
1552}
1553
1554static void
1555tierCopyAttributes( tr_tier * t, const tr_tier * o )
1556{
1557    tr_tier bak;
1558
1559    assert( t != NULL );
1560    assert( o != NULL );
1561    assert( t != o );
1562
1563    bak = *t;
1564    *t = *o;
1565    t->tor = bak.tor;
1566    t->trackers = bak.trackers;
1567    t->announce_events = tr_memdup( o->announce_events, sizeof( tr_announce_event ) * o->announce_event_count );
1568    t->announce_event_count = o->announce_event_count;
1569    t->announce_event_alloc = o->announce_event_count;
1570    t->currentTracker = bak.currentTracker;
1571    t->currentTrackerIndex = bak.currentTrackerIndex;
1572}
1573
1574void
1575tr_announcerResetTorrent( tr_announcer * announcer UNUSED, tr_torrent * tor )
1576{
1577    tr_ptrArray oldTiers = TR_PTR_ARRAY_INIT;
1578
1579    /* if we had tiers already, make a backup of them */
1580    if( tor->tiers != NULL )
1581    {
1582        oldTiers = tor->tiers->tiers;
1583        tor->tiers->tiers = TR_PTR_ARRAY_INIT;
1584    }
1585
1586    /* create the new tier/tracker structs */
1587    addTorrentToTier( tor->tiers, tor );
1588
1589    /* if we had tiers already, merge their state into the new structs */
1590    if( !tr_ptrArrayEmpty( &oldTiers ) )
1591    {
1592        int i, in;
1593        for( i=0, in=tr_ptrArraySize(&oldTiers); i<in; ++i )
1594        {
1595            int j, jn;
1596            const tr_tier * o = tr_ptrArrayNth( &oldTiers, i );
1597
1598            if( o->currentTracker == NULL )
1599                continue;
1600
1601            for( j=0, jn=tr_ptrArraySize(&tor->tiers->tiers); j<jn; ++j )
1602            {
1603                int k, kn;
1604                tr_tier * t = tr_ptrArrayNth(&tor->tiers->tiers,j);
1605
1606                for( k=0, kn=tr_ptrArraySize(&t->trackers); k<kn; ++k )
1607                {
1608                    tr_tracker * item = tr_ptrArrayNth(&t->trackers,k);
1609                    if( strcmp( o->currentTracker->announce, item->announce ) )
1610                        continue;
1611                    tierCopyAttributes( t, o );
1612                    t->currentTracker = item;
1613                    t->currentTrackerIndex = k;
1614                    t->wasCopied = TRUE;
1615                    trackerItemCopyAttributes( item, o->currentTracker );
1616                    dbgmsg( t, "attributes copied to tier %d, tracker %d"
1617                                               "from tier %d, tracker %d",
1618                            i, o->currentTrackerIndex, j, k );
1619
1620                }
1621            }
1622        }
1623    }
1624
1625    /* kickstart any tiers that didn't get started */
1626    if( tor->isRunning )
1627    {
1628        int i, n;
1629        const time_t now = tr_time( );
1630        tr_tier ** tiers = (tr_tier**) tr_ptrArrayPeek( &tor->tiers->tiers, &n );
1631        for( i=0; i<n; ++i ) {
1632            tr_tier * tier = tiers[i];
1633            if( !tier->wasCopied )
1634                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_STARTED, now );
1635        }
1636    }
1637
1638    /* cleanup */
1639    tr_ptrArrayDestruct( &oldTiers, tierFree );
1640}
Note: See TracBrowser for help on using the repository browser.