source: trunk/libtransmission/announcer.c @ 12128

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

(trunk libT) optimization for ocelot.

When seeding on a private tracker, ocelot omits other seeds from the list of peers returned. We can test for this and update our "seed probability" field accordingly.

  • Property svn:keywords set to Date Rev Author Id
File size: 47.6 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 12128 2011-03-11 04:57:53Z 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_bool
721tier_announce_event_pull( tr_tier * tier, tr_announce_event * setme )
722{
723    const tr_bool success = tier->announce_event_count > 0;
724
725    if( success )
726    {
727        *setme = tier->announce_events[0];
728
729        tr_removeElementFromArray( tier->announce_events,
730                                   0, sizeof( tr_announce_event ),
731                                   tier->announce_event_count-- );
732    }
733
734    return success;
735}
736
737static void
738torrentAddAnnounce( tr_torrent * tor, tr_announce_event e, time_t announceAt )
739{
740    int i;
741    int n;
742    tr_torrent_tiers * tiers;
743
744    assert( tr_isTorrent( tor ) );
745
746    tiers = tor->tiers;
747    n = tr_ptrArraySize( &tiers->tiers );
748    for( i=0; i<n; ++i )
749    {
750        tr_tier * tier = tr_ptrArrayNth( &tiers->tiers, i );
751        tier_announce_event_push( tier, e, announceAt );
752    }
753}
754
755void
756tr_announcerTorrentStarted( tr_torrent * tor )
757{
758    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STARTED, tr_time( ) );
759}
760void
761tr_announcerManualAnnounce( tr_torrent * tor )
762{
763    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_NONE, tr_time( ) );
764}
765void
766tr_announcerTorrentStopped( tr_torrent * tor )
767{
768    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time( ) );
769}
770void
771tr_announcerTorrentCompleted( tr_torrent * tor )
772{
773    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time( ) );
774}
775void
776tr_announcerChangeMyPort( tr_torrent * tor )
777{
778    tr_announcerTorrentStarted( tor );
779}
780
781/***
782****
783***/
784
785void
786tr_announcerAddBytes( tr_torrent * tor, int type, uint32_t byteCount )
787{
788    int i, n;
789    tr_torrent_tiers * tiers;
790
791    assert( tr_isTorrent( tor ) );
792    assert( type==TR_ANN_UP || type==TR_ANN_DOWN || type==TR_ANN_CORRUPT );
793
794    tiers = tor->tiers;
795    n = tr_ptrArraySize( &tiers->tiers );
796    for( i=0; i<n; ++i )
797    {
798        tr_tier * tier = tr_ptrArrayNth( &tiers->tiers, i );
799        tier->byteCounts[ type ] += byteCount;
800    }
801}
802
803/***
804****
805***/
806
807static tr_announce_request *
808announce_request_new( const tr_announcer  * announcer,
809                      const tr_torrent    * tor,
810                      const tr_tier       * tier,
811                      tr_announce_event     event )
812{
813    tr_announce_request * req = tr_new0( tr_announce_request, 1 );
814    req->url = tr_strdup( tier->currentTracker->announce );
815    req->tracker_id_str = tr_strdup( tier->currentTracker->tracker_id_str );
816    memcpy( req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH );
817    memcpy( req->peer_id, tor->peer_id, PEER_ID_LEN );
818    req->up = tier->byteCounts[TR_ANN_UP];
819    req->down = tier->byteCounts[TR_ANN_DOWN];
820    req->corrupt = tier->byteCounts[TR_ANN_CORRUPT];
821    req->left = tr_cpLeftUntilComplete( &tor->completion ),
822    req->event = event;
823    req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT;
824    req->key = announcer->key;
825    req->partial_seed = tr_cpGetStatus( &tor->completion ) == TR_PARTIAL_SEED;
826    tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) );
827    return req;
828}
829
830void
831tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor )
832{
833    assert( tr_isTorrent( tor ) );
834
835    if( tor->tiers )
836    {
837        int i;
838        const int n = tr_ptrArraySize( &tor->tiers->tiers );
839        for( i=0; i<n; ++i )
840        {
841            tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
842            if( tier->isRunning )
843            {
844                const tr_announce_event e = TR_ANNOUNCE_EVENT_STOPPED;
845                tr_announce_request * req = announce_request_new( announcer, tor, tier, e );
846                tr_ptrArrayInsertSorted( &announcer->stops, req, compareStops );
847            }
848        }
849
850        tiersFree( tor->tiers );
851        tor->tiers = NULL;
852    }
853}
854
855static int
856getRetryInterval( const tr_tracker * t )
857{
858    int minutes;
859    const unsigned int jitter_seconds = tr_cryptoWeakRandInt( 60 );
860    switch( t->consecutiveAnnounceFailures ) {
861        case 0:  minutes =   1; break;
862        case 1:  minutes =   5; break;
863        case 2:  minutes =  15; break;
864        case 3:  minutes =  30; break;
865        case 4:  minutes =  60; break;
866        default: minutes = 120; break;
867    }
868    return ( minutes * 60 ) + jitter_seconds;
869}
870
871struct announce_data
872{
873    int tierId;
874    time_t timeSent;
875    tr_announce_event event;
876
877    /** If the request succeeds, the value for tier's "isRunning" flag */
878    tr_bool isRunningOnSuccess;
879};
880
881static void
882on_announce_error( tr_tier * tier, const char * err, tr_announce_event e )
883{
884    int interval;
885
886    /* set the error message */
887    dbgmsg( tier, "%s", err );
888    tr_torinf( tier->tor, "%s", err );
889    tr_strlcpy( tier->lastAnnounceStr, err, sizeof( tier->lastAnnounceStr ) );
890
891    /* switch to the next tracker */
892    tierIncrementTracker( tier );
893
894    /* schedule a reannounce */
895    interval = getRetryInterval( tier->currentTracker );
896    dbgmsg( tier, "Retrying announce in %d seconds.", interval );
897    tier_announce_event_push( tier, e, tr_time( ) + interval );
898}
899
900static void
901on_announce_done( tr_session                  * session,
902                  const tr_announce_response  * response,
903                  void                        * vdata )
904{
905    tr_announcer * announcer = session->announcer;
906    struct announce_data * data = vdata;
907    tr_tier * tier = getTier( announcer, response->info_hash, data->tierId );
908    const time_t now = tr_time( );
909    const tr_announce_event event = data->event;
910
911    if( tier != NULL )
912    {
913        tr_tracker * tracker;
914
915        dbgmsg( tier, "Got announce response: "
916                      "connected:%d "
917                      "timeout:%d "
918                      "seeders:%d "
919                      "leechers:%d "
920                      "downloads:%d "
921                      "interval:%d "
922                      "min_interval:%d "
923                      "tracker_id_str:%s "
924                      "pex:%zu "
925                      "pex6:%zu "
926                      "err:%s "
927                      "warn:%s",
928                      (int)response->did_connect,
929                      (int)response->did_timeout,
930                      response->seeders,
931                      response->leechers,
932                      response->downloads,
933                      response->interval,
934                      response->min_interval,
935                      response->tracker_id_str ? response->tracker_id_str : "none",
936                      response->pex_count,
937                      response->pex6_count,
938                      response->errmsg ? response->errmsg : "none",
939                      response->warning ? response->warning : "none" );
940
941        tier->lastAnnounceTime = now;
942        tier->lastAnnounceTimedOut = response->did_timeout;
943        tier->lastAnnounceSucceeded = FALSE;
944        tier->isAnnouncing = FALSE;
945        tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
946
947        if(( tracker = tier->currentTracker ))
948            ++tracker->consecutiveAnnounceFailures;
949
950        if( !response->did_connect )
951        {
952            on_announce_error( tier, _( "Could not connect to tracker" ), event );
953        }
954        else if( response->did_timeout )
955        {
956            on_announce_error( tier, _( "Tracker did not respond" ), event );
957        }
958        else if( response->errmsg )
959        {
960            publishError( tier, response->errmsg );
961            on_announce_error( tier, response->errmsg, event );
962        }
963        else
964        {
965            int i; 
966            const char * str;
967            const tr_bool isStopped = event == TR_ANNOUNCE_EVENT_STOPPED;
968
969            publishErrorClear( tier );
970
971            if(( tracker = tier->currentTracker ))
972                tracker->consecutiveAnnounceFailures = 0;
973
974            if(( str = response->warning ))
975            {
976                tr_strlcpy( tier->lastAnnounceStr, str, sizeof( tier->lastAnnounceStr ) );
977                dbgmsg( tier, "tracker gave \"%s\"", str );
978                publishWarning( tier, str );
979            }
980
981            if(( i = response->min_interval ))
982                tier->announceMinIntervalSec = i;
983
984            if(( i = response->interval ))
985                tier->announceIntervalSec = i;
986
987            if(( str = response->tracker_id_str ))
988            {
989                tr_free( tier->currentTracker->tracker_id_str );
990                tier->currentTracker->tracker_id_str = tr_strdup( str );
991            }
992
993            tier->currentTracker->seederCount = response->seeders;
994            tier->currentTracker->leecherCount = response->leechers;
995            tier->currentTracker->downloadCount = response->downloads;
996
997            if( response->pex_count > 0 )
998                publishPeersPex( tier, response->seeders, response->leechers,
999                                 response->pex, response->pex_count );
1000
1001            if( response->pex6_count > 0 )
1002                publishPeersPex( tier, response->seeders, response->leechers,
1003                                 response->pex6, response->pex6_count );
1004
1005            if( !*tier->lastAnnounceStr )
1006                tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
1007                            sizeof( tier->lastAnnounceStr ) );
1008
1009            tier->isRunning = data->isRunningOnSuccess;
1010            tier->scrapeAt = now + tier->scrapeIntervalSec;
1011            tier->lastScrapeTime = now;
1012            tier->lastScrapeSucceeded = TRUE;
1013            tier->lastAnnounceSucceeded = TRUE;
1014            tier->lastAnnouncePeerCount = response->pex_count
1015                                        + response->pex6_count;
1016
1017            if( isStopped )
1018            {
1019                /* now that we've successfully stopped the torrent,
1020                 * we can reset the up/down/corrupt count we've kept
1021                 * for this tracker */
1022                tier->byteCounts[ TR_ANN_UP ] = 0;
1023                tier->byteCounts[ TR_ANN_DOWN ] = 0;
1024                tier->byteCounts[ TR_ANN_CORRUPT ] = 0;
1025            }
1026
1027            if( !isStopped && !tier->announce_event_count )
1028            {
1029                /* the queue is empty, so enqueue a perodic update */
1030                i = tier->announceIntervalSec;
1031                dbgmsg( tier, "Sending periodic reannounce in %d seconds", i );
1032                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_NONE, now + i );
1033            }
1034        }
1035    }
1036
1037    if( announcer )
1038        ++announcer->slotsAvailable;
1039
1040    tr_free( data );
1041}
1042
1043static void
1044announce_request_delegate( tr_announcer               * announcer,
1045                           tr_announce_request        * request,
1046                           tr_announce_response_func  * callback,
1047                           void                       * callback_data )
1048{
1049    tr_session * session = announcer->session;
1050
1051    if( strstr( request->url, "http://" ) )
1052        tr_tracker_http_announce( session, request, callback, callback_data );
1053    else
1054        fprintf( stderr, "can't handle [%s] yet\n", request->url );
1055
1056    tr_free( request->tracker_id_str );
1057    tr_free( request->url );
1058    tr_free( request );
1059}
1060
1061static void
1062tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1063{
1064    tr_announce_event announce_event;
1065
1066    assert( !tier->isAnnouncing );
1067
1068    if( tier_announce_event_pull( tier, &announce_event ) )
1069    {
1070        struct announce_data * data;
1071        const tr_torrent * tor = tier->tor;
1072        const time_t now = tr_time( );
1073
1074        tr_announce_request * req = announce_request_new( announcer, tor, tier, announce_event );
1075
1076        data = tr_new0( struct announce_data, 1 );
1077        data->tierId = tier->key;
1078        data->isRunningOnSuccess = tor->isRunning;
1079        data->timeSent = now;
1080        data->event = announce_event;
1081
1082        tier->isAnnouncing = TRUE;
1083        tier->lastAnnounceStartTime = now;
1084        --announcer->slotsAvailable;
1085
1086        announce_request_delegate( announcer, req, on_announce_done, data );
1087    }
1088}
1089
1090/***
1091****
1092****  SCRAPE
1093****
1094***/
1095
1096static void
1097on_scrape_error( tr_tier * tier, const char * errmsg )
1098{
1099    int interval;
1100
1101    /* set the error message */
1102    dbgmsg( tier, "Scrape error: %s", errmsg );
1103    tr_torinf( tier->tor, "Scrape error: %s", errmsg );
1104    tr_strlcpy( tier->lastScrapeStr, errmsg, sizeof( tier->lastScrapeStr ) );
1105
1106    /* switch to the next tracker */
1107    tierIncrementTracker( tier );
1108
1109    /* schedule a rescrape */
1110    interval = getRetryInterval( tier->currentTracker );
1111    dbgmsg( tier, "Retrying scrape in %d seconds.", interval );
1112    tier->lastScrapeSucceeded = FALSE;
1113    tier->scrapeAt = tr_time() + interval;
1114}
1115
1116static tr_tier *
1117find_tier( tr_torrent * tor, const char * url )
1118{
1119    int i;
1120    int n = tr_ptrArraySize( &tor->tiers->tiers );
1121    tr_tier ** tiers = (tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
1122   
1123    for( i=0; i<n; ++i ) {
1124        tr_tracker * tracker = tiers[i]->currentTracker;
1125        if( tracker && !tr_strcmp0( tracker->scrape, url ) )
1126            return tiers[i];
1127    }
1128
1129    return NULL;
1130}
1131
1132static void
1133on_scrape_done( tr_session                * session,
1134                const tr_scrape_response  * response,
1135                void                      * user_data UNUSED )
1136{
1137    int i;
1138    const time_t now = tr_time( );
1139    tr_announcer * announcer = session->announcer;
1140
1141    for( i=0; i<response->row_count; ++i )
1142    {
1143        const struct tr_scrape_response_row * row = &response->rows[i];
1144        tr_torrent * tor = tr_torrentFindFromHash( session, row->info_hash );
1145
1146        if( tor != NULL )
1147        {
1148            tr_tier * tier = find_tier( tor, response->url );
1149
1150            if( tier != NULL )
1151            {
1152                dbgmsg( tier, "scraped url:%s -- "
1153                              "did_connect:%d "
1154                              "did_timeout:%d "
1155                              "seeders:%d "
1156                              "leechers:%d "
1157                              "downloads:%d "
1158                              "downloaders:%d "
1159                              "min_request_interval:%d "
1160                              "err:%s ",
1161                              response->url,
1162                              (int)response->did_connect,
1163                              (int)response->did_timeout,
1164                              row->seeders,
1165                              row->leechers,
1166                              row->downloads,
1167                              row->downloaders,
1168                              response->min_request_interval,
1169                              response->errmsg ? response->errmsg : "none" );
1170
1171                tier->isScraping = FALSE;
1172                tier->lastScrapeTime = now;
1173                tier->lastScrapeSucceeded = FALSE;
1174                tier->lastScrapeTimedOut = response->did_timeout;
1175
1176                if( !response->did_connect )
1177                {
1178                    on_scrape_error( tier, _( "Could not connect to tracker" ) );
1179                }
1180                else if( response->did_timeout )
1181                {
1182                    on_scrape_error( tier, _( "Tracker did not respond" ) );
1183                }
1184                else if( response->errmsg )
1185                {
1186                    on_scrape_error( tier, response->errmsg );
1187                }
1188                else
1189                {
1190                    tr_tracker * tracker;
1191
1192                    tier->lastScrapeSucceeded = TRUE;
1193                    tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC,
1194                                                   response->min_request_interval );
1195                    tier->scrapeAt = now + tier->scrapeIntervalSec;
1196                    tr_tordbg( tier->tor, "Scrape successful. Rescraping in %d seconds.",
1197                               tier->scrapeIntervalSec );
1198
1199                    if(( tracker = tier->currentTracker ))
1200                    {
1201                        tracker->seederCount = row->seeders;
1202                        tracker->leecherCount = row->leechers;
1203                        tracker->downloadCount = row->downloads;
1204                        tracker->downloaderCount = row->downloaders;
1205                    }
1206                }
1207            }
1208        }
1209    }
1210
1211    if( announcer )
1212        ++announcer->slotsAvailable;
1213}
1214
1215static void
1216scrape_request_delegate( tr_announcer             * announcer,
1217                         tr_scrape_request        * request,
1218                         tr_scrape_response_func  * callback,
1219                         void                     * callback_data )
1220{
1221    tr_session * session = announcer->session;
1222
1223    if( strstr( request->url, "http://" ) )
1224        tr_tracker_http_scrape( session, request, callback, callback_data );
1225    else
1226        fprintf( stderr, "can't handle [%s] yet\n", request->url );
1227}
1228
1229static void
1230tierScrape( tr_announcer * announcer, tr_tier * tier )
1231{
1232    tr_scrape_request request;
1233
1234    /* sanity clause */
1235    assert( tier );
1236    assert( !tier->isScraping );
1237    assert( tier->currentTracker != NULL );
1238    assert( tr_isTorrent( tier->tor ) );
1239
1240    /* initialize the request */
1241    request.url = tier->currentTracker->scrape;
1242    request.info_hash_count = 1;
1243    memcpy( request.info_hash[0], tier->tor->info.hash, SHA_DIGEST_LENGTH );
1244    tier_build_log_name( tier, request.log_name, sizeof( request.log_name ) );
1245
1246    /* start scraping */
1247    dbgmsg( tier, "scraping \"%s\"", request.url );
1248    tier->isScraping = TRUE;
1249    tier->lastScrapeStartTime = tr_time( );
1250    --announcer->slotsAvailable;
1251    scrape_request_delegate( announcer, &request, on_scrape_done, NULL );
1252}
1253
1254static void
1255flushCloseMessages( tr_announcer * announcer )
1256{
1257    int i;
1258    const int n = tr_ptrArraySize( &announcer->stops );
1259
1260    for( i=0; i<n; ++i )
1261        announce_request_delegate( announcer, tr_ptrArrayNth( &announcer->stops, i ), NULL, NULL );
1262
1263    tr_ptrArrayClear( &announcer->stops );
1264}
1265
1266static tr_bool
1267tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1268{
1269    return !tier->isAnnouncing
1270        && !tier->isScraping
1271        && ( tier->announceAt != 0 )
1272        && ( tier->announceAt <= now )
1273        && ( tier->announce_event_count > 0 );
1274}
1275
1276static tr_bool
1277tierNeedsToScrape( const tr_tier * tier, const time_t now )
1278{
1279    return ( !tier->isScraping )
1280        && ( tier->scrapeAt != 0 )
1281        && ( tier->scrapeAt <= now )
1282        && ( tier->currentTracker != NULL )
1283        && ( tier->currentTracker->scrape != NULL );
1284}
1285
1286static int
1287compareTiers( const void * va, const void * vb )
1288{
1289    int ret;
1290    const tr_tier * a = *(const tr_tier**)va;
1291    const tr_tier * b = *(const tr_tier**)vb;
1292
1293    /* primary key: larger stats come before smaller */
1294    ret = compareTransfer( a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN],
1295                           b->byteCounts[TR_ANN_UP], b->byteCounts[TR_ANN_DOWN] );
1296
1297    /* secondary key: announcements that have been waiting longer go first */
1298    if( !ret && ( a->announceAt != b->announceAt ) )
1299        ret = a->announceAt < b->announceAt ? -1 : 1;
1300
1301    return ret;
1302}
1303
1304static void
1305announceMore( tr_announcer * announcer )
1306{
1307    tr_torrent * tor = NULL;
1308    const time_t now = tr_time( );
1309
1310    if( announcer->slotsAvailable > 0 )
1311    {
1312        int i;
1313        int n;
1314        tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1315        tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1316
1317        /* build a list of tiers that need to be announced */
1318        while(( tor = tr_torrentNext( announcer->session, tor ))) {
1319            if( tor->tiers ) {
1320                n = tr_ptrArraySize( &tor->tiers->tiers );
1321                for( i=0; i<n; ++i ) {
1322                    tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1323                    if( tierNeedsToAnnounce( tier, now ) )
1324                        tr_ptrArrayAppend( &announceMe, tier );
1325                    else if( tierNeedsToScrape( tier, now ) )
1326                        tr_ptrArrayAppend( &scrapeMe, tier );
1327                }
1328            }
1329        }
1330
1331        /* if there are more tiers than slots available, prioritize */
1332        n = tr_ptrArraySize( &announceMe );
1333        if( n > announcer->slotsAvailable )
1334            qsort( tr_ptrArrayBase( &announceMe ), n, sizeof( tr_tier * ), compareTiers );
1335
1336        /* announce some */
1337        n = MIN( tr_ptrArraySize( &announceMe ), announcer->slotsAvailable );
1338        for( i=0; i<n; ++i ) {
1339            tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1340            dbgmsg( tier, "announcing tier %d of %d", i, n );
1341            tierAnnounce( announcer, tier );
1342        }
1343
1344        /* scrape some */
1345        /* FIXME: multiscrape */
1346        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->slotsAvailable );
1347        for( i=0; i<n; ++i ) {
1348            tr_tier * tier = tr_ptrArrayNth( &scrapeMe, i );
1349            dbgmsg( tier, "scraping tier %d of %d", (i+1), n );
1350            tierScrape( announcer, tier );
1351        }
1352
1353#if 0
1354char timebuf[64];
1355tr_getLogTimeStr( timebuf, 64 );
1356fprintf( 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) );
1357#endif
1358
1359        /* cleanup */
1360        tr_ptrArrayDestruct( &scrapeMe, NULL );
1361        tr_ptrArrayDestruct( &announceMe, NULL );
1362    }
1363
1364    tor = NULL;
1365    while(( tor = tr_torrentNext( announcer->session, tor ))) {
1366        if( tor->dhtAnnounceAt <= now ) {
1367            if( tor->isRunning && tr_torrentAllowsDHT(tor) ) {
1368                int rc;
1369                rc = tr_dhtAnnounce(tor, AF_INET, 1);
1370                if(rc == 0)
1371                    /* The DHT is not ready yet. Try again soon. */
1372                    tor->dhtAnnounceAt = now + 5 + tr_cryptoWeakRandInt( 5 );
1373                else
1374                    /* We should announce at least once every 30 minutes. */
1375                    tor->dhtAnnounceAt =
1376                        now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1377            }
1378        }
1379
1380        if( tor->dhtAnnounce6At <= now ) {
1381            if( tor->isRunning && tr_torrentAllowsDHT(tor) ) {
1382                int rc;
1383                rc = tr_dhtAnnounce(tor, AF_INET6, 1);
1384                if(rc == 0)
1385                    tor->dhtAnnounce6At = now + 5 + tr_cryptoWeakRandInt( 5 );
1386                else
1387                    tor->dhtAnnounce6At =
1388                        now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1389            }
1390        }
1391    }
1392
1393    /* Local Peer Discovery */
1394    if( announcer->lpdHouseKeepingAt <= now )
1395    {
1396        tr_lpdAnnounceMore( now, LPD_HOUSEKEEPING_INTERVAL_SECS );
1397
1398        /* reschedule more LDS announces for ( the future + jitter ) */
1399        announcer->lpdHouseKeepingAt =
1400            calcRescheduleWithJitter( LPD_HOUSEKEEPING_INTERVAL_SECS );
1401    }
1402}
1403
1404static void
1405onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1406{
1407    tr_announcer * announcer = vannouncer;
1408    tr_sessionLock( announcer->session );
1409
1410    /* maybe send out some "stopped" messages for closed torrents */
1411    flushCloseMessages( announcer );
1412
1413    /* maybe send out some announcements to trackers */
1414    announceMore( announcer );
1415
1416    /* set up the next timer */
1417    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1418
1419    tr_sessionUnlock( announcer->session );
1420}
1421
1422/***
1423****
1424***/
1425
1426tr_tracker_stat *
1427tr_announcerStats( const tr_torrent * torrent,
1428                   int              * setmeTrackerCount )
1429{
1430    int i;
1431    int n;
1432    int out = 0;
1433    int tierCount;
1434    tr_tracker_stat * ret;
1435    const time_t now = tr_time( );
1436
1437    assert( tr_isTorrent( torrent ) );
1438    assert( tr_torrentIsLocked( torrent ) );
1439
1440    /* count the trackers... */
1441    tierCount = tr_ptrArraySize( &torrent->tiers->tiers );
1442    for( i=n=0; i<tierCount; ++i ) {
1443        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1444        n += tr_ptrArraySize( &tier->trackers );
1445    }
1446
1447    /* alloc the stats */
1448    *setmeTrackerCount = n;
1449    ret = tr_new0( tr_tracker_stat, n );
1450
1451    /* populate the stats */
1452    tierCount = tr_ptrArraySize( &torrent->tiers->tiers );
1453    for( i=0; i<tierCount; ++i )
1454    {
1455        int j;
1456        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1457        n = tr_ptrArraySize( &tier->trackers );
1458        for( j=0; j<n; ++j )
1459        {
1460            const tr_tracker * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
1461            tr_tracker_stat * st = ret + out++;
1462
1463            st->id = tracker->id;
1464            tr_strlcpy( st->host, tracker->hostname, sizeof( st->host ) );
1465            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1466            st->tier = i;
1467            st->isBackup = tracker != tier->currentTracker;
1468            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1469            if( tracker->scrape )
1470                tr_strlcpy( st->scrape, tracker->scrape, sizeof( st->scrape ) );
1471            else
1472                st->scrape[0] = '\0';
1473
1474            st->seederCount = tracker->seederCount;
1475            st->leecherCount = tracker->leecherCount;
1476            st->downloadCount = tracker->downloadCount;
1477
1478            if( st->isBackup )
1479            {
1480                st->scrapeState = TR_TRACKER_INACTIVE;
1481                st->announceState = TR_TRACKER_INACTIVE;
1482                st->nextScrapeTime = 0;
1483                st->nextAnnounceTime = 0;
1484            }
1485            else
1486            {
1487                if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
1488                    st->lastScrapeTime = tier->lastScrapeTime;
1489                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1490                    st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
1491                    tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, sizeof( st->lastScrapeResult ) );
1492                }
1493
1494                if( tier->isScraping )
1495                    st->scrapeState = TR_TRACKER_ACTIVE;
1496                else if( !tier->scrapeAt )
1497                    st->scrapeState = TR_TRACKER_INACTIVE;
1498                else if( tier->scrapeAt > now )
1499                {
1500                    st->scrapeState = TR_TRACKER_WAITING;
1501                    st->nextScrapeTime = tier->scrapeAt;
1502                }
1503                else
1504                    st->scrapeState = TR_TRACKER_QUEUED;
1505
1506                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1507
1508                if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
1509                    st->lastAnnounceTime = tier->lastAnnounceTime;
1510                    tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, sizeof( st->lastAnnounceResult ) );
1511                    st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
1512                    st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
1513                    st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1514                }
1515
1516                if( tier->isAnnouncing )
1517                    st->announceState = TR_TRACKER_ACTIVE;
1518                else if( !torrent->isRunning || !tier->announceAt )
1519                    st->announceState = TR_TRACKER_INACTIVE;
1520                else if( tier->announceAt > now )
1521                {
1522                    st->announceState = TR_TRACKER_WAITING;
1523                    st->nextAnnounceTime = tier->announceAt;
1524                }
1525                else
1526                    st->announceState = TR_TRACKER_QUEUED;
1527            }
1528        }
1529    }
1530
1531    return ret;
1532}
1533
1534void
1535tr_announcerStatsFree( tr_tracker_stat * trackers,
1536                       int trackerCount UNUSED )
1537{
1538    tr_free( trackers );
1539}
1540
1541/***
1542****
1543***/
1544
1545static void
1546trackerItemCopyAttributes( tr_tracker * t, const tr_tracker * o )
1547{
1548    assert( t != o );
1549    assert( t != NULL );
1550    assert( o != NULL );
1551
1552    t->seederCount = o->seederCount;
1553    t->leecherCount = o->leecherCount;
1554    t->downloadCount = o->downloadCount;
1555    t->downloaderCount = o->downloaderCount;
1556}
1557
1558static void
1559tierCopyAttributes( tr_tier * t, const tr_tier * o )
1560{
1561    tr_tier bak;
1562
1563    assert( t != NULL );
1564    assert( o != NULL );
1565    assert( t != o );
1566
1567    bak = *t;
1568    *t = *o;
1569    t->tor = bak.tor;
1570    t->trackers = bak.trackers;
1571    t->announce_events = tr_memdup( o->announce_events, sizeof( tr_announce_event ) * o->announce_event_count );
1572    t->announce_event_count = o->announce_event_count;
1573    t->announce_event_alloc = o->announce_event_count;
1574    t->currentTracker = bak.currentTracker;
1575    t->currentTrackerIndex = bak.currentTrackerIndex;
1576}
1577
1578void
1579tr_announcerResetTorrent( tr_announcer * announcer UNUSED, tr_torrent * tor )
1580{
1581    tr_ptrArray oldTiers = TR_PTR_ARRAY_INIT;
1582
1583    /* if we had tiers already, make a backup of them */
1584    if( tor->tiers != NULL )
1585    {
1586        oldTiers = tor->tiers->tiers;
1587        tor->tiers->tiers = TR_PTR_ARRAY_INIT;
1588    }
1589
1590    /* create the new tier/tracker structs */
1591    addTorrentToTier( tor->tiers, tor );
1592
1593    /* if we had tiers already, merge their state into the new structs */
1594    if( !tr_ptrArrayEmpty( &oldTiers ) )
1595    {
1596        int i, in;
1597        for( i=0, in=tr_ptrArraySize(&oldTiers); i<in; ++i )
1598        {
1599            int j, jn;
1600            const tr_tier * o = tr_ptrArrayNth( &oldTiers, i );
1601
1602            if( o->currentTracker == NULL )
1603                continue;
1604
1605            for( j=0, jn=tr_ptrArraySize(&tor->tiers->tiers); j<jn; ++j )
1606            {
1607                int k, kn;
1608                tr_tier * t = tr_ptrArrayNth(&tor->tiers->tiers,j);
1609
1610                for( k=0, kn=tr_ptrArraySize(&t->trackers); k<kn; ++k )
1611                {
1612                    tr_tracker * item = tr_ptrArrayNth(&t->trackers,k);
1613                    if( strcmp( o->currentTracker->announce, item->announce ) )
1614                        continue;
1615                    tierCopyAttributes( t, o );
1616                    t->currentTracker = item;
1617                    t->currentTrackerIndex = k;
1618                    t->wasCopied = TRUE;
1619                    trackerItemCopyAttributes( item, o->currentTracker );
1620                    dbgmsg( t, "attributes copied to tier %d, tracker %d"
1621                                               "from tier %d, tracker %d",
1622                            i, o->currentTrackerIndex, j, k );
1623
1624                }
1625            }
1626        }
1627    }
1628
1629    /* kickstart any tiers that didn't get started */
1630    if( tor->isRunning )
1631    {
1632        int i, n;
1633        const time_t now = tr_time( );
1634        tr_tier ** tiers = (tr_tier**) tr_ptrArrayPeek( &tor->tiers->tiers, &n );
1635        for( i=0; i<n; ++i ) {
1636            tr_tier * tier = tiers[i];
1637            if( !tier->wasCopied )
1638                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_STARTED, now );
1639        }
1640    }
1641
1642    /* cleanup */
1643    tr_ptrArrayDestruct( &oldTiers, tierFree );
1644}
Note: See TracBrowser for help on using the repository browser.