source: trunk/libtransmission/announcer.c @ 12190

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

(trunk libT) #117 "UDP tracker protocol support" -- fix trivial r12141 log message typo: s/Unsupported ur/Unsupported url/

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