source: trunk/libtransmission/announcer.c @ 9173

Last change on this file since 9173 was 9173, checked in by charles, 13 years ago

(trunk) copy-by-value changes to tr_tracker_stat

File size: 47.7 KB
Line 
1/*
2 * This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
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:$
11 */
12
13#include <assert.h>
14#include <limits.h>
15
16#include <event.h>
17#include <evhttp.h> /* for HTTP_OK */
18
19#include "transmission.h"
20#include "announcer.h"
21#include "crypto.h"
22#include "net.h"
23#include "ptrarray.h"
24#include "publish.h"
25#include "session.h"
26#include "torrent.h"
27#include "utils.h"
28#include "web.h"
29
30#define dbgmsg( tier, ... ) \
31if( tr_deepLoggingIsActive( ) ) do { \
32  char name[128]; \
33  tr_snprintf( name, sizeof( name ), "[%s--%s]", tr_torrentName( tier->tor ), \
34      ( tier->currentTracker ? tier->currentTracker->host->name : "" ) ); \
35  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
36} while( 0 )
37
38enum
39{
40    /* unless the tracker says otherwise, rescrape this frequently */
41    DEFAULT_SCRAPE_INTERVAL_SEC = ( 60 * 15 ),
42
43    /* unless the tracker says otherwise, this is the announce interval */
44    DEFAULT_ANNOUNCE_INTERVAL_SEC = ( 60 * 10 ),
45
46    /* unless the tracker says otherwise, this is the announce min_interval */
47    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = ( 60 * 2 ),
48
49    /* how long to wait before a reannounce the first time we get an error */
50    FIRST_ANNOUNCE_RETRY_INTERVAL_SEC = 30,
51
52    /* how long to wait before a rescrape the first time we get an error */
53    FIRST_SCRAPE_RETRY_INTERVAL_SEC = 120,
54
55    /* the length of the 'key' argument passed in tracker requests */
56    KEYLEN = 8,
57
58    /* how many scrapes we allow at one time */
59    MAX_CONCURRENT_SCRAPES = 16,
60
61    /* how many announces we allow at one time */
62    MAX_CONCURRENT_ANNOUNCES = 48,
63
64    /* if a tracker takes more than this long to respond,
65     * we treat it as nonresponsive */
66    MAX_TRACKER_RESPONSE_TIME_SECS = ( 60 * 2 ),
67
68    /* the value of the 'numwant' argument passed in tracker requests. */
69    NUMWANT = 200,
70
71    /* how long to put slow (nonresponsive) trackers in the penalty box */
72    SLOW_HOST_PENALTY_SECS = ( 60 * 10 ),
73
74    UPKEEP_INTERVAL_SECS = 1
75
76};
77
78/***
79****
80***/
81
82static int
83compareTransfer( int a_uploaded, int a_downloaded,
84                 int b_uploaded, int b_downloaded )
85{
86    /* higher upload count goes first */
87    if( a_uploaded != b_uploaded )
88        return a_uploaded > b_uploaded ? -1 : 1;
89
90    /* then higher download count goes first */
91    if( a_downloaded != b_downloaded )
92        return a_downloaded > b_downloaded ? -1 : 1;
93
94    return 0;
95}
96
97/***
98****
99***/
100
101/**
102 * used by tr_announcer to recognize nonresponsive
103 * trackers and de-prioritize them
104 */
105typedef struct
106{
107    char * name;
108
109    /* how many seconds it took to get the last tracker response */
110    int lastResponseInterval;
111
112    /* the last time we sent an announce or scrape message */
113    time_t lastRequestTime;
114}
115tr_host;
116
117static int
118compareHosts( const void * va, const void * vb )
119{
120    const tr_host * a = va;
121    const tr_host * b = vb;
122    return strcmp( a->name, b->name );
123}
124
125static int
126compareHostToName( const void * va, const void * vb )
127{
128    const tr_host * a = va;
129    return strcmp( a->name, vb );
130}
131
132static char *
133getHostName( const char * url )
134{
135    int port = 0;
136    char * host = NULL;
137    char * ret;
138    tr_httpParseURL( url, strlen( url ), &host, &port, NULL );
139    ret = tr_strdup_printf( "%s:%d", ( host ? host : "invalid" ), port );
140    tr_free( host );
141    return ret;
142}
143
144static tr_host*
145hostNew( const char * name )
146{
147    tr_host * host = tr_new0( tr_host, 1 );
148    host->name = tr_strdup( name );
149    return host;
150}
151
152static void
153hostFree( void * vhost )
154{
155    tr_host * host = vhost;
156
157    tr_free( host->name );
158    tr_free( host );
159}
160
161/***
162****
163***/
164
165/**
166 * Since we can't poll a tr_torrent's fields after it's destroyed,
167 * we pre-build the "stop" announcement message when a torrent
168 * is removed from Transmission
169 */
170struct stop_message
171{
172    tr_host * host;
173    char * url;
174    int up;
175    int down;
176};
177
178static void
179stopFree( struct stop_message * stop )
180{
181    tr_free( stop->url );
182    tr_free( stop );
183}
184
185static int
186compareStops( const void * va, const void * vb )
187{
188    const struct stop_message * a = va;
189    const struct stop_message * b = vb;
190    return compareTransfer( a->up, a->down, b->up, b->down);
191}
192
193/***
194****
195***/
196
197/**
198 * "global" (per-tr_session) fields
199 */
200typedef struct tr_announcer
201{
202    tr_ptrArray hosts; /* tr_host */
203    tr_ptrArray stops; /* struct stop_message */
204    tr_session * session;
205    struct event * upkeepTimer;
206    int announceSlotsAvailable;
207    int scrapeSlotsAvailable;
208}
209tr_announcer;
210
211static tr_host *
212getHost( tr_announcer * announcer, const char * url )
213{
214    char * name = getHostName( url );
215    tr_host * host;
216
217    host = tr_ptrArrayFindSorted( &announcer->hosts, name, compareHostToName );
218    if( host == NULL )
219    {
220        host = hostNew( name );
221        fprintf( stderr, "creating new tr_host [%s] %p\n", name, host );
222        tr_ptrArrayInsertSorted( &announcer->hosts, host, compareHosts );
223    }
224    else fprintf( stderr, "reusing tr_host [%s] %p\n", name, host );
225
226    tr_free( name );
227    return host;
228}
229
230static void
231onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer );
232
233void
234tr_announcerInit( tr_session * session )
235{
236    tr_announcer * a;
237
238    assert( tr_isSession( session ) );
239
240    a = tr_new0( tr_announcer, 1 );
241    a->hosts = TR_PTR_ARRAY_INIT;
242    a->stops = TR_PTR_ARRAY_INIT;
243    a->session = session;
244    a->announceSlotsAvailable = MAX_CONCURRENT_ANNOUNCES;
245    a->scrapeSlotsAvailable = MAX_CONCURRENT_SCRAPES;
246    a->upkeepTimer = tr_new0( struct event, 1 );
247    evtimer_set( a->upkeepTimer, onUpkeepTimer, a );
248    tr_timerAdd( a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
249
250    session->announcer = a;
251}
252
253static void flushCloseMessages( tr_announcer * announcer );
254
255void
256tr_announcerClose( tr_session * session )
257{
258    tr_announcer * announcer = session->announcer;
259
260    flushCloseMessages( announcer );
261
262    evtimer_del( announcer->upkeepTimer );
263    tr_free( announcer->upkeepTimer );
264    announcer->upkeepTimer = NULL;
265
266    tr_ptrArrayDestruct( &announcer->stops, NULL );
267    tr_ptrArrayDestruct( &announcer->hosts, hostFree );
268
269    session->announcer = NULL;
270    tr_free( announcer );
271}
272
273/***
274****
275***/
276
277/* a row in tr_tier's list of trackers */
278typedef struct
279{
280    tr_host * host;
281
282    char * announce;
283    char * scrape;
284
285    char * tracker_id;
286
287    int seederCount;
288    int leecherCount;
289    int downloadCount;
290    int downloaderCount;
291
292    /* sent as the "key" argument in tracker requests
293     * to verify us if our IP address changes.
294     * This is immutable for the life of the tracker object. */
295    char key_param[KEYLEN + 1];
296}
297tr_tracker_item;
298
299static void
300generateKeyParam( char * msg, size_t msglen )
301{
302    size_t i;
303    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
304    const int poolSize = strlen( pool );
305
306    for( i=0; i<msglen; ++i )
307        *msg++ = pool[tr_cryptoRandInt( poolSize )];
308    *msg = '\0';
309}
310
311static tr_tracker_item*
312trackerNew( tr_announcer  * announcer,
313            const char    * announce,
314            const char    * scrape )
315{
316    tr_tracker_item * tracker = tr_new0( tr_tracker_item, 1  );
317    tracker->host = getHost( announcer, announce );
318    tracker->announce = tr_strdup( announce );
319    tracker->scrape = tr_strdup( scrape );
320    generateKeyParam( tracker->key_param, KEYLEN );
321    return tracker;
322}
323
324static void
325trackerFree( void * vtracker )
326{
327    tr_tracker_item * tracker = vtracker;
328
329    tr_free( tracker->tracker_id );
330    tr_free( tracker->announce );
331    tr_free( tracker->scrape );
332    tr_free( tracker );
333}
334
335/***
336****
337***/
338
339struct tr_torrent_tiers;
340
341/**
342 * A group of trackers in a single tier,
343 * as per the multitracker spec
344 */
345typedef struct
346{
347    tr_ptrArray trackers; /* tr_tracker_item */
348    tr_tracker_item * currentTracker;
349
350    tr_torrent * tor;
351
352    time_t lastScrapeTime;
353    time_t lastAnnounceTime;
354    time_t lastScrapeStartTime;
355    time_t lastAnnounceStartTime;
356    tr_bool lastScrapeSucceeded;
357    tr_bool lastAnnounceSucceeded;
358
359    time_t scrapeAt;
360    time_t manualAnnounceAllowedAt;
361
362    time_t announceAt;
363    const char * announceEvent;
364
365    /* unique lookup key */
366    int key;
367
368    int currentTrackerIndex;
369    int scrapeIntervalSec;
370    int announceIntervalSec;
371    int announceMinIntervalSec;
372    int retryAnnounceIntervalSec;
373    int retryScrapeIntervalSec;
374
375    int randOffset;
376    int lastAnnouncePeerCount;
377
378    tr_bool isRunning;
379    tr_bool isAnnouncing;
380    tr_bool isScraping;
381
382    char lastAnnounceStr[128];
383    char lastScrapeStr[128];
384}
385tr_tier;
386
387static tr_tier *
388tierNew( tr_torrent * tor )
389{
390    tr_tier * t;
391    static int nextKey = 1;
392    const time_t now = time( NULL );
393
394    t = tr_new0( tr_tier, 1 );
395    t->randOffset = tr_cryptoRandInt( 60 );
396    t->key = nextKey++;
397    t->trackers = TR_PTR_ARRAY_INIT;
398    t->currentTracker = NULL;
399    t->currentTrackerIndex = -1;
400    t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
401    t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
402    t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
403    t->scrapeAt = now + t->randOffset;
404    t->tor = tor;
405
406    return t;
407}
408
409static void
410tierFree( void * vtier )
411{
412    tr_tier * tier = vtier;
413    tr_ptrArrayDestruct( &tier->trackers, trackerFree );
414    tr_free( tier );
415}
416
417static void
418tierIncrementTracker( tr_tier * tier )
419{
420    const int i = ( tier->currentTrackerIndex + 1 )
421                        % tr_ptrArraySize( &tier->trackers );
422    tier->currentTracker = tr_ptrArrayNth( &tier->trackers, i );
423    tier->currentTrackerIndex = i;
424
425    /* reset some of the tier's fields */
426    tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
427    tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
428    tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
429    tier->retryAnnounceIntervalSec = FIRST_ANNOUNCE_RETRY_INTERVAL_SEC;
430    tier->retryScrapeIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
431    tier->isAnnouncing = FALSE;
432    tier->isScraping = FALSE;
433    tier->lastAnnounceStartTime = 0;
434    tier->lastScrapeStartTime = 0;
435}
436
437static void
438tierAddTracker( tr_announcer * announcer,
439                tr_tier      * tier,
440                const char   * announce,
441                const char   * scrape )
442{
443    tr_tracker_item * tracker = trackerNew( announcer, announce, scrape );
444
445    tr_ptrArrayAppend( &tier->trackers, tracker );
446    dbgmsg( tier, "adding tracker %s", announce );
447
448    if( !tier->currentTracker )
449        tierIncrementTracker( tier );
450}
451
452
453/***
454****
455***/
456
457/*
458 * per-torrent info.
459 * this opaque data structure can be found in tr_torrent.tiers
460 */
461typedef struct tr_torrent_tiers
462{
463    tr_ptrArray tiers;
464    tr_publisher publisher;
465}
466tr_torrent_tiers;
467
468static tr_torrent_tiers*
469tiersNew( void )
470{
471    tr_torrent_tiers * tiers = tr_new0( tr_torrent_tiers, 1 );
472    tiers->tiers = TR_PTR_ARRAY_INIT;
473    tiers->publisher = TR_PUBLISHER_INIT;
474    return tiers;
475}
476
477static void
478tiersFree( tr_torrent_tiers * tiers )
479{
480    tr_publisherDestruct( &tiers->publisher );
481    tr_ptrArrayDestruct( &tiers->tiers, tierFree );
482    tr_free( tiers );
483}
484
485static tr_tier*
486getTier( tr_announcer * announcer, int torrentId, int tierId )
487{
488    tr_tier * tier = NULL;
489
490    if( announcer )
491    {
492        tr_torrent * tor = tr_torrentFindFromId( announcer->session, torrentId );
493
494        if( tor && tor->tiers )
495        {
496            int i;
497            tr_ptrArray * tiers = &tor->tiers->tiers;
498            const int n = tr_ptrArraySize( tiers );
499            for( i=0; !tier && i<n; ++i )
500            {
501                tr_tier * tmp = tr_ptrArrayNth( tiers, i );
502                if( tmp->key == tierId )
503                    tier = tmp;
504            }
505        }
506    }
507
508    return tier;
509}
510
511/***
512****  PUBLISH
513***/
514
515static const tr_tracker_event emptyEvent = { 0, NULL, NULL, 0, 0 };
516
517static void
518publishMessage( tr_tier * tier, const char * msg, int type )
519{
520    if( tier && tier->tor && tier->tor->tiers )
521    {
522        tr_torrent_tiers * tiers = tier->tor->tiers;
523        tr_tracker_event event = emptyEvent;
524        event.messageType = type;
525        event.text = msg;
526        tr_publisherPublish( &tiers->publisher, tier, &event );
527    }
528}
529
530static void
531publishErrorClear( tr_tier * tier )
532{
533    publishMessage( tier, NULL, TR_TRACKER_ERROR_CLEAR );
534}
535
536static void
537publishErrorMessageAndStop( tr_tier * tier, const char * msg )
538{
539    tier->isRunning = FALSE;
540
541    publishMessage( tier, msg, TR_TRACKER_ERROR );
542}
543
544static void
545publishWarning( tr_tier * tier, const char * msg )
546{
547    publishMessage( tier, msg, TR_TRACKER_WARNING );
548}
549
550static int
551publishNewPeers( tr_tier * tier, tr_bool allAreSeeds,
552                 const void * compact, int compactLen )
553{
554    tr_tracker_event e = emptyEvent;
555
556    e.messageType = TR_TRACKER_PEERS;
557    e.allAreSeeds = allAreSeeds;
558    e.compact = compact;
559    e.compactLen = compactLen;
560
561    if( compactLen )
562        tr_publisherPublish( &tier->tor->tiers->publisher, tier, &e );
563
564    return compactLen / 6;
565}
566
567static int
568publishNewPeersCompact( tr_tier * tier, tr_bool allAreSeeds,
569                        const void * compact, int compactLen )
570{
571    int i;
572    const uint8_t *compactWalk;
573    uint8_t *array, *walk;
574    const int peerCount = compactLen / 6;
575    const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 );
576    tr_address addr;
577    tr_port port;
578
579    addr.type = TR_AF_INET;
580    memset( &addr.addr, 0, sizeof( addr.addr ) );
581    array = tr_new( uint8_t, arrayLen );
582    for ( i=0, walk=array, compactWalk=compact ; i<peerCount ; ++i )
583    {
584        memcpy( &addr.addr.addr4, compactWalk, 4 );
585        memcpy( &port, compactWalk + 4, 2 );
586
587        memcpy( walk, &addr, sizeof( addr ) );
588        memcpy( walk + sizeof( addr ), &port, 2 );
589
590        walk += sizeof( tr_address ) + 2;
591        compactWalk += 6;
592    }
593
594    publishNewPeers( tier, allAreSeeds, array, arrayLen );
595
596    tr_free( array );
597
598    return peerCount;
599}
600
601static int
602publishNewPeersCompact6( tr_tier * tier, tr_bool allAreSeeds,
603                         const void * compact, int compactLen )
604{
605    int i;
606    const uint8_t *compactWalk;
607    uint8_t *array, *walk;
608    const int peerCount = compactLen / 18;
609    const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 );
610    tr_address addr;
611    tr_port port;
612
613    addr.type = TR_AF_INET6;
614    memset( &addr.addr, 0, sizeof( addr.addr ) );
615    array = tr_new( uint8_t, arrayLen );
616    for ( i = 0, walk = array, compactWalk = compact ; i < peerCount ; ++i )
617    {
618        memcpy( &addr.addr.addr6, compactWalk, 16 );
619        memcpy( &port, compactWalk + 16, 2 );
620        compactWalk += 18;
621
622        memcpy( walk, &addr, sizeof( addr ) );
623        memcpy( walk + sizeof( addr ), &port, 2 );
624        walk += sizeof( tr_address ) + 2;
625    }
626    publishNewPeers( tier, allAreSeeds, array, arrayLen );
627    tr_free( array );
628
629    return peerCount;
630}
631
632static char*
633createAnnounceURL( const tr_announcer     * announcer,
634                   const tr_torrent       * torrent,
635                   const tr_tier          * tier,
636                   const char             * eventName )
637{
638    const int isStopping = !strcmp( eventName, "stopped" );
639    const int numwant = isStopping ? 0 : NUMWANT;
640    const tr_tracker_item  * tracker = tier->currentTracker;
641    const char * ann = tracker->announce;
642    struct evbuffer * buf = evbuffer_new( );
643    char * ret;
644
645    evbuffer_add_printf( buf, "%s"
646                              "%c"
647                              "info_hash=%s"
648                              "&peer_id=%s"
649                              "&port=%d"
650                              "&uploaded=%" PRIu64
651                              "&downloaded=%" PRIu64
652                              "&left=%" PRIu64
653                              "&numwant=%d"
654                              "&key=%s"
655                              "&compact=1"
656                              "&supportcrypto=1",
657                              ann,
658                              strchr( ann, '?' ) ? '&' : '?',
659                              torrent->info.hashEscaped,
660                              torrent->peer_id,
661                              tr_sessionGetPeerPort( announcer->session ),
662                              torrent->uploadedCur,
663                              torrent->downloadedCur,
664                              tr_cpLeftUntilComplete( &torrent->completion ),
665                              numwant,
666                              tracker->key_param );
667
668    if( torrent->corruptCur )
669        evbuffer_add_printf( buf, "&corrupt=%" PRIu64, torrent->corruptCur );
670
671    if( !isStopping )
672        evbuffer_add_printf( buf, "&upload_only=%d", tr_torrentIsSeed( torrent ) ? 1 : 0 );
673
674    if( announcer->session->encryptionMode == TR_ENCRYPTION_REQUIRED )
675        evbuffer_add_printf( buf, "&requirecrypto=1" );
676
677    if( eventName && *eventName )
678        evbuffer_add_printf( buf, "&event=%s", eventName );
679
680    if( tracker->tracker_id && *tracker->tracker_id )
681        evbuffer_add_printf( buf, "&trackerid=%s", tracker->tracker_id );
682
683    ret = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
684    evbuffer_free( buf );
685
686    dbgmsg( tier, "announce URL is \"%s\"", ret );
687    return ret;
688}
689
690
691/***
692****
693***/
694
695tr_torrent_tiers *
696tr_announcerAddTorrent( tr_announcer * announcer, tr_torrent * tor )
697{
698    tr_torrent_tiers * tiers;
699
700    assert( announcer != NULL );
701    assert( tr_isTorrent( tor ) );
702
703    tiers = tiersNew( );
704
705    /* build our private table of tiers... */
706    if( tor->info.trackerCount )
707    {
708        int i;
709        int tierIndex = -1;
710        tr_tier * tier = NULL;
711
712        for( i=0; i<tor->info.trackerCount; ++i )
713        {
714            if( tor->info.trackers[i].tier != tierIndex )
715                tier = NULL;
716
717            tierIndex = tor->info.trackers[i].tier;
718
719            if( tier == NULL ) {
720                tier = tierNew( tor );
721                dbgmsg( tier, "adding tier" );
722                tr_ptrArrayAppend( &tiers->tiers, tier );
723            }
724
725            tierAddTracker( announcer, tier, tor->info.trackers[i].announce,
726                                             tor->info.trackers[i].scrape );
727        }
728    }
729
730    return tiers;
731}
732
733tr_publisher_tag
734tr_announcerSubscribe( struct tr_torrent_tiers   * tiers,
735                       tr_delivery_func            func,
736                       void                      * userData )
737{
738    return tr_publisherSubscribe( &tiers->publisher, func, userData );
739}
740
741void
742tr_announcerUnsubscribe( struct tr_torrent_tiers  * tiers,
743                         tr_publisher_tag           tag )
744{
745    if( tiers )
746        tr_publisherUnsubscribe( &tiers->publisher, tag );
747}
748
749static tr_bool
750tierCanManualAnnounce( const tr_tier * tier )
751{
752    return tier->isRunning
753        && tier->manualAnnounceAllowedAt <= time( NULL );
754}
755
756tr_bool
757tr_announcerCanManualAnnounce( const tr_torrent * tor )
758{
759    int i;
760    int n;
761    const tr_tier ** tiers;
762
763    assert( tr_isTorrent( tor ) );
764    assert( tor->tiers != NULL );
765
766    n = tr_ptrArraySize( &tor->tiers->tiers );
767    tiers = (const tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
768    for( i=0; i<n; ++i )
769        if( tierCanManualAnnounce( tiers[i] ) )
770            return TRUE;
771
772    return FALSE;
773}
774
775time_t
776tr_announcerNextManualAnnounce( const tr_torrent * tor )
777{
778    int i;
779    int n;
780    const tr_torrent_tiers * tiers;
781    time_t ret = ~(time_t)0;
782
783    assert( tr_isTorrent( tor  ) );
784
785    tiers = tor->tiers;
786    n = tr_ptrArraySize( &tiers->tiers );
787    for( i=0; i<n; ++i ) {
788        tr_tier * tier = tr_ptrArrayNth( (tr_ptrArray*)&tiers->tiers, i );
789        if( tier->isRunning )
790            ret = MIN( ret, tier->manualAnnounceAllowedAt );
791    }
792
793    return ret;
794}
795
796static void
797tierClearNextAnnounce( tr_tier * tier )
798{
799    tier->announceAt = 0;
800    tier->announceEvent = NULL;
801    dbgmsg( tier, "cleared out announceEvent -- no announces pending." );
802}
803
804static void
805tierSetNextAnnounce( tr_tier * tier, const char * announceEvent, time_t announceAt )
806{
807    assert( tier != NULL );
808    assert( announceEvent != NULL );
809
810    if( !strcmp( announceEvent, "manual" ) && !tierCanManualAnnounce( tier ) )
811        return;
812
813    tier->announceAt = announceAt;
814    tier->announceEvent = announceEvent;
815    dbgmsg( tier, "next announce event is \"%s\" in %d seconds\n", announceEvent, (int)difftime(announceAt,time(NULL)) );
816}
817
818static void
819torrentSetNextAnnounce( tr_torrent * tor, const char * announceEvent, time_t announceAt )
820{
821    int i;
822    int n;
823    tr_torrent_tiers * tiers;
824
825    assert( tr_isTorrent( tor ) );
826
827    tiers = tor->tiers;
828    n = tr_ptrArraySize( &tiers->tiers );
829    for( i=0; i<n; ++i )
830        tierSetNextAnnounce( tr_ptrArrayNth( &tiers->tiers, i ), announceEvent, announceAt );
831}
832
833void
834tr_announcerManualAnnounce( tr_torrent * tor )
835{
836    torrentSetNextAnnounce( tor, "manual", time( NULL ) );
837}
838void
839tr_announcerTorrentStarted( tr_torrent * tor )
840{
841    torrentSetNextAnnounce( tor, "started", time( NULL ) );
842}
843void
844tr_announcerTorrentStopped( tr_torrent * tor )
845{
846    torrentSetNextAnnounce( tor, "stopped", time( NULL ) );
847}
848void
849tr_announcerTorrentCompleted( tr_torrent * tor )
850{
851    torrentSetNextAnnounce( tor, "completed", time( NULL ) );
852}
853void
854tr_announcerChangeMyPort( tr_torrent * tor )
855{
856    tr_announcerTorrentStarted( tor );
857}
858
859
860void
861tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor )
862{
863    assert( tr_isTorrent( tor ) );
864
865    if( tor->tiers )
866    {
867        int i;
868        const int n = tr_ptrArraySize( &tor->tiers->tiers );
869        for( i=0; i<n; ++i )
870        {
871            tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
872
873            if( tier->isRunning )
874            {
875                struct stop_message * s = tr_new0( struct stop_message, 1 );
876                s->up = tor->uploadedCur;
877                s->down = tor->downloadedCur;
878                s->url = createAnnounceURL( announcer, tor, tier, "stopped" );
879                s->host = tier->currentTracker->host;
880                tr_ptrArrayInsertSorted( &announcer->stops, s, compareStops );
881            }
882        }
883
884        tiersFree( tor->tiers );
885        tor->tiers = NULL;
886    }
887}
888
889/* return true if (1) we've tried it recently AND (2) it didn't respond... */
890static tr_bool
891hostIsNotResponding( const tr_host * host, const time_t now )
892{
893    tr_bool b = ( host->lastRequestTime )
894             && ( host->lastRequestTime >= ( now - SLOW_HOST_PENALTY_SECS ) )
895             && ( host->lastResponseInterval > MAX_TRACKER_RESPONSE_TIME_SECS );
896    return b;
897}
898
899static tr_bool
900tierIsNotResponding( const tr_tier * tier, const time_t now )
901{
902    return !tier->currentTracker
903        || hostIsNotResponding( tier->currentTracker->host, now );
904}
905
906static int
907compareTiers( const void * va, const void * vb )
908{
909    int ret = 0;
910    tr_bool af, bf;
911    const tr_tier * a = *(const tr_tier**)va;
912    const tr_tier * b = *(const tr_tier**)vb;
913
914    /* working domains come before non-working */
915    if( !ret ) {
916        const time_t now = time( NULL );
917        af = tierIsNotResponding( a, now );
918        bf = tierIsNotResponding( b, now );
919        if( af != bf )
920            ret = !af ? -1 : 1;
921    }
922
923    /* stops come before starts */
924    if( !ret ) {
925        af = a->tor->isRunning;
926        bf = b->tor->isRunning;
927        if( af != bf )
928            ret = af ? 1 : -1;
929    }
930
931    /* upload comes before download */
932    if( !ret )
933        ret = compareTransfer( a->tor->uploadedCur, a->tor->downloadedCur,
934                               b->tor->uploadedCur, b->tor->downloadedCur );
935
936    /* incomplete comes before complete */
937    if( !ret ) {
938        af = a->tor->completeness == TR_LEECH;
939        bf = b->tor->completeness == TR_LEECH;
940        if( af != bf )
941            return af ? -1 : 1;
942    }
943
944    /* private before public */
945    if( !ret ) {
946        af = tr_torrentIsPrivate( a->tor );
947        bf = tr_torrentIsPrivate( b->tor );
948        if( af != bf )
949            ret = af ? -1 : 1;
950    }
951
952    return ret;
953}
954
955static uint8_t *
956parseOldPeers( tr_benc * bePeers, size_t *  byteCount )
957{
958    int       i;
959    uint8_t * array, *walk;
960    const int peerCount = bePeers->val.l.count;
961
962    assert( tr_bencIsList( bePeers ) );
963
964    array = tr_new( uint8_t, peerCount * ( sizeof( tr_address ) + 2 ) );
965
966    for( i = 0, walk = array; i < peerCount; ++i )
967    {
968        const char * s;
969        int64_t      itmp;
970        tr_address   addr;
971        tr_port      port;
972        tr_benc    * peer = &bePeers->val.l.vals[i];
973
974        if( tr_bencDictFindStr( peer, "ip", &s ) )
975            if( tr_pton( s, &addr ) == NULL )
976                continue;
977
978        if( !tr_bencDictFindInt( peer, "port", &itmp )
979                || itmp < 0
980                || itmp > USHRT_MAX )
981            continue;
982
983        memcpy( walk, &addr, sizeof( tr_address ) );
984        port = htons( itmp );
985        memcpy( walk + sizeof( tr_address ), &port, 2 );
986        walk += sizeof( tr_address ) + 2;
987    }
988
989    *byteCount = peerCount * sizeof( tr_address ) + 2;
990    return array;
991}
992
993static tr_bool
994parseAnnounceResponse( tr_tier     * tier,
995                       const char  * response,
996                       size_t        responseLen,
997                       tr_bool     * gotScrape )
998{
999    tr_benc benc;
1000    tr_bool success = FALSE;
1001    int scrapeFields = 0;
1002    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1003
1004    dbgmsg( tier, "response len: %d, isBenc: %d", (int)responseLen, (int)bencLoaded );
1005    publishErrorClear( tier );
1006    if( bencLoaded && tr_bencIsDict( &benc ) )
1007    {
1008        int incomplete = -1;
1009        size_t rawlen;
1010        int64_t i;
1011        tr_benc * tmp;
1012        const char * str;
1013        const uint8_t * raw;
1014
1015        success = TRUE;
1016
1017        tier->retryAnnounceIntervalSec = FIRST_ANNOUNCE_RETRY_INTERVAL_SEC;
1018
1019        if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
1020        {
1021            tr_strlcpy( tier->lastAnnounceStr, str,
1022                        sizeof( tier->lastAnnounceStr ) );
1023            dbgmsg( tier, "tracker gave \"%s\"", str );
1024            publishMessage( tier, str, TR_TRACKER_ERROR );
1025            success = FALSE;
1026        }
1027
1028        if( tr_bencDictFindStr( &benc, "warning message", &str ) )
1029        {
1030            tr_strlcpy( tier->lastAnnounceStr, str,
1031                        sizeof( tier->lastAnnounceStr ) );
1032            dbgmsg( tier, "tracker gave \"%s\"", str );
1033            publishWarning( tier, str );
1034        }
1035
1036        if( tr_bencDictFindInt( &benc, "interval", &i ) )
1037        {
1038            dbgmsg( tier, "setting interval to %d", (int)i );
1039            tier->announceIntervalSec = i;
1040        }
1041
1042        if( tr_bencDictFindInt( &benc, "min interval", &i ) )
1043        {
1044            dbgmsg( tier, "setting min interval to %d", (int)i );
1045            tier->announceMinIntervalSec = i;
1046        }
1047
1048        if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
1049        {
1050            tier->currentTracker->tracker_id = tr_strdup( str );
1051        }
1052
1053        if( tr_bencDictFindInt( &benc, "complete", &i ) )
1054        {
1055            ++scrapeFields;
1056            tier->currentTracker->seederCount = i;
1057        }
1058
1059        if( tr_bencDictFindInt( &benc, "incomplete", &i ) )
1060        {
1061            ++scrapeFields;
1062            tier->currentTracker->leecherCount = incomplete = i;
1063        }
1064
1065        if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
1066        {
1067            ++scrapeFields;
1068            tier->currentTracker->downloadCount = i;
1069        }
1070
1071        if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) )
1072        {
1073            /* "compact" extension */
1074            const int allAreSeeds = incomplete == 0;
1075            tier->lastAnnouncePeerCount = publishNewPeersCompact( tier, allAreSeeds, raw, rawlen );
1076        }
1077        else if( tr_bencDictFindList( &benc, "peers", &tmp ) )
1078        {
1079            /* original version of peers */
1080            const tr_bool allAreSeeds = incomplete == 0;
1081            size_t byteCount = 0;
1082            uint8_t * array = parseOldPeers( tmp, &byteCount );
1083            tier->lastAnnouncePeerCount = publishNewPeers( tier, allAreSeeds, array, byteCount );
1084            tr_free( array );
1085        }
1086
1087        if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) )
1088        {
1089            /* "compact" extension */
1090            const tr_bool allAreSeeds = incomplete == 0;
1091            tier->lastAnnouncePeerCount += publishNewPeersCompact6( tier, allAreSeeds, raw, rawlen );
1092        }
1093
1094        if( tier->lastAnnounceStr[0] == '\0' )
1095            tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
1096                        sizeof( tier->lastAnnounceStr ) );
1097    }
1098
1099    if( bencLoaded )
1100        tr_bencFree( &benc );
1101
1102fprintf( stderr, "scrapeField count is %d\n", scrapeFields );
1103    *gotScrape = scrapeFields >= 2;
1104
1105    return success;
1106}
1107
1108struct announce_data
1109{
1110    int torrentId;
1111    int tierId;
1112    time_t timeSent;
1113
1114    /** If the request succeeds, the value for tier's "isRunning" flag */
1115    tr_bool isRunningOnSuccess;
1116};
1117
1118static void
1119onAnnounceDone( tr_session   * session,
1120                long           responseCode,
1121                const void   * response,
1122                size_t         responseLen,
1123                void         * vdata )
1124{
1125    tr_announcer * announcer = session->announcer;
1126    struct announce_data * data = vdata;
1127    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1128    tr_bool gotScrape = FALSE;
1129    tr_bool success = FALSE;
1130    const time_t now = time ( NULL );
1131
1132    if( announcer && tier )
1133    {
1134        tier->lastAnnouncePeerCount = 0;
1135
1136        if( tier->currentTracker->host )
1137        {
1138            tr_host * host = tier->currentTracker->host;
1139            host->lastRequestTime = data->timeSent;
1140            host->lastResponseInterval = now - data->timeSent;
1141        }
1142
1143        if( responseCode == HTTP_OK )
1144        {
1145            tier->lastAnnounceTime = now;
1146            success = parseAnnounceResponse( tier, response, responseLen, &gotScrape );
1147            dbgmsg( tier, "success is %d", success );
1148        }
1149        else if( responseCode )
1150        {
1151            /* %1$ld - http status code, such as 404
1152             * %2$s - human-readable explanation of the http status code */
1153            char * buf = tr_strdup_printf(
1154                _( "Announce failed: tracker gave HTTP Response Code %1$ld (%2$s)" ),
1155                responseCode,
1156                tr_webGetResponseStr( responseCode ) );
1157
1158            tr_strlcpy( tier->lastAnnounceStr, buf,
1159                        sizeof( tier->lastAnnounceStr ) );
1160
1161            /* if the repsonse may require intervention, notify the user;
1162             * otherwise, just log it */
1163            if( responseCode >= 400 )
1164                publishWarning( tier, buf );
1165            tr_torinf( tier->tor, "%s", buf );
1166            dbgmsg( tier, "%s", buf );
1167
1168            tr_free( buf );
1169        }
1170        else
1171        {
1172            tr_strlcpy( tier->lastAnnounceStr,
1173                        _( "tracker did not respond" ),
1174                        sizeof( tier->lastAnnounceStr ) );
1175            dbgmsg( tier, "%s", tier->lastAnnounceStr );
1176        }
1177    }
1178
1179    if( tier )
1180    {
1181        tier->isAnnouncing = FALSE;
1182
1183        if( responseCode == 0 )
1184        {
1185            dbgmsg( tier, "No response from tracker... retrying in two minutes." );
1186            tier->manualAnnounceAllowedAt = ~(time_t)0;
1187            tierSetNextAnnounce( tier, tier->announceEvent, now + tier->randOffset + 120 );
1188        }
1189        else if( 200 <= responseCode && responseCode <= 299 )
1190        {
1191            const int interval = tier->announceIntervalSec + tier->randOffset;
1192            dbgmsg( tier, "request succeeded. reannouncing in %d seconds", interval );
1193
1194            if( gotScrape )
1195            {
1196                tier->lastScrapeTime = now;
1197                tier->scrapeAt = now + tier->scrapeIntervalSec + tier->randOffset;
1198            }
1199
1200            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1201
1202            if( strcmp( tier->announceEvent, "stopped" ) == 0 )
1203                tierClearNextAnnounce( tier );
1204            else
1205                tierSetNextAnnounce( tier, "", now + interval );
1206        }
1207        else if( 300 <= responseCode && responseCode <= 399 )
1208        {
1209            /* how did this get here?  libcurl handles this */
1210            const int interval = 5;
1211            dbgmsg( tier, "got a redirect. retrying in %d seconds", interval );
1212            tierSetNextAnnounce( tier, tier->announceEvent, now + interval );
1213            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1214        }
1215        else if( 400 <= responseCode && responseCode <= 499 )
1216        {
1217            /* The request could not be understood by the server due to
1218             * malformed syntax. The client SHOULD NOT repeat the
1219             * request without modifications. */
1220            publishErrorMessageAndStop( tier, _( "Tracker returned a 4xx message" ) );
1221            tier->manualAnnounceAllowedAt = ~(time_t)0;
1222        }
1223        else if( 500 <= responseCode && responseCode <= 599 )
1224        {
1225            /* Response status codes beginning with the digit "5" indicate
1226             * cases in which the server is aware that it has erred or is
1227             * incapable of performing the request.  So we pause a bit and
1228             * try again. */
1229            tier->manualAnnounceAllowedAt = ~(time_t)0;
1230            tierSetNextAnnounce( tier, tier->announceEvent, now + tier->retryAnnounceIntervalSec );
1231            tier->retryAnnounceIntervalSec *= 2;
1232        }
1233        else
1234        {
1235            /* WTF did we get?? */
1236            const int interval = tier->randOffset + 120;
1237            dbgmsg( tier, "Invalid response from tracker... retrying in two minutes." );
1238            tier->manualAnnounceAllowedAt = ~(time_t)0;
1239            tierSetNextAnnounce( tier, tier->announceEvent, now + interval );
1240        }
1241
1242        tier->lastAnnounceSucceeded = success;
1243
1244        if( success )
1245            tier->isRunning = data->isRunningOnSuccess;
1246
1247        if( !success )
1248            tierIncrementTracker( tier );
1249    }
1250
1251    ++announcer->announceSlotsAvailable;
1252
1253    tr_free( data );
1254}
1255
1256static const char *
1257getAnnounceEvent( const tr_tier * tier )
1258{
1259    const char * event;
1260
1261    assert( tier != NULL );
1262    assert( tier->announceEvent != NULL );
1263    assert( tr_isTorrent( tier->tor ) );
1264
1265    /* BEP 21: "In order to tell the tracker that a peer is a partial seed,
1266     * it MUST send an event=paused parameter in every announce while
1267     * it is a partial seed." */
1268    if( tr_cpGetStatus( &tier->tor->completion ) == TR_PARTIAL_SEED )
1269        return "paused";
1270
1271    event = tier->announceEvent;
1272
1273    if( !strcmp( event, "manual" ) )
1274        return "started";
1275
1276    return event;
1277}
1278
1279static void
1280tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1281{
1282    char * url;
1283    struct announce_data * data;
1284    const tr_torrent * tor = tier->tor;
1285    const time_t now = time( NULL );
1286
1287    assert( !tier->isAnnouncing );
1288
1289    data = tr_new0( struct announce_data, 1 );
1290    data->torrentId = tr_torrentId( tor );
1291    data->tierId = tier->key;
1292    data->isRunningOnSuccess = tor->isRunning;
1293    data->timeSent = now;
1294
1295    url = createAnnounceURL( announcer, tor, tier, getAnnounceEvent( tier ) );
1296
1297    tier->isAnnouncing = TRUE;
1298    tier->lastAnnounceStartTime = now;
1299    --announcer->announceSlotsAvailable;
1300    tr_webRun( announcer->session, url, NULL, onAnnounceDone, data );
1301
1302    tr_free( url );
1303}
1304
1305static tr_bool
1306parseScrapeResponse( tr_tier     * tier,
1307                     const char  * response,
1308                     size_t        responseLen )
1309{
1310    tr_bool success = FALSE;
1311    tr_benc benc, *files;
1312    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1313    if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) )
1314    {
1315        const char * key;
1316        tr_benc * val;
1317        int i = 0;
1318        while( tr_bencDictChild( files, i++, &key, &val ))
1319        {
1320            int64_t intVal;
1321            tr_benc * flags;
1322
1323            if( memcmp( tier->tor->info.hash, key, SHA_DIGEST_LENGTH ) )
1324                continue;
1325
1326            success = TRUE;
1327            publishErrorClear( tier );
1328
1329            if( ( tr_bencDictFindInt( val, "complete", &intVal ) ) )
1330                tier->currentTracker->seederCount = intVal;
1331
1332            if( ( tr_bencDictFindInt( val, "incomplete", &intVal ) ) )
1333                tier->currentTracker->leecherCount = intVal;
1334
1335            if( ( tr_bencDictFindInt( val, "downloaded", &intVal ) ) )
1336                tier->currentTracker->downloadCount = intVal;
1337
1338            if( ( tr_bencDictFindInt( val, "downloaders", &intVal ) ) )
1339                tier->currentTracker->downloaderCount = intVal;
1340
1341            if( tr_bencDictFindDict( val, "flags", &flags ) )
1342                if( ( tr_bencDictFindInt( flags, "min_request_interval", &intVal ) ) )
1343                    tier->scrapeIntervalSec = intVal;
1344
1345            /* as per ticket #1045, safeguard against trackers returning
1346             * a very low min_request_interval... */
1347            if( tier->scrapeIntervalSec < DEFAULT_SCRAPE_INTERVAL_SEC )
1348                tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
1349
1350            tr_tordbg( tier->tor,
1351                       "Scrape successful. Rescraping in %d seconds.",
1352                       tier->scrapeIntervalSec );
1353
1354            tier->retryScrapeIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
1355        }
1356    }
1357
1358    if( bencLoaded )
1359        tr_bencFree( &benc );
1360
1361    return success;
1362}
1363
1364static void
1365onScrapeDone( tr_session   * session,
1366              long           responseCode,
1367              const void   * response,
1368              size_t         responseLen,
1369              void         * vdata )
1370{
1371    tr_announcer * announcer = session->announcer;
1372    struct announce_data * data = vdata;
1373    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1374    const time_t now = time( NULL );
1375    tr_bool success = FALSE;
1376
1377    if( announcer )
1378        ++announcer->scrapeSlotsAvailable;
1379
1380    if( announcer && tier )
1381    {
1382        tier->isScraping = FALSE;
1383        tier->lastScrapeTime = now;
1384   
1385        if( tier->currentTracker->host )
1386        {
1387            tr_host * host = tier->currentTracker->host;
1388            host->lastRequestTime = data->timeSent;
1389            host->lastResponseInterval = now - data->timeSent;
1390        }
1391
1392        if( responseCode == HTTP_OK )
1393        {
1394            success = parseScrapeResponse( tier, response, responseLen );
1395        }
1396
1397        if( 200 <= responseCode && responseCode <= 299 )
1398        {
1399            const int interval = tier->scrapeIntervalSec + tier->randOffset;
1400            tier->scrapeAt = now + interval;
1401
1402            tr_strlcpy( tier->lastScrapeStr, _( "Success" ), sizeof( tier->lastScrapeStr ) );
1403            tr_tordbg( tier->tor, "Request succeeded. Rescraping in %d seconds", interval );
1404        }
1405        else if( 300 <= responseCode && responseCode <= 399 )
1406        {
1407            /* this shouldn't happen; libcurl should handle this */
1408            const int interval = 5;
1409            tier->scrapeAt = now + interval;
1410            tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1411                         "Got a redirect. Retrying in %d seconds", interval );
1412            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1413        }
1414        else
1415        {
1416            const int interval = tier->retryScrapeIntervalSec + tier->randOffset;
1417            tier->retryScrapeIntervalSec *= 2;
1418            tier->scrapeAt = now + interval;
1419
1420            /* %1$ld - http status code, such as 404
1421             * %2$s - human-readable explanation of the http status code */
1422            if( !responseCode )
1423                tr_strlcpy( tier->lastScrapeStr, _( "tracker did not respond" ),
1424                            sizeof( tier->lastScrapeStr ) );
1425            else
1426                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1427                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1428                             responseCode, tr_webGetResponseStr( responseCode ) );
1429        }
1430
1431        tier->lastScrapeSucceeded = success;
1432    }
1433
1434    tr_free( data );
1435}
1436
1437static void
1438tierScrape( tr_announcer * announcer, tr_tier * tier )
1439{
1440    const char * scrape;
1441    struct evbuffer * buf;
1442    struct announce_data * data;
1443    const time_t now = time( NULL );
1444
1445    assert( tier );
1446    assert( !tier->isScraping );
1447    assert( tier->currentTracker != NULL );
1448    assert( tr_isTorrent( tier->tor ) );
1449
1450    data = tr_new0( struct announce_data, 1 );
1451    data->torrentId = tr_torrentId( tier->tor );
1452    data->tierId = tier->key;
1453
1454    scrape = tier->currentTracker->scrape;
1455
1456    buf = evbuffer_new( );
1457    evbuffer_add_printf( buf, "%s%cinfo_hash=%s",
1458                         scrape,
1459                         strchr( scrape, '?' ) ? '&' : '?',
1460                         tier->tor->info.hashEscaped );
1461
1462    tier->isScraping = TRUE;
1463    tier->lastScrapeStartTime = now;
1464    --announcer->scrapeSlotsAvailable;
1465    dbgmsg( tier, "scraping \"%s\"", (const char*)EVBUFFER_DATA(buf) );
1466    tr_webRun( announcer->session, (const char*)EVBUFFER_DATA(buf), NULL, onScrapeDone, data );
1467
1468    evbuffer_free( buf );
1469}
1470
1471static void
1472flushCloseMessages( tr_announcer * announcer )
1473{
1474    int i;
1475    const int n = tr_ptrArraySize( &announcer->stops );
1476
1477    for( i=0; i<n; ++i )
1478    {
1479        struct stop_message * stop = tr_ptrArrayNth( &announcer->stops, i );
1480        fprintf( stderr, "sending close message: [%s]\n", stop->url );
1481        tr_webRun( announcer->session, stop->url, NULL, NULL, NULL );
1482        stopFree( stop );
1483    }
1484
1485    tr_ptrArrayClear( &announcer->stops );
1486}
1487
1488static tr_bool
1489tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1490{
1491    return !tier->isAnnouncing
1492        && !tier->isScraping
1493        && ( tier->announceEvent != NULL )
1494        && ( tier->announceAt <= now );
1495}
1496
1497static tr_bool
1498tierNeedsToScrape( const tr_tier * tier, const time_t now )
1499{
1500    return !tier->isScraping && tier->scrapeAt <= now;
1501}
1502
1503static void
1504announceMore( tr_announcer * announcer )
1505{
1506    const tr_bool canAnnounce = announcer->announceSlotsAvailable > 0;
1507    const tr_bool canScrape = announcer->scrapeSlotsAvailable > 0;
1508
1509    if( announcer->announceSlotsAvailable > 0 )
1510    {
1511        int i;
1512        int n;
1513        const time_t now = time( NULL );
1514        tr_torrent * tor = NULL;
1515        tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1516        tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1517
1518        /* build a list of tiers that need to be announced */
1519        while(( tor = tr_torrentNext( announcer->session, tor ))) {
1520            if( tor->tiers ) {
1521                n = tr_ptrArraySize( &tor->tiers->tiers );
1522                for( i=0; i<n; ++i ) {
1523                    tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1524                    if( canAnnounce && tierNeedsToAnnounce( tier, now ) )
1525                        tr_ptrArrayAppend( &announceMe, tier );
1526                    else if( canScrape && tierNeedsToScrape( tier, now ) )
1527                        tr_ptrArrayAppend( &scrapeMe, tier );
1528                }
1529            }
1530        }
1531
1532        /* if there are more tiers than slots available, prioritize */
1533        n = tr_ptrArraySize( &announceMe );
1534        if( n > announcer->announceSlotsAvailable )
1535            qsort( tr_ptrArrayBase( &announceMe ), n, sizeof( tr_tier * ), compareTiers );
1536
1537        /* announce some */
1538        n = MIN( tr_ptrArraySize( &announceMe ), announcer->announceSlotsAvailable );
1539        for( i=0; i<n; ++i ) {
1540            tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1541            dbgmsg( tier, "announcing tier %d of %d", i, n );
1542            tierAnnounce( announcer, tier );
1543        }
1544
1545
1546        /* scrape some */
1547        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->scrapeSlotsAvailable );
1548        for( i=0; i<n; ++i ) {
1549            tr_tier * tier = tr_ptrArrayNth( &scrapeMe, i );
1550            dbgmsg( tier, "scraping tier %d of %d", (i+1), n );
1551            tierScrape( announcer, tier );
1552        }
1553
1554        /* cleanup */
1555        tr_ptrArrayDestruct( &scrapeMe, NULL );
1556        tr_ptrArrayDestruct( &announceMe, NULL );
1557    }
1558}
1559
1560static void
1561onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1562{
1563    tr_announcer * announcer = vannouncer;
1564
1565    /* maybe send out some "stopped" messages for closed torrents */
1566    flushCloseMessages( announcer );
1567
1568    /* maybe send out some announcements to trackers */
1569    announceMore( announcer );
1570
1571    /* set up the next timer */
1572    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1573}
1574
1575/***
1576****
1577***/
1578
1579void
1580tr_announcerGetCounts( tr_torrent * torrent,
1581                       int * setme_completedCount,
1582                       int * setme_leecherCount,
1583                       int * setme_seederCount,
1584                       int * setme_downloaderCount )
1585{
1586    int i;
1587    int n;
1588    int completed = 0;
1589    int leechers = 0;
1590    int seeders = 0;
1591    int downloaders = 0;
1592
1593    /* count the trackers... */
1594    for( i=0, n=tr_ptrArraySize( &torrent->tiers->tiers ); i<n; ++i )
1595    {
1596        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1597
1598        if( tier->currentTracker )
1599        {
1600            seeders += tier->currentTracker->seederCount;
1601            leechers += tier->currentTracker->leecherCount;
1602            completed += tier->currentTracker->downloadCount;
1603            downloaders += tier->currentTracker->downloaderCount;
1604        }
1605    }
1606
1607    if( setme_completedCount )  *setme_completedCount = completed;
1608    if( setme_leecherCount )    *setme_leecherCount = leechers;
1609    if( setme_seederCount )     *setme_seederCount = seeders;
1610    if( setme_downloaderCount ) *setme_downloaderCount = downloaders;
1611}
1612
1613tr_tracker_stat *
1614tr_announcerStats( const tr_torrent * torrent,
1615                   int              * setmeTrackerCount )
1616{
1617    int i;
1618    int n;
1619    int out = 0;
1620    int tierCount;
1621    tr_tracker_stat * ret;
1622
1623    assert( tr_isTorrent( torrent ) );
1624
1625    /* count the trackers... */
1626    for( i=n=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i ) {
1627        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1628        n += tr_ptrArraySize( &tier->trackers );
1629    }
1630
1631    /* alloc the stats */
1632    *setmeTrackerCount = n;
1633    ret = tr_new0( tr_tracker_stat, n );
1634
1635    /* populate the stats */
1636    for( i=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i )
1637    {
1638        int j;
1639        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1640        n = tr_ptrArraySize( &tier->trackers );
1641        for( j=0; j<n; ++j )
1642        {
1643            const tr_tracker_item * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
1644            tr_tracker_stat * st = ret + out++;
1645
1646            tr_strlcpy( st->host, tracker->host->name, sizeof( st->host ) );
1647            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1648            st->tier = i + 1;
1649            st->isActive = tracker == tier->currentTracker;
1650            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1651            if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
1652                st->lastScrapeTime = tier->lastScrapeTime;
1653                st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1654                tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, sizeof( st->lastScrapeResult ) );
1655            }
1656            st->isScraping = tier->isScraping;
1657            if(( st->willScrape = st->isActive && !tier->isScraping )) {
1658                st->nextScrapeTime = tier->scrapeAt;
1659            }
1660            st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1661            if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
1662                st->lastAnnounceTime = tier->lastAnnounceTime;
1663                tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, sizeof( st->lastAnnounceResult ) );
1664                if(( st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded )) {
1665                    st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1666                }
1667            }
1668            st->isAnnouncing = tier->isAnnouncing;
1669            if(( st->willAnnounce = torrent->isRunning && !tier->isAnnouncing )) {
1670                st->nextAnnounceTime = tier->announceAt;
1671            }
1672            st->seederCount = tracker->seederCount;
1673            st->leecherCount = tracker->leecherCount;
1674            st->downloadCount = tracker->downloadCount;
1675        }
1676    }
1677
1678    return ret;
1679}
1680
1681void
1682tr_announcerStatsFree( tr_tracker_stat * trackers,
1683                       int trackerCount UNUSED )
1684{
1685    tr_free( trackers );
1686}
Note: See TracBrowser for help on using the repository browser.