source: trunk/libtransmission/announcer.c @ 12160

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

(trunk libT) more tweakin'

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