source: trunk/libtransmission/announcer.c @ 12127

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

(trunk libT) #117 "UDP tracker protocol support (BEP #15)" -- refactor announcer.c so that alternate tracker protocols can be supported.

This commit adds a set of package-visible structs and functions to allow delegating announces and scrapes to different protocol handlers. (Examples: struct tr_announce_request, struct tr_announce_response, struct tr_scrape_request, struct tr_scrape_response.) HTTP is the only protocol handler currently implemented; however, this provides a clean API for other protocol handlers, and having this in trunk will help shake out any bugs in this refactoring.

In addition, logging via the TR_DEBUG_FD environment variable is vastly improved in the announcer module now.

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