source: trunk/libtransmission/announcer.c @ 12350

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

(trunk libT) fix 2.30b1 memory corruption bug when editing trackers.

The problem was that the new number of trackers was not being kept and the old count was retained. So if the count changed, tr_torrentTrackers() could return dangling pointers to the caller.

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